diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 000000000..cb4380ba8 Binary files /dev/null and b/.DS_Store differ diff --git a/.gitignore b/.gitignore index f0698f3e4..163b751c2 100644 --- a/.gitignore +++ b/.gitignore @@ -30,3 +30,8 @@ /epub/toc.xhtml /book.epub /book.mobi + +# Archivos temporales creados por emacs +*~ +\#* +*#* diff --git a/.nojekyll b/.nojekyll new file mode 100644 index 000000000..e69de29bb diff --git a/00_intro.md b/00_intro.md index 380511d79..f129bc7e3 100644 --- a/00_intro.md +++ b/00_intro.md @@ -1,154 +1,163 @@ {{meta {load_files: ["code/intro.js"]}}} -# Introduction +# Introducción {{quote {author: "Ellen Ullman", title: "Close to the Machine: Technophilia and its Discontents", chapter: true} -We think we are creating the system for our own purposes. We believe -we are making it in our own image... But the computer is not really -like us. It is a projection of a very slim part of ourselves: that -portion devoted to logic, order, rule, and clarity. +Nosotros creemos que estamos creando el sistema para nuestros propios +propósitos. Creemos que lo estamos haciendo a nuestra propia imagen... +Pero la computadora no es realmente como nosotros. Es una proyección de una +parte muy delgada de nosotros mismos: esa porción dedicada a la lógica, +el orden, la reglas y la claridad. quote}} -{{figure {url: "img/chapter_picture_00.jpg", alt: "Picture of a screwdriver and a circuit board", chapter: "framed"}}} +Este es un libro acerca de instruir ((computadora))s. Hoy en dia las +computadoras son tan comunes como los destornilladores (aunque bastante más +complejas que estos), y hacer que hagan exactamente lo que quieres que +hagan no siempre es fácil. -This is a book about instructing ((computer))s. Computers are about as -common as screwdrivers today, but they are quite a bit more complex, -and making them do what you want them to do isn't always easy. +Si la tarea que tienes para tu computadora es común, y bien entendida, +tal y como mostrarte tu correo electrónico o funcionar como una calculadora, +puedes abrir la ((aplicación)) apropiada y ponerte a trabajar en ella. +Pero para realizar tareas únicas o abiertas, es posible que no haya +una aplicación disponible. -If the task you have for your computer is a common, well-understood -one, such as showing you your email or acting like a calculator, you -can open the appropriate ((application)) and get to work. But for -unique or open-ended tasks, there probably is no application. - -That is where ((programming)) may come in. _Programming_ is the act of -constructing a _program_—a set of precise instructions telling a -computer what to do. Because computers are dumb, pedantic beasts, -programming is fundamentally tedious and frustrating. +Ahí es donde la ((programación)) podría entrar en juego. La _programación_ es +el acto de construir un _programa_—un conjunto de instrucciones precisas que +le dicen a una computadora qué hacer. Porque las computadoras son bestias +tontas y pedantes, la programación es fundamentalmente tediosa y frustrante. {{index [programming, "joy of"], speed}} -Fortunately, if you can get over that fact, and maybe even enjoy the rigor -of thinking in terms that dumb machines can deal with, programming can -be rewarding. It allows you to do things in seconds that would take -_forever_ by hand. It is a way to make your computer tool -do things that it couldn't do before. And it provides a wonderful -exercise in abstract thinking. - -Most programming is done with ((programming language))s. A _programming -language_ is an artificially constructed language used to instruct -computers. It is interesting that the most effective way we've found -to communicate with a computer borrows so heavily from the way we -communicate with each other. Like human languages, computer languages -allow words and phrases to be combined in new ways, making it possible to -express ever new concepts. +Afortunadamente, si puedes superar eso, y tal vez incluso disfrutar el rigor +de pensar en términos que las máquinas tontas puedan manejar, la programación +puede ser muy gratificante. Te permite hacer en segundos cosas que +tardarían _siglos_ a mano. Es una forma de hacer que tu herramienta +computadora haga cosas que antes no podía. Ademas proporciona de un maravilloso +ejercicio en pensamiento abstracto. + +La mayoría de la programación se realiza con ((lenguajes de programación)). +Un _lenguaje de programación_ es un lenguaje artificialmente construido que +se utiliza para instruir ordenadores. Es interesante que la forma más efectiva +que hemos encontrado para comunicarnos con una computadora es bastante +parecida a la forma que usamos para comunicarnos entre nosotros. +Al igual que los lenguajes humanos, los lenguajes de computación permiten +que las palabras y frases sean combinadas de nuevas maneras, +lo que nos permite expresar siempre nuevos conceptos. {{index [JavaScript, "availability of"], "casual computing"}} -At one point language-based interfaces, such as the BASIC and DOS -prompts of the 1980s and 1990s, were the main method of interacting with -computers. They have largely been replaced with visual interfaces, -which are easier to learn but offer less freedom. Computer languages -are still there, if you know where to look. One such language, -JavaScript, is built into every modern web ((browser)) and is thus -available on almost every device. +Las interfaces basadas en lenguajes, que en un momento fueron la principal +forma de interactuar con las computadoras para la mayoría de las personas, +han sido en gran parte reemplazadas con interfaces más simples y limitadas. +Pero todavía están allí, si sabes dónde mirar. + +En un punto, las interfaces basadas en lenguajes, como las terminales +BASIC y DOS de los 80 y 90, eran la principal forma de interactuar con las +computadoras. Estas han sido reemplazados en gran medida por interfaces +visuales, las cuales son más fáciles de aprender pero ofrecen menos libertad. +Los lenguajes de computadora todavía están allí, si sabes dónde mirar. +Uno de esos lenguajes, JavaScript, está integrado en cada ((navegador)) web +moderno y, por lo tanto, está disponible en casi todos los dispositivos. {{indexsee "web browser", browser}} -This book will try to make you familiar enough with this language to -do useful and amusing things with it. +Este libro intentará familiarizarte lo suficiente con este lenguaje para +poder hacer cosas útiles y divertidas con él. -## On programming +## Acerca de la programación {{index [programming, "difficulty of"]}} -Besides explaining JavaScript, I will introduce the basic -principles of programming. Programming, it turns out, is hard. The -fundamental rules are simple and clear, but programs built on top of -these rules tend to become complex enough to introduce their own rules -and complexity. You're building your own maze, in a way, and you might -just get lost in it. +Además de explicar JavaScript, también introduciré los principios básicos +de la programación. La programación, en realidad, es difícil. Las +reglas fundamentales son típicamente simples y claras, pero los programas +construidos en base a estas reglas tienden a ser lo suficientemente +complejas como para introducir sus propias reglas y complejidad. +De alguna manera, estás construyendo tu propio laberinto, y +es posible que te pierdas en él. {{index learning}} -There will be times when reading this book feels terribly frustrating. -If you are new to programming, there will be a lot of new material to -digest. Much of this material will then be _combined_ in ways that -require you to make additional connections. +Habrá momentos en los que leer este libro se sentirá terriblemente frustrante. +Si eres nuevo en la programación, habrá mucho material nuevo para +digerir. Gran parte de este material sera entonces _combinado_ en formas que +requerirán que hagas conexiones adicionales. -It is up to you to make the necessary effort. When you are struggling -to follow the book, do not jump to any conclusions about your own -capabilities. You are fine—you just need to keep at it. Take a break, -reread some material, and make sure you read and understand the -example programs and ((exercises)). Learning is hard work, but -everything you learn is yours and will make subsequent learning -easier. +Depende de ti hacer el esfuerzo necesario. Cuando estés luchando +para seguir el libro, no saltes a ninguna conclusión acerca de tus propias +capacidades. Estás bien, sólo tienes que seguir intentando. +Tomate un descanso, vuelve a leer algún material, y asegúrate de leer +y comprender los programas de ejemplo y ((ejercicios)). Aprender es un +trabajo duro, pero todo lo que aprendes se convertirá en tuyo, y +hará que el aprendizaje subsiguiente sea más fácil. -{{quote {author: "Ursula K. Le Guin", title: "The Left Hand of Darkness"} +{{quote {author: "Ursula K. Le Guin", title: "La Mano Izquierda De La Oscuridad"} {{index "Le Guin, Ursula K."}} -When action grows unprofitable, gather information; when information -grows unprofitable, sleep. +Cuando la acción deja de servirte, reúne información; cuando la información +deja de servirte, duerme.. quote}} {{index [program, "nature of"], data}} -A program is many things. It is a piece of text typed by a programmer, -it is the directing force that makes the computer do what it does, it -is data in the computer's memory, yet it controls the actions -performed on this same memory. Analogies that try to compare programs -to objects we are familiar with tend to fall short. A superficially -fitting one is that of a machine—lots of separate parts tend to be -involved, and to make the whole thing tick, we have to consider the -ways in which these parts interconnect and contribute to the operation -of the whole. - -A ((computer)) is a physical machine that acts as a host for these immaterial -machines. Computers themselves can do only stupidly straightforward -things. The reason they are so useful is that they do these things at -an incredibly high ((speed)). A program can ingeniously combine an -enormous number of these simple actions to do very -complicated things. +Un programa son muchas cosas. Es una pieza de texto escrita por un programador, +es la fuerza directriz que hace que la computadora haga lo que hace, +son datos en la memoria de la computadora, y sin embargo controla las acciones +realizadas en esta misma memoria. Las analogías que intentan comparar programas +a objetos con los que estamos familiarizados tienden a fallar. Una analogía +que es superficialmente adecuada es el de una máquina —muchas partes +separadas tienden a estar involucradas—, y para hacer que todo funcione, +tenemos que considerar la formas en las que estas partes se interconectan y +contribuyen a la operación de un todo. + +Una ((computadora)) es una máquina física que actúa como un anfitrión para +estas máquinas inmateriales. Las computadoras en si mismas solo pueden hacer +cosas estúpidamente sencillas. La razón por la que son tan útiles es que hacen +estas cosas a una ((velocidad)) increíblemente alta. Un programa puede +ingeniosamente combinar una cantidad enorme de estas acciones simples +para realizar cosas bastante complicadas. {{index [programming, "joy of"]}} -A program is a building of thought. It is costless to build, it is -weightless, and it grows easily under our typing hands. +Un programa es un edificio de pensamiento. No cuesta nada construirlo, no pesa +nada, y crece fácilmente bajo el teclear de nuestras manos. -But without care, a program's size and ((complexity)) will grow out of -control, confusing even the person who created it. Keeping programs -under control is the main problem of programming. When a program -works, it is beautiful. The art of programming is the skill of -controlling complexity. The great program is subdued—made simple in -its complexity. +Pero sin ningún cuidado, el tamaño de un programa y su ((complejidad)) +crecerán sin control, confundiendo incluso a la persona que lo creó. +Mantener programas bajo control es el problema principal de la programación. +Cuando un programa funciona, es hermoso. El arte de la programación es la +habilidad de controlar la complejidad. Un gran programa es moderado, hecho +simple en su complejidad. {{index "programming style", "best practices"}} -Some programmers believe that this complexity is best managed by using -only a small set of well-understood techniques in their programs. They -have composed strict rules ("best practices") prescribing the form -programs should have and carefully stay within their safe little -zone. +Algunos programadores creen que esta complejidad se maneja mejor mediante +el uso de solo un pequeño conjunto de técnicas bien entendidas en sus +programas. Ellos han compuesto reglas estrictas ("mejores prácticas") que +prescriben la forma que los programas deberían tener, y se mantienen +cuidadosamente dentro de su pequeña y segura zona. {{index experiment}} -This is not only boring, it is ineffective. New problems often -require new solutions. The field of programming is young and still -developing rapidly, and it is varied enough to have room for wildly -different approaches. There are many terrible mistakes to make in -program design, and you should go ahead and make them so that you -understand them. A sense of what a good program looks like is -developed in practice, not learned from a list of rules. +Esto no solamente es aburrido, sino que también es ineficaz. Problemas nuevos +a menudo requieren soluciones nuevas. El campo de la programación es joven y +todavía se esta desarrollando rápidamente, y es lo suficientemente variado +como para tener espacio para aproximaciones salvajemente diferentes. +Hay muchos errores terribles que hacer en el diseño de programas, así que +ve adelante y comételos para que los entiendas mejor. La idea de cómo se ve +un buen programa se desarrolla con la práctica, no se aprende de una lista +de reglas. -## Why language matters +## Por qué el lenguaje importa {{index "programming language", "machine code", "binary data"}} -In the beginning, at the birth of computing, there were no programming -languages. Programs looked something like this: +Al principio, en el nacimiento de la informática, no habían lenguajes +de programación. Los programas se veían mas o menos así: ```{lang: null} 00110001 00000000 00000000 @@ -164,246 +173,246 @@ languages. Programs looked something like this: {{index [programming, "history of"], "punch card", complexity}} -That is a program to add the numbers from 1 to 10 together and print -out the result: `1 + 2 + ... + 10 = 55`. It could run on a simple, -hypothetical machine. To program early computers, it was necessary to -set large arrays of switches in the right position or punch holes in -strips of cardboard and feed them to the computer. You can probably -imagine how tedious and error-prone this procedure was. Even writing -simple programs required much cleverness and discipline. Complex ones -were nearly inconceivable. +Ese es un programa que suma los números del 1 al 10 entre ellos e imprime +el resultado: `1 + 2 + ... + 10 = 55`. Podría ser ejecutado en una simple +máquina hipotética. Para programar las primeras computadoras, era necesario +colocar grandes arreglos de interruptores en la posición correcta o +perforar agujeros en tarjetas de cartón y dárselos a la computadora. +Probablemente puedas imaginarte lo tedioso y propenso a errores que era este +procedimiento. Incluso escribir programas simples requería de mucha inteligencia +y disciplina. Los complejos eran casi inconcebibles. {{index bit, "wizard (mighty)"}} -Of course, manually entering these arcane patterns of bits (the ones -and zeros) did give the programmer a profound sense of being a mighty -wizard. And that has to be worth something in terms of job -satisfaction. +Por supuesto, ingresar manualmente estos patrones arcanos de bits (los unos +y ceros) le dieron al programador un profundo sentido de ser un poderoso +mago. Y eso tiene que valer algo en términos de satisfacción laboral. + +{{index memoria, instrucción}} -{{index memory, instruction}} +Cada línea del programa anterior contiene una sola instrucción. Podría +ser escrito en español así: -Each line of the previous program contains a single instruction. It -could be written in English like this: - 1. Store the number 0 in memory location 0. - 2. Store the number 1 in memory location 1. - 3. Store the value of memory location 1 in memory location 2. - 4. Subtract the number 11 from the value in memory location 2. - 5. If the value in memory location 2 is the number 0, - continue with instruction 9. - 6. Add the value of memory location 1 to memory location 0. - 7. Add the number 1 to the value of memory location 1. - 8. Continue with instruction 3. - 9. Output the value of memory location 0. + 1. Almacenar el número 0 en la ubicación de memoria 0. + 2. Almacenar el número 1 en la ubicación de memoria 1. + 3. Almacenar el valor de la ubicación de memoria 1 en la ubicación de memoria 2. + 4. Restar el número 11 del valor en la ubicación de memoria 2. + 5. Si el valor en la ubicación de memoria 2 es el número 0, + continuar con la instrucción 9. + 6. Sumar el valor de la ubicación de memoria 1 a la ubicación de memoria 0. + 7. Sumar el número 1 al valor de la ubicación de memoria 1. + 8. Continuar con la instrucción 3. + 9. Imprimir el valor de la ubicación de memoria 0. -{{index readability, naming, binding}} +{{index legibilidad, nombrado, variable}} -Although that is already more readable than the soup of bits, it is -still rather obscure. Using names instead of numbers for the -instructions and memory locations helps. +Aunque eso ya es más legible que la sopa de bits, es aún difícil de entender. +Usar nombres en lugar de números para las +instrucciones y ubicaciones de memoria ayuda: ```{lang: "text/plain"} - Set “total” to 0. - Set “count” to 1. + Establecer "total" como 0. + Establecer "cuenta" como 1. [loop] - Set “compare” to “count”. - Subtract 11 from “compare”. - If “compare” is zero, continue at [end]. - Add “count” to “total”. - Add 1 to “count”. - Continue at [loop]. -[end] - Output “total”. + Establecer "comparar" como "cuenta". + Restar 11 de "comparar". + Si "comparar" es cero, continuar en [fin]. + Agregar "cuenta" a "total". + Agregar 1 a "cuenta". + Continuar en [loop]. +[fin] + Imprimir "total". ``` {{index loop, jump, "summing example"}} -Can you see how the program works at this point? The first two lines -give two memory locations their starting values: `total` will be used -to build up the result of the computation, and `count` will keep track -of the number that we are currently looking at. The lines using -`compare` are probably the weirdest ones. The program wants to see -whether `count` is equal to 11 to decide whether it can stop -running. Because our hypothetical machine is rather primitive, it can -only test whether a number is zero and make a decision based -on that. So it uses the memory location labeled `compare` to compute -the value of `count - 11` and makes a decision based on that value. -The next two lines add the value of `count` to the result and -increment `count` by 1 every time the program has decided that `count` -is not 11 yet. - -Here is the same program in JavaScript: +¿Puedes ver cómo funciona el programa en este punto? Las primeras dos líneas +le dan a dos ubicaciones de memoria sus valores iniciales: se usará `total` +para construir el resultado de la computación, y `cuenta` hará un seguimiento +del número que estamos mirando actualmente. Las líneas usando +`comparar` son probablemente las más extrañas. El programa quiere ver +si `cuenta` es igual a 11 para decidir si puede detener su ejecución. +Debido a que nuestra máquina hipotética es bastante primitiva, esta solo puede +probar si un número es cero y hace una decisión (o salta) basándose en eso. +Por lo tanto, usa la ubicación de memoria etiquetada como +`comparar` para calcular el valor de `cuenta - 11` y toma una decisión +basada en ese valor. Las siguientes dos líneas agregan el valor de `cuenta` +al resultado e incrementan `cuenta` en 1 cada vez que el programa haya decidido +que `cuenta` todavía no es 11. + +Aquí está el mismo programa en JavaScript: ``` -let total = 0, count = 1; -while (count <= 10) { - total += count; - count += 1; +let total = 0, cuenta = 1; +while (cuenta <= 10) { + total += cuenta; + cuenta += 1; } console.log(total); // → 55 ``` -{{index "while loop", loop, [braces, block]}} +{{index "while loop", loop, "curly braces"}} -This version gives us a few more improvements. Most important, there -is no need to specify the way we want the program to jump back and -forth anymore. The `while` construct takes care of that. It continues -executing the block (wrapped in braces) below it as long as the -condition it was given holds. That condition is `count <= 10`, which -means “_count_ is less than or equal to 10”. We no longer have to -create a temporary value and compare that to zero, which was just an -uninteresting detail. Part of the power of programming languages is -that they can take care of uninteresting details for us. +Esta versión nos da algunas mejoras más. La más importante, ya +no hay necesidad de especificar la forma en que queremos que el programa salte +hacia adelante y hacia atrás. El constructo del lenguaje `while` se ocupa +de eso. Este continúa ejecutando el bloque de código (envuelto en llaves) +debajo de el, siempre y cuando la condición que se le dio se mantenga. +Esa condición es `cuenta <= 10`, lo que significa "_cuenta_ es menor o igual a +10". Ya no tenemos que crear un valor temporal y compararlo con cero, +lo cual era un detalle poco interesante. Parte del poder de los lenguajes de +programación es que se encargan por nosotros de los detalles sin interés. {{index "console.log"}} -At the end of the program, after the `while` construct has finished, -the `console.log` operation is used to write out the result. +Al final del programa, después de que el `while` haya terminado, +la operación `console.log` se usa para mostrar el resultado. -{{index "sum function", "range function", abstraction, function}} +{{index "sum function", "range function", abstracción, function}} -Finally, here is what the program could look like if we happened to -have the convenient operations `range` and `sum` available, which -respectively create a ((collection)) of numbers within a range and -compute the sum of a collection of numbers: +Finalmente, aquí está cómo se vería el programa si tuviéramos acceso a las +las convenientes operaciones `rango` y `suma` disponibles, que +respectivamente crean una ((colección)) de números dentro de un rango y +calculan la suma de una colección de números: ```{startCode: true} -console.log(sum(range(1, 10))); +console.log(suma(rango(1, 10))); // → 55 ``` {{index readability}} -The moral of this story is that the same program can be expressed in -both long and short, unreadable and readable ways. The first version of the -program was extremely obscure, whereas this last one is almost -English: `log` the `sum` of the `range` of numbers from 1 to 10. (We -will see in [later chapters](data) how to define operations like `sum` -and `range`.) +La moraleja de esta historia es que el mismo programa se puede expresar en +formas largas y cortas, ilegibles y legibles. La primera versión del +programa era extremadamente oscura, mientras que esta última es casi +Español: muestra en el `log` de la consola la `suma` del `rango` de los números +1 al 10. (En [capítulos posteriores](datos) veremos cómo definir operaciones +como `suma` y `rango`.) {{index ["programming language", "power of"], composability}} -A good programming language helps the programmer by allowing them to -talk about the actions that the computer has to perform on a higher -level. It helps omit details, provides convenient building blocks -(such as `while` and `console.log`), allows you to define your own -building blocks (such as `sum` and `range`), and makes those blocks -easy to compose. +Un buen lenguaje de programación ayuda al programador permitiéndole +hablar sobre las acciones que la computadora tiene que realizar en un nivel +superior. Ayuda a omitir detalles poco interesantes, proporciona bloques +de construcción convenientes (como `while` y `console.log`), te permite +que definas tus propios bloques de construcción (como `suma` y `rango`), +y hace que esos bloques sean fáciles de componer. -## What is JavaScript? +## ¿Qué es JavaScript? -{{index history, Netscape, browser, "web application", JavaScript, [JavaScript, "history of"], "World Wide Web"}} +{{index historia, Netscape, browser, "web application", JavaScript, [JavaScript, "history of"], "World Wide Web"}} {{indexsee WWW, "World Wide Web"}} {{indexsee Web, "World Wide Web"}} -JavaScript was introduced in 1995 as a way to add programs to web -pages in the Netscape Navigator browser. The language has since been -adopted by all other major graphical web browsers. It has made modern -web applications possible—applications with which you can interact -directly without doing a page reload for every action. JavaScript is also -used in more traditional websites to provide various forms of -interactivity and cleverness. - -{{index Java, naming}} - -It is important to note that JavaScript has almost nothing to do with -the programming language named Java. The similar name was inspired by -marketing considerations rather than good judgment. When JavaScript -was being introduced, the Java language was being heavily marketed and -was gaining popularity. Someone thought it was a good idea to try to -ride along on this success. Now we are stuck with the name. - -{{index ECMAScript, compatibility}} - -After its adoption outside of Netscape, a ((standard)) document was -written to describe the way the JavaScript language should work so -that the various pieces of software that claimed to support JavaScript -were actually talking about the same language. This is called the -ECMAScript standard, after the Ecma International organization that -did the standardization. In practice, the terms ECMAScript and -JavaScript can be used interchangeably—they are two names for the same -language. - -{{index [JavaScript, "weaknesses of"], debugging}} - -There are those who will say _terrible_ things about JavaScript. Many -of these things are true. When I was required to write something in -JavaScript for the first time, I quickly came to despise it. It would -accept almost anything I typed but interpret it in a way that was -completely different from what I meant. This had a lot to do with the -fact that I did not have a clue what I was doing, of course, but there -is a real issue here: JavaScript is ridiculously liberal in what it -allows. The idea behind this design was that it would make programming -in JavaScript easier for beginners. In actuality, it mostly makes -finding problems in your programs harder because the system will not -point them out to you. +JavaScript se introdujo en 1995 como una forma de agregar programas a páginas +web en el navegador Netscape Navigator. El lenguaje ha sido desde entonces +adoptado por todos los otros navegadores web principales. Ha hecho que las +aplicaciones web modernas sean posibles: aplicaciones con las que puedes +interactuar directamente, sin hacer una recarga de página para cada acción. +JavaScript también es utilizado en sitios web más tradicionales para +proporcionar diversas formas de interactividad e ingenio. + +{{index Java, nombre}} + +Es importante tener en cuenta que JavaScript casi no tiene nada que ver con +el lenguaje de programación llamado Java. El nombre similar fue inspirado por +consideraciones de marketing, en lugar de buen juicio. Cuando JavaScript +estaba siendo introducido, el lenguaje Java estaba siendo fuertemente +comercializado y estaba ganando popularidad. Alguien pensó que era una buena +idea intentar cabalgar sobre este éxito. Ahora estamos atrapados con el nombre. + +{{index ECMAScript, compatibilidad}} + +Después de su adopción fuera de Netscape, un documento ((estándar)) fue +escrito para describir la forma en que debería funcionar el lenguaje JavaScript, +para que las diversas piezas de software que decían ser compatibles con JavaScript +en realidad estuvieran hablando del mismo lenguaje. Este se llamo el +Estándar ECMAScript, por Ecma International que +hizo la estandarización. En la práctica, los términos ECMAScript y +JavaScript se puede usar indistintamente, son dos nombres para el mismo +lenguaje. + +{{index [JavaScript, "puntos débiles"], debugging}} + +Hay quienes dirán cosas _terribles_ sobre JavaScript. Muchas de estas cosas +son verdaderas. Cuando estaba comenzando a escribir +algo en JavaScript por primera vez, rápidamente comencé a despreciarlo. +El lenguaje aceptaba casi cualquier cosa que escribiera, pero la interpretaba +de una manera que era completamente diferente de lo que quería decir. +Por supuesto, esto tenía mucho que ver con el hecho de que no tenía idea de +lo que estaba haciendo, pero hay un problema real aquí: JavaScript es +ridículamente liberal en lo que permite. La idea detrás de este diseño era +que haría a la programación en JavaScript más fácil para los principiantes. +En realidad, lo que mas hace es que encontrar problemas en tus programas +sea más difícil porque el sistema no los señalará por ti. {{index [JavaScript, "flexibility of"], flexibility}} -This flexibility also has its advantages, though. It leaves space for -a lot of techniques that are impossible in more rigid languages, and -as you will see (for example in [Chapter ?](modules)), it can be used -to overcome some of JavaScript's shortcomings. After ((learning)) the -language properly and working with it for a while, I have learned to -actually _like_ JavaScript. +Sin embargo, esta flexibilidad también tiene sus ventajas. Deja espacio para +muchas técnicas que son imposibles en idiomas más rígidos, y +como verás (por ejemplo en el [Capítulo 10](módulos)) se pueden usar +para superar algunas de las deficiencias de JavaScript. Después de +((aprender)) el idioma correctamente y luego de trabajar con él por un tiempo, +he aprendido a _querer_ a JavaScript. {{index future, [JavaScript, "versions of"], ECMAScript, "ECMAScript 6"}} -There have been several versions of JavaScript. ECMAScript version 3 -was the widely supported version in the time of JavaScript's ascent to -dominance, roughly between 2000 and 2010. During this time, work was -underway on an ambitious version 4, which planned a number of radical -improvements and extensions to the language. Changing a living, widely -used language in such a radical way turned out to be politically -difficult, and work on the version 4 was abandoned in 2008, leading to -a much less ambitious version 5, which made only some uncontroversial -improvements, coming out in 2009. Then in 2015 version 6 came out, a -major update that included some of the ideas planned for version 4. -Since then we've had new, small updates every year. - -The fact that the language is evolving means that browsers have to -constantly keep up, and if you're using an older browser, it may not -support every feature. The language designers are careful to not make -any changes that could break existing programs, so new browsers can -still run old programs. In this book, I'm using the 2017 version of -JavaScript. +Ha habido varias versiones de JavaScript. ECMAScript versión 3 +fue la versión mas ampliamente compatible en el momento del ascenso de +JavaScript a su dominio, aproximadamente entre 2000 y 2010. Durante este +tiempo, se trabajó en marcha hacia una ambiciosa versión 4, que planeaba una +serie de radicales mejoras y extensiones al lenguaje. Cambiar un lenguaje +vivo y ampliamente utilizado de una manera tan radical resultó ser +políticamente difícil, y el trabajo en la versión 4 fue abandonado en 2008, +lo que llevó a la versión 5, mucho menos ambiciosa, que se publicaría en el 2009. +Luego, en 2015, una actualización importante, incluyendo algunas de las ideas +planificadas para la versión 4, fue realizada. Desde entonces hemos tenido +actualizaciones nuevas y pequeñas cada año. + +El hecho de que el lenguaje esté evolucionando significa que los navegadores +deben mantenerse constantemente al día, y si estás usando uno más antiguo, +puede que este no soporte todas las mejoras. Los diseñadores de lenguajes +tienen cuidado de no realizar cualquier cambio que pueda romper los +programas ya existentes, de manera que los nuevos navegadores puedan todavía +ejecutar programas viejos. En este libro, usaré la versión 2017 de JavaScript. {{index [JavaScript, "uses of"]}} -Web browsers are not the only platforms on which JavaScript is used. -Some databases, such as MongoDB and CouchDB, use JavaScript as their -scripting and query language. Several platforms for desktop and server -programming, most notably the ((Node.js)) project (the subject of -[Chapter ?](node)), provide an environment for programming JavaScript -outside of the browser. +Los navegadores web no son las únicas plataformas en las que se usa JavaScript. +Algunas bases de datos, como MongoDB y CouchDB, usan JavaScript como su +lenguaje de scripting y consultas. Varias plataformas para programación de +escritorio y servidores, más notablemente el proyecto ((Node.js)) (el tema del +[Capítulo 20](Node)) proporcionan un entorno para programar en JavaScript +fuera del navegador. -## Code, and what to do with it +## Código, y qué hacer con él {{index "reading code", "writing code"}} -_Code_ is the text that makes up programs. Most chapters in this book -contain quite a lot of code. I believe reading code and writing ((code)) -are indispensable parts of ((learning)) to program. Try to not just -glance over the examples—read them attentively and understand them. -This may be slow and confusing at first, but I promise that you'll -quickly get the hang of it. The same goes for the ((exercises)). Don't -assume you understand them until you've actually written a working -solution. +_Código_ es el texto que compone los programas. La mayoría de los capítulos +en este libro contienen bastante. Creo que leer código y escribir +((código)) son partes indispensables del ((aprendizaje)) para programar. +Trata de no solo echar un vistazo a los ejemplos, léelos atentamente +y entiéndelos. Esto puede ser algo lento y confuso al principio, pero te +prometo que rápidamente vas agarrar el truco. Lo mismo ocurre con los +((ejercicios)). No supongas que los entiendes hasta que hayas +escrito una solución funcional para resolverlos. {{index interpretation}} -I recommend you try your solutions to exercises in an actual -JavaScript interpreter. That way, you'll get immediate feedback on -whether what you are doing is working, and, I hope, you'll be tempted -to ((experiment)) and go beyond the exercises. +Te recomiendo que pruebes tus soluciones a los ejercicios en un intérprete real +de JavaScript. De esta forma, obtendrás retroalimentación inmediata acerca +de que si esta funcionando lo que estás haciendo, y, espero, serás tentado +a ((experimentar)) e ir más allá de los ejercicios. {{if interactive -When reading this book in your browser, you can edit (and run) all -example programs by clicking them. +Al leer este libro en tu navegador, puedes editar (y ejecutar) todos los +programas de ejemplo haciendo clic en ellos. if}} @@ -411,107 +420,106 @@ if}} {{index download, sandbox, "running code"}} -The easiest way to run the example code in the book, and to experiment -with it, is to look it up in the online version of the book at -[_https://eloquentjavascript.net_](https://eloquentjavascript.net/). There, -you can click any code example to edit and run it and to see the -output it produces. To work on the exercises, go to -[_https://eloquentjavascript.net/code_](https://eloquentjavascript.net/code), -which provides starting code for each coding exercise and allows you -to look at the solutions. +La forma más fácil de ejecutar el código de ejemplo en el libro y experimentar +con él, es buscarlo en la versión en línea del libro en +[_eloquentjavascript.net_](https://eloquentjavascript.net/). Alli +puedes hacer clic en cualquier ejemplo de código para editar y ejecutarlo +y ver el resultado que produce. Para trabajar en los ejercicios, ve a +[_eloquentjavascript.net/code_](https://eloquentjavascript.net/code), +que proporciona el código de inicio para cada ejercicio de programación +y te permite ver las soluciones. if}} {{index "developer tools", "JavaScript console"}} -If you want to run the programs defined in this book outside of the -book's website, some care will be required. Many examples stand on their -own and should work in any JavaScript environment. But code in later -chapters is often written for a specific environment (the browser or -Node.js) and can run only there. In addition, many chapters define -bigger programs, and the pieces of code that appear in them depend on -each other or on external files. The -[sandbox](https://eloquentjavascript.net/code) on the website provides -links to Zip files containing all the scripts and data files -necessary to run the code for a given chapter. - -## Overview of this book - -This book contains roughly three parts. The first 12 chapters discuss -the JavaScript language. The next seven chapters are about web -((browsers)) and the way JavaScript is used to program them. Finally, -two chapters are devoted to ((Node.js)), another environment to -program JavaScript in. - -Throughout the book, there are five _project chapters_, which describe -larger example programs to give you a taste of actual programming. In -order of appearance, we will work through building a [delivery -robot](robot), a [programming language](language), a [platform -game](game), a [pixel paint program](paint), and a [dynamic -website](skillsharing). - -The language part of the book starts with four chapters that introduce -the basic structure of the JavaScript language. They introduce -[control structures](program_structure) (such as the `while` word you -saw in this introduction), [functions](functions) (writing your own -building blocks), and [data structures](data). After these, you will -be able to write basic programs. Next, Chapters [?](higher_order) and -[?](object) introduce techniques to use functions and objects to write -more _abstract_ code and keep complexity under control. - -After a [first project chapter](robot), the language part of the book -continues with chapters on [error handling and bug fixing](error), -[regular expressions](regexp) (an important tool for working with -text), [modularity](modules) (another defense against complexity), and -[asynchronous programming](async) (dealing with events that take -time). The [second project chapter](language) concludes the first part -of the book. - -The second part, Chapters [?](browser) to [?](paint), describes the -tools that browser JavaScript has access to. You'll learn to display -things on the screen (Chapters [?](dom) and [?](canvas)), respond to -user input ([Chapter ?](event)), and communicate over the network -([Chapter ?](http)). There are again two project chapters in this -part. - -After that, [Chapter ?](node) describes Node.js, and [Chapter -?](skillsharing) builds a small website using that tool. +Si deseas ejecutar los programas definidos en este libro fuera de +la caja de arena del libro, se requiere cierto cuidado. Muchos ejemplos +se mantienen por si mismos y deberían de funcionar en cualquier entorno de +JavaScript. Pero código en capítulos más avanzados a menudo se escribe para +un entorno específico (el navegador o Node.js) y solo puede ser ejecutado allí. +Además, muchos capítulos definen programas más grandes, y las piezas de código +que aparecen en ellos dependen de otras piezas o de archivos externos. La +[caja de arena](https://eloquentjavascript.net/code) +en el sitio web proporciona enlaces a archivos Zip que contienen todos +los scripts y archivos de datos necesarios para ejecutar el código de +un capítulo determinado. + +## Descripción general de este libro + +Este libro contiene aproximadamente tres partes. Los primeros 12 capítulos +discuten el lenguaje JavaScript en sí. Los siguientes siete capítulos son +acerca de los ((navegadores)) web y la forma en la que JavaScript es usado +para programarlos. Finalmente, dos capítulos están dedicados a ((Node.js)), +otro entorno en donde programar JavaScript. + +A lo largo del libro, hay cinco _capítulos de proyectos_, que describen +programas de ejemplo más grandes para darte una idea de la programación real. En +orden de aparición, trabajaremos en la construcción de un +[robot de entrega](robot), un [lenguaje de programación](lenguaje), +un [juego de plataforma](juego), un [programa de pintura](paint) y un [sitio web dinámico](skillsharing). + +La parte del lenguaje del libro comienza con cuatro capítulos para presentar +la estructura básica del lenguaje de JavaScript. Estos introducen +[estructuras de control](estructura_de_programa) (como la palabra `while` +que ya viste en esta introducción), [funciones](funciones) (escribir tus propios +bloques de construcción), y [estructuras de datos](datos). Después de estos, seras +capaz de escribir programas simples. Luego, los Capítulos [5](orden_superior) y +[6](objeto) introducen técnicas para usar funciones y objetos y asi escribir +código más _abstracto_ y de manera que puedas mantener la complejidad bajo control. + +Después de un [primer capítulo de proyecto](robot), la primera parte del libro +continúa con los capítulos sobre [manejo y solución de errores](error), en +[expresiones regulares](regexp) (una herramienta importante para trabajar con +texto), en [modularidad](modulos) (otra defensa contra la complejidad), +y en [programación asincrónica](async) (que se encarga de eventos que +toman tiempo). El [segundo capítulo de proyecto](lenguaje) concluye la primera +parte del libro. + +La segunda parte, Capítulos [13](navegador) a [19](paint), describe las +herramientas a las que el JavaScript en un navegador tiene acceso. Aprenderás a +mostrar cosas en la pantalla (Capítulos [14](dom) y [17](canvas)), responder a +entradas de usuario ([Capitulo 15](evento)), y a comunicarte a través de la red +([Capitulo 18](http)). Hay dos capítulos de proyectos en este parte. + +Después de eso, el [Capítulo 20](node) describe Node.js, y el +[Capitulo 21](skillsharing) construye un pequeño sistema web usando esta herramienta. {{if commercial -Finally, [Chapter ?](fast) describes some of the considerations that -come up when optimizing JavaScript programs for speed. +Finalmente, el [Capítulo 22](rapido) describe algunas de las consideraciones que +aparecen cuando se optimizan los programas de JavaScript para tener mas velocidad. if}} -## Typographic conventions +## Convenciones tipográficas {{index "factorial function"}} -In this book, text written in a `monospaced` font will represent -elements of programs—sometimes they are self-sufficient fragments, and -sometimes they just refer to part of a nearby program. Programs (of -which you have already seen a few) are written as follows: +En este libro, el texto escrito en una fuente `monoespaciada` representará +elementos de programas, a veces son fragmentos autosuficientes, y +a veces solo se refieren a partes de un programa cercano. Los programas (de +los que ya has visto algunos), se escriben de la siguiente manera: ``` -function factorial(n) { - if (n == 0) { +function factorial(numero) { + if (numero == 0) { return 1; } else { - return factorial(n - 1) * n; + return factorial(numero - 1) * numero; } } ``` {{index "console.log"}} -Sometimes, to show the output that a program produces, the -expected output is written after it, with two slashes and an arrow in -front. +Algunas veces, para mostrar el resultado que produce un programa, +la salida esperada se escribe después de el, con dos diagonales y una flecha en +frente. ``` console.log(factorial(8)); // → 40320 ``` -Good luck! +¡Buena suerte! diff --git a/01_values.md b/01_values.md index 63cb2533d..bec164908 100644 --- a/01_values.md +++ b/01_values.md @@ -1,90 +1,91 @@ {{meta {docid: values}}} -# Values, Types, and Operators +# Valores, Tipos, y Operadores {{quote {author: "Master Yuan-Ma", title: "The Book of Programming", chapter: true} -Below the surface of the machine, the program moves. Without effort, -it expands and contracts. In great harmony, electrons scatter and -regroup. The forms on the monitor are but ripples on the water. The -essence stays invisibly below. +Debajo de la superficie de la máquina, el programa se mueve. Sin esfuerzo, +se expande y se contrae. En gran armonía, los electrones se dispersan y +se reagrupan. Las figuras en el monitor son tan solo ondas sobre el agua. +La esencia se mantiene invisible debajo de la superficie. quote}} {{index "Yuan-Ma", "Book of Programming"}} -{{figure {url: "img/chapter_picture_1.jpg", alt: "Picture of a sea of bits", chapter: framed}}} +{{figure {url: "img/chapter_picture_1.jpg", alt: "Una foto de un mar de bits", chapter: framed}}} {{index "binary data", data, bit, memory}} -Inside the computer's world, there is only data. You can read data, -modify data, create new data—but that which isn't data cannot be -mentioned. All this data is stored as long sequences of bits and is -thus fundamentally alike. +Dentro del mundo de la computadora, solo existen datos. +Puedes leer datos, modificar datos, crear nuevos +datos—pero todo lo que no sean datos, no puede ser mencionado. +Toda estos datos están almacenados como largas secuencias +de bits, y por lo tanto, todos los datos son fundamentalmente parecidos. {{index CD, signal}} -_Bits_ are any kind of two-valued things, usually described as zeros and -ones. Inside the computer, they take forms such as a high or low -electrical charge, a strong or weak signal, or a shiny or dull spot on -the surface of a CD. Any piece of discrete information can be reduced -to a sequence of zeros and ones and thus represented in bits. +Los _bits_ son cualquier tipo de cosa que pueda tener dos valores, usualmente +descritos como ceros y unos. Dentro de la computadora, estos toman formas +tales como cargas eléctricas altas o bajas, una señal fuerte o débil, o un +punto brillante u opaco en la superficie de un CD. Cualquier pedazo de +información discreta puede ser reducida a una secuencia de ceros y unos y, +de esa manera ser representada en bits. {{index "binary number", radix, "decimal number"}} -For example, we can express the number 13 in bits. It works the same -way as a decimal number, but instead of 10 different ((digit))s, you -have only 2, and the weight of each increases by a factor of 2 from -right to left. Here are the bits that make up the number 13, with the -weights of the digits shown below them: +Por ejemplo, podemos expresar el numero 13 en bits. Funciona de la misma +manera que un número decimal, pero en vez de 10 diferentes dígitos, solo +tienes 2, y el peso de cada uno aumenta por un factor de 2 de derecha a +izquierda. Aquí tenemos los bits que conforman el número 13, con el peso +de cada dígito mostrado debajo de el: ```{lang: null} 0 0 0 0 1 1 0 1 128 64 32 16 8 4 2 1 ``` -So that's the binary number 00001101. Its non-zero digits stand for -8, 4, and 1, and add up to 13. +Entonces ese es el número binario 00001101, o 8 + 4 + 1, o 13. -## Values +## Valores -{{index [memory, organization], "volatile data storage", "hard drive"}} +{{index memory, "volatile data storage", "hard drive"}} -Imagine a sea of bits—an ocean of them. A typical modern computer has -more than 30 billion bits in its volatile data storage (working -memory). Nonvolatile storage (the hard disk or equivalent) tends to -have yet a few orders of magnitude more. +Imagina un mar de bits, un océano repleto. Una computadora moderna +promedio tiene mas de 30 billones de bits en su almacenamiento de datos +volátiles (memoria funcional). El almacenamiento no volátil +(disco duro o equivalente) tiende a tener unas cuantas mas ordenes de magnitud. -To be able to work with such quantities of bits without getting lost, -we must separate them into chunks that represent pieces of -information. In a JavaScript environment, those chunks are called -_((value))s_. Though all values are made of bits, they play different -roles. Every value has a ((type)) that determines its role. Some -values are numbers, some values are pieces of text, some values are -functions, and so on. +Para poder trabajar con tales cantidades de bits sin perdernos, +debemos separarlos en porciones que representen pedazos de información. +En un entorno de JavaScript, esas porciones son llamadas _((valores))_. +Aunque todos los valores están hechos de bits, estos juegan papeles diferentes. +Cada valor tiene un ((tipo)) que determina su rol. Algunos valores son números, +otros son pedazos de texto, otros son funciones, y asi sucesivamente. {{index "garbage collection"}} -To create a value, you must merely invoke its name. This is -convenient. You don't have to gather building material for your values -or pay for them. You just call for one, and _whoosh_, you have it. They -are not really created from thin air, of course. Every value has to be -stored somewhere, and if you want to use a gigantic amount of them at -the same time, you might run out of memory. Fortunately, this is a -problem only if you need them all simultaneously. As soon as you no -longer use a value, it will dissipate, leaving behind its bits to be -recycled as building material for the next generation of values. +Para crear un valor, solo debemos de invocar su nombre. Esto es +conveniente. No tenemos que recopilar materiales de construcción para nuestros +valores, o pagar por ellos. Solo llamamos su nombre, y _woosh_, ahi lo tienes. +Estos no son realmente creados de la nada, por supuesto. Cada valor +tiene que ser almacenado en algún sitio, y si quieres usar una cantidad gigante +de valores al mismo tiempo, puede que te quedes sin memoria. Afortunadamente, +esto solo es un problema si los necesitas todos al mismo tiempo. +Tan pronto como dejes de utilizar un valor, este se disipará, dejando atrás +sus bits para que estos sean reciclados como material de construcción para la +próxima generación de valores. -This chapter introduces the atomic elements of JavaScript programs, -that is, the simple value types and the operators that can act on such -values. +Este capitulo introduce los elementos atómicos de los programas en JavaScript, +estos son, los tipos de valores simples y los operadores que actúan en +tales valores. -## Numbers +## Números -{{index [syntax, number], number, [number, notation]}} +{{index syntax, number, [number, notation]}} -Values of the _number_ type are, unsurprisingly, numeric values. In a -JavaScript program, they are written as follows: +Valores del tipo _number_ (número) son, como es de esperar, valores numéricos. +En un programa hecho en JavaScript, se escriben de la siguiente manera: ``` 13 @@ -92,40 +93,42 @@ JavaScript program, they are written as follows: {{index "binary number"}} -Use that in a program, and it will cause the bit pattern for the -number 13 to come into existence inside the computer's memory. +Utiliza eso en un programa, y ocasionará que el patrón de bits +que representa el número 13 sea creado dentro de la memoria de la computadora. -{{index [number, representation], bit}} +{{index [number, representación], bit}} -JavaScript uses a fixed number of bits, 64 of them, to store a -single number value. There are only so many patterns you can make with -64 bits, which means that the number of different numbers that can be -represented is limited. With _N_ decimal ((digit))s, you can represent -10^N^ numbers. Similarly, given 64 binary -digits, you can represent 2^64^ different numbers, which is about 18 -quintillion (an 18 with 18 zeros after it). That's a lot. +JavaScript utiliza un número fijo de bits, específicamente 64, +para almacenar un solo valor numérico. Solo existen una cantidad finita de +patrones que podemos crear con 64 bits, lo que significa que la cantidad de +números diferentes que pueden ser representados es limitada. Para una +cantidad de _N_ dígitos decimales, la cantidad de números que pueden ser +representados es 10^N^. Del mismo modo, dados 64 dígitos binarios, podemos +representar 2^64^ números diferentes, lo que es alrededor de 18 trillones +(un 18 con 18 ceros más). Eso es muchísimo. -Computer memory used to be much smaller, and people tended to use -groups of 8 or 16 bits to represent their numbers. It was easy to -accidentally _((overflow))_ such small numbers—to end up with a number -that did not fit into the given number of bits. Today, even computers -that fit in your pocket have plenty of memory, so you are free to use -64-bit chunks, and you need to worry about overflow only when dealing -with truly astronomical numbers. +La memoria de un computador solía ser mucho mas pequeña que en la actualidad, +y las personas tendían a utilizar grupos de 8 o 16 bits para representar sus +números. Era común accidentalmente _((desbordar))_ esta limitación— terminando +con un número que no cupiera dentro de la cantidad dada de bits. Hoy en día, +incluso computadoras que caben dentro de tu bolsillo poseen de bastante memoria, +por lo que somos libres de usar pedazos de memoria de 64 bits, y solamente +nos tenemos que preocupar por desbordamientos de memoria cuando lidiamos +con números verdaderamente astronómicos. -{{index sign, "floating-point number", "sign bit"}} +{{index sign, "floating-point number", "fractional number", "sign bit"}} -Not all whole numbers less than 18 quintillion fit in a JavaScript number, -though. Those bits also store negative numbers, so one bit indicates -the sign of the number. A bigger issue is that nonwhole numbers must -also be represented. To do this, some of the bits are used to store -the position of the decimal point. The actual maximum whole number -that can be stored is more in the range of 9 quadrillion (15 -zeros)—which is still pleasantly huge. +A pesar de esto, no todos los números enteros por debajo de 18 +trillones caben en un número de JavaScript. Esos bits también almacenan +números negativos, por lo que un bit indica el signo de un número. Un problema +mayor es que los números no enteros tienen que ser representados también. +Para hacer esto, algunos de los bits son usados para almacenar la posición +del punto decimal. El número entero mas grande que puede ser +almacenado está en el rango de los nueve mil billones (15 ceros), que sigue siendo inmenso. -{{index [number, notation], "fractional number"}} +{{index [number, notation]}} -Fractional numbers are written by using a dot. +Los números fraccionarios se escriben usando un punto: ``` 9.81 @@ -133,35 +136,36 @@ Fractional numbers are written by using a dot. {{index exponent, "scientific notation", [number, notation]}} -For very big or very small numbers, you may also use scientific -notation by adding an _e_ (for _exponent_), followed by the exponent -of the number. +Para números muy grandes o muy pequeños, pudiéramos también usar notación +científica agregando una _e_ (de "exponente"), seguida por el exponente +del número: ``` 2.998e8 ``` -That is 2.998 × 10^8^ = 299,800,000. +Eso es 2.998 × 10^8^ = 299,800,000. {{index pi, [number, "precision of"], "floating-point number"}} -Calculations with whole numbers (also called _((integer))s_) smaller -than the aforementioned 9 quadrillion are guaranteed to always be -precise. Unfortunately, calculations with fractional numbers are -generally not. Just as π (pi) cannot be precisely expressed by a -finite number of decimal digits, many numbers lose some precision when -only 64 bits are available to store them. This is a shame, but it -causes practical problems only in specific situations. The important -thing is to be aware of it and treat fractional digital numbers as -approximations, not as precise values. +Los cálculos con números enteros (también llamados _((integer))s_) +mas pequeños a los nueve mil billones anteriormente mencionados están +garantizados a ser siempre precisos. Desafortunadamente, los cálculos +con números fraccionarios, generalmente no lo son. Así como π (pi) no puede +ser precisamente expresado por un número finito de números decimales, +muchos números pierden algo de precisión cuando solo hay 64 bits disponibles +para almacenarlos. Esto es una pena, pero solo causa problemas prácticos +en situaciones especificas. Lo importante es que debemos ser consciente de estas +limitaciones y tratar a los números fraccionarios como aproximaciones, +no como valores precisos. -### Arithmetic +### Aritmética -{{index [syntax, operator], operator, "binary operator", arithmetic, addition, multiplication}} +{{index syntax, operator, "binary operator", arithmetic, addition, multiplication}} -The main thing to do with numbers is arithmetic. Arithmetic operations -such as addition or multiplication take two number values and produce -a new number from them. Here is what they look like in JavaScript: +Lo que mayormente se hace con los números es aritmética. Operaciones aritméticas +tales como la adición y la multiplicación, toman dos valores numéricos y +producen un nuevo valor a raíz de ellos. Asi es como lucen en JavaScript: ``` 100 + 4 * 11 @@ -169,190 +173,196 @@ a new number from them. Here is what they look like in JavaScript: {{index [operator, application], asterisk, "plus character", "* operator", "+ operator"}} -The `+` and `*` symbols are called _operators_. The first stands for -addition, and the second stands for multiplication. Putting an -operator between two values will apply it to those values and produce -a new value. +Los símbolos `+` y `*` son llamados _operadores_. El primero representa a la +adición, y el segundo representa a la multiplicación. Colocar un operador entre +dos valores aplicará la operación asociada a esos valores y producirá un nuevo valor. {{index grouping, parentheses, precedence}} -But does the example mean "add 4 and 100, and multiply the result by 11," -or is the multiplication done before the adding? As you might have -guessed, the multiplication happens first. But as in mathematics, you -can change this by wrapping the addition in parentheses. +¿Pero el ejemplo significa "agrega 4 y 100, y multiplica el resultado por 11", +o es la multiplicación aplicada antes de la adición? Como quizás hayas +podido adivinar, la multiplicación sucede primero. Pero asi como en +las matemáticas, puedes cambiar este orden envolviendo la adición en paréntesis: ``` (100 + 4) * 11 ``` -{{index "hyphen character", "slash character", division, subtraction, minus, "- operator", "/ operator"}} +{{index "dash character", "slash character", division, subtraction, minus, "- operator", "/ operator"}} -For subtraction, there is the `-` operator, and division can be done -with the `/` operator. +Para sustraer, existe el operador `-`, y la división puede ser realizada +con el operador `/`. -When operators appear together without parentheses, the order in which -they are applied is determined by the _((precedence))_ of the -operators. The example shows that multiplication comes before -addition. The `/` operator has the same precedence as `*`. Likewise -for `+` and `-`. When multiple operators with the same precedence -appear next to each other, as in `1 - 2 + 1`, they are applied left to -right: `(1 - 2) + 1`. +Cuando operadores aparecen juntos sin paréntesis, el orden en el cual +son aplicados es determinado por la _((precedencia))_ de los operadores. +El ejemplo muestra que la multiplicación es aplicada antes que la adición. +El operador `/` tiene la misma precedencia que `*`. Lo mismo aplica para +`+` y `-`. Cuando operadores con la misma precedencia aparecen uno al lado +del otro, como en `1 - 2 + 1`, estos se aplican de izquierda a derecha: +`(1 - 2) + 1`. -These rules of precedence are not something you should worry about. -When in doubt, just add parentheses. +Estas reglas de precedencia no son algo de lo que deberías preocuparte. +Cuando tengas dudas, solo agrega un paréntesis. -{{index "modulo operator", division, "remainder operator", "% operator"}} +{{index "operador módulo", división, "operador de sobrante", "operador %"}} -There is one more arithmetic operator, which you might not immediately -recognize. The `%` symbol is used to represent the _remainder_ -operation. `X % Y` is the remainder of dividing `X` by `Y`. For -example, `314 % 100` produces `14`, and `144 % 12` gives `0`. -The remainder operator's precedence is the same as that of multiplication and -division. You'll also often see this operator referred to as _modulo_. +Existe otro operador aritmético que quizás no reconozcas inmediatamente. +El símbolo `%` es utilizado para representar la operación de _residuo_. +`X % Y` es el residuo de dividir `X` entre `Y`. Por ejemplo, `314 % 100` +produce `14`, y `144 % 12` produce `0`. La precedencia del residuo es la +la misma que la de la multiplicación y la división. Frecuentemente veras +que este operador es también conocido como _modulo_. -### Special numbers +### Números especiales {{index [number, "special values"]}} -There are three special values in JavaScript that are considered -numbers but don't behave like normal numbers. +Existen 3 valores especiales en JavaScript que son considerados +números pero que no se comportan como números normales. {{index infinity}} -The first two are `Infinity` and `-Infinity`, which represent the -positive and negative infinities. `Infinity - 1` is still `Infinity`, -and so on. Don't put too much trust in infinity-based computation, -though. It isn't mathematically sound, and it will quickly lead to the -next special number: `NaN`. +Los primeros dos son `Infinity` y `-Infinity`, los cuales representan las +infinidades positivas y negativas. `Infinity - 1` aun es +`Infinity`, y asi sucesivamente. A pesar de esto, no confíes mucho +en computaciones que dependan de infinidades. Estas no son matemáticamente +confiables, y puede que muy rápidamente nos resulten en el próximo número +especial: `NaN`. {{index NaN, "not a number", "division by zero"}} -`NaN` stands for "not a number", even though it _is_ a value of the -number type. You'll get this result when you, for example, try to -calculate `0 / 0` (zero divided by zero), `Infinity - Infinity`, or -any number of other numeric operations that don't yield a meaningful -result. +`NaN` significa "no es un número" ("Not A Number"), +aunque _sea_ un valor del tipo numérico. +Obtendras este resultado cuando, por ejemplo, trates de calcular +`0 / 0` (cero dividido entre cero), `Infinity - Infinity`, o cualquier +otra cantidad de operaciones numéricas que no produzcan un resultado +significante. ## Strings {{indexsee "grave accent", backtick}} -{{index [syntax, string], text, character, [string, notation], "single-quote character", "double-quote character", "quotation mark", backtick}} +{{index syntax, text, character, [string, notation], "single-quote character", "double-quote character", "quotation mark", backtick}} -The next basic data type is the _((string))_. Strings are used to -represent text. They are written by enclosing their content in quotes. +El próximo tipo de dato básico es el _((string))_. Las cadenas (_strings_) +son usados para representar texto. Son escritas encerrando su contenido +en comillas: ``` -`Down on the sea` -"Lie on the ocean" -'Float on the ocean' +`Debajo en el mar` +"Descansa en el océano" +'Flota en el océano' ``` -You can use single quotes, double quotes, or backticks to mark -strings, as long as the quotes at the start and the end of the string -match. +Puedes usar comillas simples, comillas dobles, o comillas invertidas +para representar strings, siempre y cuando las comillas al principio +y al final coincidan. {{index "line break", "newline character"}} -Almost anything can be put between quotes, and JavaScript will make a -string value out of it. But a few characters are more difficult. You -can imagine how putting quotes between quotes might be hard. -_Newlines_ (the characters you get when you press [enter]{keyname}) can be -included without escaping only when the string is quoted with backticks -(`` ` ``). +Casi todo puede ser colocado entre comillas, y JavaScript construirá +un valor string a partir de ello. Pero algunos caracteres son mas difíciles. +Te puedes imaginar que colocar comillas entre comillas podría ser difícil. +Los _saltos de línea_ (los caracteres que obtienes cuando presionas la tecla de Enter) +solo pueden ser incluidos cuando el string está encapsulado con comillas +invertidas (`` ` ``). -{{index [escaping, "in strings"], ["backslash character", "in strings"]}} +{{index [escaping, "in strings"], "backslash character"}} -To make it possible to include such characters in a string, the -following notation is used: whenever a backslash (`\`) is found inside -quoted text, it indicates that the character after it has a special -meaning. This is called _escaping_ the character. A quote that is -preceded by a backslash will not end the string but be part of it. -When an `n` character occurs after a backslash, it is interpreted as a -newline. Similarly, a `t` after a backslash means a ((tab character)). -Take the following string: +Para hacer posible incluir tales caracteres en un string, la siguiente +notación es utilizada: cuando una barra invertida (`\`) es encontrada dentro de +un texto entre comillas, indica que el carácter que le sigue tiene un +significado especial. Esto se conoce como _escapar_ el carácter. +Una comilla que es precedida por una barra invertida no representará +el final del string sino que formara parte del mismo. Cuando el +carácter `n` es precedido por una barra invertida, este se interpreta +como un Newline (salto de linea). De la mima forma, `t` después de una barra +invertida, se interpreta como un character de tabulación. +Toma como referencia el siguiente string: ``` -"This is the first line\nAnd this is the second" +"Esta es la primera linea\nY esta es la segunda" ``` -The actual text contained is this: +El texto actual es este: ```{lang: null} -This is the first line -And this is the second +Esta es la primera linea +Y esta es la segunda ``` -There are, of course, situations where you want a backslash in a -string to be just a backslash, not a special code. If two backslashes -follow each other, they will collapse together, and only one will be -left in the resulting string value. This is how the string "_A newline -character is written like `"`\n`"`._" can be expressed: +Se encuentran, por supuesto, situaciones donde queremos que una barra +invertida en un string solo sea una barra invertida, y no un carácter +especial. Si dos barras invertidas prosiguen una a la otra, serán +colapsadas y sólo una permanecerá en el valor resultante del string. +Asi es como el string "_Un carácter de salto de linea es escrito +así: `"`\n`"`._" puede ser expresado: ``` -"A newline character is written like \"\\n\"." +Un carácter de salto de linea es escrito así: \"\\n\"." ``` {{id unicode}} {{index [string, representation], Unicode, character}} -Strings, too, have to be modeled as a series of bits to be able to -exist inside the computer. The way JavaScript does this is based on -the _((Unicode))_ standard. This standard assigns a number to -virtually every character you would ever need, including characters -from Greek, Arabic, Japanese, Armenian, and so on. If we have a number -for every character, a string can be described by a sequence of -numbers. +Las cadenas deben de ser modeladas como una serie de bits para poder +existir dentro de la computadora. La forma en la que JavaScript hace esto +es basada en el estándar _((Unicode))_. Este estándar asigna un número a +todo carácter que alguna vez pudieras necesitar, incluyendo caracteres en +Griego, Árabe, Japones, Armenio, y asi sucesivamente. Si tenemos un número para +representar cada carácter, un string puede ser descrito como una +secuencia de números. {{index "UTF-16", emoji}} -And that's what JavaScript does. But there's a complication: -JavaScript's representation uses 16 bits per string element, which can -describe up to 2^16^ different characters. But Unicode defines more -characters than that—about twice as many, at this point. So some -characters, such as many emoji, take up two "character positions" in -JavaScript strings. We'll come back to this in [Chapter -?](higher_order#code_units). +Y eso es lo que hace JavaScript. Pero hay una complicación: +La representación de JavaScript usa 16 bits por cada elemento string, en +el cual caben 2^16^ números diferentes. Pero Unicode define mas caracteres +que aquellos—aproximadamente el doble, en este momento. +Entonces algunos caracteres, como muchos emojis, necesitan ocupar dos +"posiciones de caracteres" en los strings de JavaScript. Volveremos a este +tema en el [Capitulo 5](orden_superior#unidades_de_codigo). {{index "+ operator", concatenation}} -Strings cannot be divided, multiplied, or subtracted, but the `+` -operator _can_ be used on them. It does not add, but it -_concatenates_—it glues two strings together. The following line will -produce the string `"concatenate"`: +Los strings no pueden ser divididas, multiplicados o restados, pero +el operador `+` _puede_ ser utilizado en ellos. No los suma, sino que +los _concatena_: pega dos cadenas una tras otra. La siguiente línea producirá +el string `"concatenar"`: ``` -"con" + "cat" + "e" + "nate" +"con" + "cat" + "e" + "nar" ``` -String values have a number of associated functions (_methods_) that -can be used to perform other operations on them. I'll say more about -these in [Chapter ?](data#methods). +Los valores string tienen un conjunto de funciones (_métodos_) asociadas, +que pueden ser usadas para realizar operaciones en ellos. Regresaremos +a estas en el [Capítulo 4](datos#metodos). {{index interpolation, backtick}} -Strings written with single or double quotes behave very much the -same—the only difference is in which type of quote you need to escape -inside of them. Backtick-quoted strings, usually called _((template -literals))_, can do a few more tricks. Apart from being able to span -lines, they can also embed other values. +Los strings escritos con comillas simples o dobles se comportan +casi de la misma manera—La única diferencia es el tipo de comilla que necesitamos +para escapar dentro de ellos. Los strings de comillas inversas, usualmente +llamados _((plantillas literales))_, pueden realizar algunos trucos +más. Mas allá de permitir salto de línea, pueden también incrustar otros +valores. ``` -`half of 100 is ${100 / 2}` +`la mitad de 100 es ${100 / 2}` ``` -When you write something inside `${}` in a template literal, its -result will be computed, converted to a string, and included at that -position. The example produces "_half of 100 is 50_". +Cuando escribes algo dentro de `${}` en una plantilla literal, el +resultado será computado, convertido a string, e incluido en esa +posición. El ejemplo anterior produce "_la mitad de 100 es 50_". -## Unary operators +## Operadores unarios {{index operator, "typeof operator", type}} -Not all operators are symbols. Some are written as words. One example -is the `typeof` operator, which produces a string value naming the -type of the value you give it. +No todo los operadores son símbolos. Algunos se escriben como palabras. +Un ejemplo es el operador `typeof`, que produce un string con el nombre +del tipo de valor que le demos. ``` console.log(typeof 4.5) @@ -365,37 +375,37 @@ console.log(typeof "x") {{id "console.log"}} -We will use `console.log` in example code to indicate that we want to -see the result of evaluating something. More about that in the [next -chapter](program_structure). +Usaremos `console.log` en los ejemplos de código para indicar que +que queremos ver el resultado de alguna evaluación. +Mas acerca de esto esto en el [proximo capitulo](estructura_de_programa). {{index negation, "- operator", "binary operator", "unary operator"}} -The other operators shown all operated on two values, but `typeof` -takes only one. Operators that use two values are called _binary_ -operators, while those that take one are called _unary_ operators. The -minus operator can be used both as a binary operator and as a unary -operator. +En los otros operadores que hemos visto hasta ahora, todos operaban en +dos valores, pero `typeof` sola opera con un valor. Los operadores que usan +dos valores son llamados operadores _binarios_, mientras que aquellos +operadores que usan uno son llamados operadores _unarios_. El operador menos +puede ser usado tanto como un operador binario o como un operador unario. ``` console.log(- (10 - 2)) // → -8 ``` -## Boolean values +## Valores Booleanos {{index Boolean, operator, true, false, bit}} -It is often useful to have a value that distinguishes between only two -possibilities, like "yes" and "no" or "on" and "off". For this -purpose, JavaScript has a _Boolean_ type, which has just two values, -true and false, which are written as those words. +Es frecuentemente util tener un valor que distingue entre solo dos +posibilidades, como "si", y "no", o "encendido" y "apagado". Para este +propósito, JavaScript tiene el tipo _Boolean_, que tiene solo dos valores: +true (verdadero) y false (falso) que se escriben de la misma forma. -### Comparison +### Comparación -{{index comparison}} +{{index comparación}} -Here is one way to produce Boolean values: +Aquí se muestra una forma de producir valores Booleanos: ``` console.log(3 > 2) @@ -404,14 +414,14 @@ console.log(3 < 2) // → false ``` -{{index [comparison, "of numbers"], "> operator", "< operator", "greater than", "less than"}} +{{index [comparación, "de números"], "> operador", "< operator", "greater than", "less than"}} -The `>` and `<` signs are the traditional symbols for "is greater -than" and "is less than", respectively. They are binary operators. -Applying them results in a Boolean value that indicates whether they -hold true in this case. +Los signos `>` y `<` son tradicionalmente símbolos para "mayor que" +y "menor que", respectivamente. Ambos son operadores binarios. +Aplicarlos resulta en un valor Boolean que indica si la condición +que indican se cumple. -Strings can be compared in the same way. +Los Strings pueden ser comparados de la misma forma. ``` console.log("Aardvark" < "Zoroaster") @@ -420,51 +430,52 @@ console.log("Aardvark" < "Zoroaster") {{index [comparison, "of strings"]}} -The way strings are ordered is roughly alphabetic but not really what -you'd expect to see in a dictionary: uppercase letters are always -"less" than lowercase ones, so `"Z" < "a"`, and nonalphabetic -characters (!, -, and so on) are also included in the ordering. When -comparing strings, JavaScript goes over the characters from left to -right, comparing the ((Unicode)) codes one by one. +La forma en la que los strings son ordenados, es aproximadamente alfabético, +aunque no realmente de la misma forma que esperaríamos ver en un diccionario: +las letras mayúsculas son siempre "menores que" las letras minúsculas, así que +`"Z" < "a"`, y caracteres no alfabéticos (como `!`, `-` y demás) son también +incluidos en el ordenamiento. Cuando comparamos strings, JavaScript evalúa +los caracteres de izquierda a derecha, comparando los códigos ((Unicode)) +uno por uno. -{{index equality, ">= operator", "<= operator", "== operator", "!= operator"}} +{{index igualdad, "operador >=", "operador <=", "operador ==", "operador !="}} -Other similar operators are `>=` (greater than or equal to), `<=` -(less than or equal to), `==` (equal to), and `!=` (not equal to). +Otros operadores similares son `>=` (mayor o igual que), `<=` (menor o igual que), +`==` (igual a), y `!=` (no igual a). ``` console.log("Itchy" != "Scratchy") // → true -console.log("Apple" == "Orange") +console.log("Manzana" == "Naranja") // → false ``` {{index [comparison, "of NaN"], NaN}} -There is only one value in JavaScript that is not equal to itself, and -that is `NaN` ("not a number"). +Solo hay un valor en JavaScript que no es igual a si mismo, y este es +`NaN` ("no es un número"). ``` console.log(NaN == NaN) // → false ``` -`NaN` is supposed to denote the result of a nonsensical computation, -and as such, it isn't equal to the result of any _other_ nonsensical -computations. +Se supone que `NaN` denota el resultado de una computación sin sentido, +y como tal, no es igual al resultado de ninguna _otra_ computación sin sentido. -### Logical operators +### Operadores lógicos {{index reasoning, "logical operators"}} -There are also some operations that can be applied to Boolean values -themselves. JavaScript supports three logical operators: _and_, _or_, -and _not_. These can be used to "reason" about Booleans. +También existen algunas operaciones que pueden ser aplicadas a valores +Booleanos. JavaScript soporta tres operadores lógicos: _and_, _or_, y _not_. +Estos pueden ser usados para "razonar" acerca de valores Booleanos. {{index "&& operator", "logical and"}} -The `&&` operator represents logical _and_. It is a binary operator, -and its result is true only if both the values given to it are true. +El operador `&&` representa el operador lógico _and_. Es un operador +binario, y su resultado es verdadero solo si ambos de los valores +dados son verdaderos. ``` console.log(true && false) @@ -475,8 +486,8 @@ console.log(true && true) {{index "|| operator", "logical or"}} -The `||` operator denotes logical _or_. It produces true if either of -the values given to it is true. +El operador `||` representa el operador lógico _or_. Lo que produce es +verdadero si cualquiera de los valores dados es verdadero. ``` console.log(false || true) @@ -487,19 +498,20 @@ console.log(false || false) {{index negation, "! operator"}} -_Not_ is written as an exclamation mark (`!`). It is a unary operator -that flips the value given to it—`!true` produces `false`, and `!false` -gives `true`. +_Not_ se escribe como un signo de exclamación (`!`). Es un operador +unario que voltea el valor dado—`!true` produce `false` y `!false` +produce `true`. {{index precedence}} -When mixing these Boolean operators with arithmetic and other -operators, it is not always obvious when parentheses are needed. In -practice, you can usually get by with knowing that of the operators we -have seen so far, `||` has the lowest precedence, then comes `&&`, -then the comparison operators (`>`, `==`, and so on), and then the -rest. This order has been chosen such that, in typical expressions -like the following one, as few parentheses as possible are necessary: +Cuando estos operadores Booleanos son mezclados con aritmética y con otros +operadores, no siempre es obvio cuando son necesarios los paréntesis. +En la práctica, usualmente puedes manejarte bien sabiendo que de +los operadores que hemos visto hasta ahora, `||` tiene la menor precedencia, +luego le sigue `&&`, luego le siguen los operadores de comparación +(`>`, `==`, y demás), y luego el resto. Este orden ha sido determinado para +que en expresiones como la siguiente, la menor cantidad de paréntesis +posible sea necesaria: ``` 1 + 1 == 2 && 10 * 10 > 50 @@ -507,9 +519,9 @@ like the following one, as few parentheses as possible are necessary: {{index "conditional execution", "ternary operator", "?: operator", "conditional operator", "colon character", "question mark"}} -The last logical operator I will discuss is not unary, not binary, but -_ternary_, operating on three values. It is written with a question -mark and a colon, like this: +El ultimo operador lógico que discutiremos no es unario, tampoco binario, +sino _ternario_, esto es, que opera en tres valores. Es escrito con un +signo de interrogación y dos puntos, de esta forma: ``` console.log(true ? 1 : 2); @@ -518,36 +530,36 @@ console.log(false ? 1 : 2); // → 2 ``` -This one is called the _conditional_ operator (or sometimes just -the _ternary_ operator since it is the only such operator in the -language). The value on the left of the question mark "picks" which of -the other two values will come out. When it is true, it chooses the -middle value, and when it is false, it chooses the value on the right. +Este es llamado el operador _condicional_ (o algunas veces simplemente +operador _ternario_ ya que solo existe uno de este tipo). El valor a la +izquierda del signo de interrogación "decide" cual de los otros dos valores +sera retornado. Cuando es verdadero, elige el valor de en medio, y cuando es +falso, el valor de la derecha. -## Empty values +## Valores vacíos {{index undefined, null}} -There are two special values, written `null` and `undefined`, that are -used to denote the absence of a _meaningful_ value. They are -themselves values, but they carry no information. +Existen dos valores especiales, escritos como `null` y `undefined`, que son +usados para denotar la ausencia de un valor _significativo_. Son en si mismos +valores, pero no traen consigo información. -Many operations in the language that don't produce a meaningful value -(you'll see some later) yield `undefined` simply because they have to -yield _some_ value. +Muchas operaciones en el lenguaje que no producen un valor significativo +(veremos algunas mas adelante), producen `undefined` simplemente porque tienen +que producir _algún_ valor. -The difference in meaning between `undefined` and `null` is an accident -of JavaScript's design, and it doesn't matter most of the time. In cases -where you actually have to concern yourself with these values, I -recommend treating them as mostly interchangeable. +La diferencia en significado entre `undefined` y `null` es un accidente del +diseño de JavaScript, y realmente no importa la mayor parte del tiempo. +En los casos donde realmente tendríamos que preocuparnos por estos valores, +mayormente recomiendo que los trates como intercambiables. -## Automatic type conversion +## Conversión de tipo automática {{index NaN, "type coercion"}} -In the Introduction, I mentioned that JavaScript goes out of its way -to accept almost any program you give it, even programs that do odd -things. This is nicely demonstrated by the following expressions: +En la Introducción, mencione que JavaScript tiende a salirse de su camino para +aceptar casi cualquier programa que le demos, incluso programas que hacen cosas +extrañas. Esto es bien demostrado por las siguientes expresiones: ``` console.log(8 * null) @@ -556,7 +568,7 @@ console.log("5" - 1) // → 4 console.log("5" + 1) // → 51 -console.log("five" * 2) +console.log("cinco" * 2) // → NaN console.log(false == 0) // → true @@ -564,33 +576,33 @@ console.log(false == 0) {{index "+ operator", arithmetic, "* operator", "- operator"}} -When an operator is applied to the "wrong" type of value, JavaScript -will quietly convert that value to the type it needs, using a set of -rules that often aren't what you want or expect. This is called -_((type coercion))_. The `null` in the first expression becomes `0`, -and the `"5"` in the second expression becomes `5` (from string to -number). Yet in the third expression, `+` tries string concatenation -before numeric addition, so the `1` is converted to `"1"` (from number -to string). +Cuando un operador es aplicado al tipo de valor "incorrecto", JavaScript +silenciosamente convertirá ese valor al tipo que necesita, utilizando una +serie de reglas que frecuentemente no dan el resultado que quisieras +o esperarías. Esto es llamado _((coercion de tipo))_. El `null` +en la primera expresión se torna `0`, y el `"5"`en la segunda expresión +se torna `5` (de string a número). Sin embargo, en la tercera expresión, +`+` intenta realizar una concatenación de string antes que una adición +numérica, entonces el `1` es convertido a `"1"` (de número a string) {{index "type coercion", [number, "conversion to"]}} -When something that doesn't map to a number in an obvious way (such as -`"five"` or `undefined`) is converted to a number, you get the value -`NaN`. Further arithmetic operations on `NaN` keep producing `NaN`, so -if you find yourself getting one of those in an unexpected place, look -for accidental type conversions. +Cuando algo que no se traduce a un número en una manera obvia (tal como +`"cinco"` o `undefined`) es convertido a un número, obtenemos el valor +`NaN`. Operaciones aritméticas subsecuentes con `NaN`, continúan +produciendo `NaN`, asi que si te encuentras obteniendo uno de estos +valores en algun lugar inesperado, busca por coerciones de tipo accidentales. {{index null, undefined, [comparison, "of undefined values"], "== operator"}} -When comparing values of the same type using `==`, the outcome is easy -to predict: you should get true when both values are the same, except -in the case of `NaN`. But when the types differ, JavaScript uses a -complicated and confusing set of rules to determine what to do. In -most cases, it just tries to convert one of the values to the other -value's type. However, when `null` or `undefined` occurs on either -side of the operator, it produces true only if both sides are one of -`null` or `undefined`. +Cuando se utiliza `==` para comparar valores del mismo tipo, el desenlace +es fácil de predecir: debemos de obtener verdadero cuando ambos valores +son lo mismo, excepto en el caso de `NaN`. Pero cuando los tipos difieren, +JavaScript utiliza una serie de reglas complicadas y confusas para determinar +que hacer. En la mayoria de los casos, solo tratara de convertir uno de +estos valores al tipo del otro valor. Sin embargo, cuando `null` o `undefined` +ocurren en cualquiera de los lados del operador, este produce verdadero solo si +ambos lados son valores o `null` o `undefined`. ``` console.log(null == undefined); @@ -599,92 +611,93 @@ console.log(null == 0); // → false ``` -That behavior is often useful. When you want to test whether a value -has a real value instead of `null` or `undefined`, you can compare it -to `null` with the `==` (or `!=`) operator. +Este comportamiento es frecuentemente util. Cuando queremos probar +si un valor tiene un valor real en vez de `null` o `undefined`, puedes +compararlo con `null` usando el operador `==` (o `!=`). {{index "type coercion", [Boolean, "conversion to"], "=== operator", "!== operator", comparison}} -But what if you want to test whether something refers to the precise -value `false`? Expressions like `0 == false` and `"" == false` are -also true because of automatic type conversion. When you do _not_ want -any type conversions to happen, there are two additional operators: -`===` and `!==`. The first tests whether a value is _precisely_ equal -to the other, and the second tests whether it is not precisely equal. -So `"" === false` is false as expected. +Pero que pasa si queremos probar que algo se refiere precisamente +al valor `false`? Las reglas para convertir strings y números a valores +Booleanos, dice que `0`, `NaN`, y el string vació (`""`) cuentan como `false`, +mientras que todos los otros valores cuentan como `true`. Debido a esto, +expresiones como `0 == false`, y `"" == false` son también verdaderas. +Cuando no queremos ninguna conversion de tipo automática, existen otros +dos operadores adicionales: `===` y `!==`. El primero prueba si un valor +es _precisamente_ igual al otro, y el segundo prueba si un valor no es +precisamente igual. Entonces `"" === false` es falso, como es de esperarse. -I recommend using the three-character comparison operators defensively to -prevent unexpected type conversions from tripping you up. But when you're -certain the types on both sides will be the same, there is no problem with -using the shorter operators. +Recomiendo usar el operador de comparación de tres caracteres de una manera +defensiva para prevenir que conversiones de tipo inesperadas te estorben. +Pero cuando estés seguro de que el tipo va a ser el mismo en ambos lados, no es +problemático utilizar los operadores mas cortos. -### Short-circuiting of logical operators +### Corto circuito de operadores lógicos {{index "type coercion", [Boolean, "conversion to"], operator}} -The logical operators `&&` and `||` handle values of different types -in a peculiar way. They will convert the value on their left side to -Boolean type in order to decide what to do, but depending on the -operator and the result of that conversion, they will return either the -_original_ left-hand value or the right-hand value. +Los operadores lógicos `&&` y `||`, manejan valores de diferentes tipos +de una forma peculiar. Ellos convertirán el valor en su lado izquierdo a un +tipo Booleano para decidir que hacer, pero dependiendo del operador y el +resultado de la conversión, devolverán o el valor _original_ de la izquierda +o el valor de la derecha. {{index "|| operator"}} -The `||` operator, for example, will return the value to its left when -that can be converted to true and will return the value on its right -otherwise. This has the expected effect when the values are Boolean -and does something analogous for values of other types. +El operador `||`, por ejemplo, devolverá el valor de su izquierda cuando +este puede ser convertido a verdadero y de ser lo contrario devolverá +el valor de la derecha. Esto tiene el efecto esperado cuando los valores +son Booleanos, pero se comporta de una forma algo análoga con valores +de otros tipos. ``` -console.log(null || "user") -// → user -console.log("Agnes" || "user") +console.log(null || "usuario") +// → usuario +console.log("Agnes" || "usuario") // → Agnes ``` {{index "default value"}} -We can use this functionality as a way to fall back on a default -value. If you have a value that might be empty, you can put `||` after -it with a replacement value. If the initial value can be converted to -false, you'll get the replacement instead. The rules for converting -strings and numbers to Boolean values state that `0`, `NaN`, and the -empty string (`""`) count as `false`, while all the other values count -as `true`. So `0 || -1` produces `-1`, and `"" || "!?"` yields `"!?"`. +Podemos utilizar esta funcionalidad como una forma de recurrir a +un valor por defecto. Si tenemos un valor que puede estar vacío, +podemos usar `||` después de este para remplazarlo con otro valor. +Si el valor inicial puede ser convertido a falso, obtendra el +reemplazo en su lugar. {{index "&& operator"}} -The `&&` operator works similarly but the other way around. When the -value to its left is something that converts to false, it returns that -value, and otherwise it returns the value on its right. +El operador `&&` funciona de manera similar, pero de forma opuesta. +Cuando el valor a su izquierda es algo que se convierte a falso, devuelve +ese valor, y de lo contrario, devuelve el valor a su derecha. -Another important property of these two operators is that the part to -their right is evaluated only when necessary. In the case of `true || -X`, no matter what `X` is—even if it's a piece of program that does -something _terrible_—the result will be true, and `X` is never -evaluated. The same goes for `false && X`, which is false and will -ignore `X`. This is called _((short-circuit evaluation))_. +Otra propiedad importante de estos dos operadores es que la parte +de su derecha solo es evaluada si es necesario. En el caso de +de `true || X`, no importa que sea `X`—aun si es una pieza del programa +que hace algo _terrible_—el resultado será verdadero, y `X` nunca +sera evaluado. Lo mismo sucede con `false && X`, que es falso e ignorará +`X`. Esto es llamado _((evaluación de corto circuito))_. {{index "ternary operator", "?: operator", "conditional operator"}} -The conditional operator works in a similar way. Of the second and -third values, only the one that is selected is evaluated. +El operador condicional funciona de manera similar. Del segundo y +tercer valor, solo el que es seleccionado es evaluado. -## Summary +## Resumen -We looked at four types of JavaScript values in this chapter: numbers, -strings, Booleans, and undefined values. +Observamos cuatro tipos de valores de JavaScript en este capítulo: números, +textos (`strings`), Booleanos, y valores indefinidos. -Such values are created by typing in their name (`true`, `null`) or -value (`13`, `"abc"`). You can combine and transform values with -operators. We saw binary operators for arithmetic (`+`, `-`, `*`, `/`, -and `%`), string concatenation (`+`), comparison (`==`, `!=`, `===`, -`!==`, `<`, `>`, `<=`, `>=`), and logic (`&&`, `||`), as well as -several unary operators (`-` to negate a number, `!` to negate -logically, and `typeof` to find a value's type) and a ternary operator -(`?:`) to pick one of two values based on a third value. +Tales valores son creados escribiendo su nombre (`true`, `null`) +o valor (`13`, `"abc"`). Puedes combinar y transformar valores con +operadores. Vimos operadores binarios para aritmética (`+`, `-`, `*`, `/`, +y `%`), concatenación de strings (`+`), comparaciones (`==`, `!=`, `===`, +`!==`, `<`, `>`, `<=`, `>=`), y lógica (`&&`, `||`), así también como +varios otros operadores unarios (`-` para negar un número, `!` para negar +lógicamente, y `typeof` para saber el valor de un tipo) y un operador +ternario (`?:`) para elegir uno de dos valores basándose en un tercer valor. -This gives you enough information to use JavaScript as a pocket -calculator but not much more. The [next -chapter](program_structure) will start tying -these expressions together into basic programs. +Esto te dá la información suficiente para usar JavaScript como una +calculadora de bolsillo, pero no para mucho más. El [próximo +capitulo](estructura_de_programa) empezará a juntar estas expresiones +para formar programas básicos. diff --git a/02_program_structure.md b/02_program_structure.md index a2ee7a377..bf5c12156 100644 --- a/02_program_structure.md +++ b/02_program_structure.md @@ -1,212 +1,220 @@ -# Program Structure +# Estructura de Programa {{quote {author: "_why", title: "Why's (Poignant) Guide to Ruby", chapter: true} -And my heart glows bright red under my filmy, translucent skin and -they have to administer 10cc of JavaScript to get me to come back. (I -respond well to toxins in the blood.) Man, that stuff will kick the -peaches right out your gills! +Y mi corazón brilla de un color rojo brillante bajo mi piel transparente y +translúcida, y tienen que administrarme 10cc de JavaScript para conseguir que +regrese. (respondo bien a las toxinas en la sangre.) ¡Hombre, esa cosa es +increíble! quote}} {{index why, "Poignant Guide"}} -{{figure {url: "img/chapter_picture_2.jpg", alt: "Picture of tentacles holding objects", chapter: framed}}} +{{figure {url: "img/chapter_picture_2.jpg", alt: "Foto de tentáculos sosteniendo objetos", chapter: framed}}} -In this chapter, we will start to do things that can actually be called -_programming_. We will expand our command of the JavaScript language -beyond the nouns and sentence fragments we've seen so far, to the -point where we can express meaningful prose. +En este capítulo, comenzaremos a hacer cosas que realmente se pueden llamar +_programación_. Expandiremos nuestro dominio del lenguaje JavaScript +más allá de los sustantivos y fragmentos de oraciones que hemos visto hasta +ahora, al punto donde podemos expresar prosa significativa. -## Expressions and statements +## Expresiones y declaraciones -{{index grammar, [syntax, expression], [code, "structure of"], grammar, [JavaScript, syntax]}} +{{index grammar, syntax, [code, "structure of"], grammar, [JavaScript, syntax]}} -In [Chapter ?](values), we made values and applied operators to them -to get new values. Creating values like this is the main substance of -any JavaScript program. But that substance has to be framed in a -larger structure to be useful. So that's what we'll cover next. +En el [Capítulo 1](valores), creamos valores y les aplicamos operadores a ellos +para obtener nuevos valores. Crear valores de esta manera es la sustancia +principal de cualquier programa en JavaScript. Pero esa sustancia tiene que +enmarcarse en una estructura más grande para poder ser útil. Así que eso es +lo que veremos a continuación. -{{index "literal expression", [parentheses, expression]}} +{{index "literal expression"}} -A fragment of code that produces a value is called an -_((expression))_. Every value that is written literally (such as `22` -or `"psychoanalysis"`) is an expression. An expression between -parentheses is also an expression, as is a ((binary operator)) -applied to two expressions or a ((unary operator)) applied to one. +Un fragmento de código que produce un valor se llama una +_((expresión))_. Cada valor que se escribe literalmente (como `22` +o `"psicoanálisis"`) es una expresión. Una expresión entre +((paréntesis)) también es una expresión, como lo es un ((operador binario)) +aplicado a dos expresiones o un ((operador unario)) aplicado a una sola. {{index [nesting, "of expressions"], "human language"}} -This shows part of the beauty of a language-based interface. -Expressions can contain other expressions in a way similar to how subsentences in human languages are nested—a subsentence can -contain its own subsentences, and so on. This allows us to build -expressions that describe arbitrarily complex computations. +Esto demuestra parte de la belleza de una interfaz basada en un lenguaje. +Las expresiones pueden contener otras expresiones de una manera muy similar +a como las sub-oraciones en los lenguajes humanos están anidadas, una +sub-oración puede contener sus propias sub-oraciones, y así sucesivamente. +Esto nos permite construir expresiones que describen cálculos arbitrariamente +complejos. {{index statement, semicolon, program}} -If an expression corresponds to a sentence fragment, a JavaScript -_statement_ corresponds to a full sentence. A program is a list of -statements. +Si una expresión corresponde al fragmento de una oración, una _declaración_ +en JavaScript corresponde a una oración completa. Un programa es una lista de +declaraciones. -{{index [syntax, statement]}} +{{index syntax}} -The simplest kind of statement is an expression with a semicolon after -it. This is a program: +El tipo más simple de declaración es una expresión con un punto y coma después +ella. Esto es un programa: ``` 1; !false; ``` -It is a useless program, though. An ((expression)) can be content to -just produce a value, which can then be used by the enclosing code. A -((statement)) stands on its own, so it amounts to something only if it -affects the world. It could display something on the screen—that -counts as changing the world—or it could change the internal state of -the machine in a way that will affect the statements that come after -it. These changes are called _((side effect))s_. The statements in the -previous example just produce the values `1` and `true` and then -immediately throw them away. This leaves no impression on the world at -all. When you run this program, nothing observable happens. +Sin embargo, es un programa inútil. Una ((expresión)) puede estar feliz solo con +producir un valor, que luego pueda ser utilizado por el código circundante. Una +((declaración)) es independiente por si misma, por lo que equivale a +algo solo si afecta al mundo. Podría mostrar algo en la pantalla, eso +cuenta como cambiar el mundo, o podría cambiar el estado interno de +la máquina en una manera que afectará a las declaraciones que vengan después de +ella. Estos cambios se llaman _((efecto secundario))s_. Las declaraciones en el +ejemplo anterior solo producen los valores `1` y `true` y luego +inmediatamente los tira a la basura. Esto no deja ninguna huella en el mundo. +Cuando ejecutes este programa, nada observable ocurre. {{index "programming style", "automatic semicolon insertion", semicolon}} -In some cases, JavaScript allows you to omit the semicolon at the end -of a statement. In other cases, it has to be there, or the next -((line)) will be treated as part of the same statement. The rules for -when it can be safely omitted are somewhat complex and error-prone. So -in this book, every statement that needs a semicolon will always get -one. I recommend you do the same, at least until you've learned more -about the subtleties of missing semicolons. +En algunos casos, JavaScript te permite omitir el punto y coma al final +de una declaración. En otros casos, tiene que estar allí, o la próxima +((línea)) serán tratada como parte de la misma declaración. Las reglas para +saber cuando se puede omitir con seguridad son algo complejas y propensas a +errores. Asi que en este libro, cada declaración que necesite un punto y coma +siempre tendra uno. Te recomiendo que hagas lo mismo, al menos hasta que hayas +aprendido más sobre las sutilezas de los puntos y comas que puedan ser +omitidos. -## Bindings +## Vinculaciones {{indexsee variable, binding}} -{{index [syntax, statement], [binding, definition], "side effect", [memory, organization], [state, in binding]}} +{{index syntax, [binding, definition], "side effect", memory}} -How does a program keep an internal state? How does it remember -things? We have seen how to produce new values from old values, but -this does not change the old values, and the new value has to be -immediately used or it will dissipate again. To catch and hold values, -JavaScript provides a thing called a _binding_, or _variable_: +Cómo mantiene un programa un ((estado)) interno? Cómo recuerda +cosas? Hasta ahora hemos visto cómo producir nuevos valores a partir de +valores anteriores, pero esto no cambia los valores anteriores, y el nuevo valor +tiene que ser usado inmediatamente o se disipará nuevamente. Para atrapar y +mantener valores, JavaScript proporciona una cosa llamada _vinculación_, o +_variable_: ``` -let caught = 5 * 5; +let atrapado = 5 * 5; ``` {{index "let keyword"}} -That's a second kind of ((statement)). The special word -(_((keyword))_) `let` indicates that this sentence is going to define -a binding. It is followed by the name of the binding and, if we want -to immediately give it a value, by an `=` operator and an expression. +Ese es un segundo tipo de ((declaración)). La palabra especial +(_((palabra clave))_) `let` indica que esta oración va a definir +una vinculación. Le sigue el nombre de la vinculación y, si queremos +darle un valor inmediatamente, un operador `=` y una expresión. -The previous statement creates a binding called `caught` and uses it -to grab hold of the number that is produced by multiplying 5 by 5. +La declaración anterior crea una vinculación llamada `atrapado` y la usa +para capturar el número que se produce al multiplicar 5 por 5. -After a binding has been defined, its name can be used as an -((expression)). The value of such an expression is the value the -binding currently holds. Here's an example: +Después de que una vinculación haya sido definida, su nombre puede usarse como +una ((expresión)). El valor de tal expresión es el valor que la +vinculación mantiene actualmente. Aquí hay un ejemplo: ``` -let ten = 10; -console.log(ten * ten); +let diez = 10; +console.log(diez * diez); // → 100 ``` {{index "= operator", assignment, [binding, assignment]}} -When a binding points at a value, that does not mean it is tied to -that value forever. The `=` operator can be used at any time on -existing bindings to disconnect them from their current value and have -them point to a new one. +Cuando una vinculación señala a un valor, eso no significa que esté atada a +ese valor para siempre. El operador `=` puede usarse en cualquier momento en +vinculaciones existentes para desconectarlas de su valor actual y hacer que +ellas apuntan a uno nuevo: ``` -let mood = "light"; -console.log(mood); -// → light -mood = "dark"; -console.log(mood); -// → dark +let humor = "ligero"; +console.log(humor); +// → ligero +humor = "oscuro"; +console.log(humor); +// → oscuro ``` {{index [binding, "model of"], "tentacle (analogy)"}} -You should imagine bindings as tentacles, rather than boxes. They do -not _contain_ values; they _grasp_ them—two bindings can refer to the -same value. A program can access only the values that it still has a -reference to. When you need to remember something, you grow a tentacle -to hold on to it or you reattach one of your existing tentacles to it. +Deberías imaginar a las vinculaciones como tentáculos, en lugar de cajas. +Ellas no _contienen_ valores; ellas los _agarran_, dos vinculaciones pueden +referirse al mismo valor. Un programa solo puede acceder a los valores que +todavía pueda referenciar. Cuando necesitas recordar algo, creces un +tentáculo para aferrarte a él o vuelves a conectar uno de tus tentáculos +existentes a ese algo. -Let's look at another example. To remember the number of dollars that -Luigi still owes you, you create a binding. And then when he pays back -$35, you give this binding a new value. +Veamos otro ejemplo. Para recordar la cantidad de dólares que +Luigi aún te debe, creas una vinculación. Y luego, cuando él te +pague de vuelta $35, le das a esta vinculación un nuevo valor: ``` -let luigisDebt = 140; -luigisDebt = luigisDebt - 35; -console.log(luigisDebt); +let deudaLuigi = 140; +deudaLuigi = deudaLuigi - 35; +console.log(deudaLuigi); // → 105 ``` {{index undefined}} -When you define a binding without giving it a value, the tentacle has -nothing to grasp, so it ends in thin air. If you ask for the value of -an empty binding, you'll get the value `undefined`. +Cuando defines una vinculación sin darle un valor, el tentáculo no tiene +nada que agarrar, por lo que termina en solo aire. Si pides el valor de +una vinculación vacía, obtendrás el valor `undefined`. {{index "let keyword"}} -A single `let` statement may define multiple bindings. The -definitions must be separated by commas. +Una sola declaración `let` puede definir múltiples vinculaciones. +Las definiciones deben estar separadas por comas. ``` -let one = 1, two = 2; -console.log(one + two); +let uno = 1, dos = 2; +console.log(uno + dos); // → 3 ``` -The words `var` and `const` can also be used to create bindings, in a -way similar to `let`. +Las palabras `var` y `const` también pueden ser usadas para crear +vinculaciones, en una manera similar a `let`. ``` -var name = "Ayda"; -const greeting = "Hello "; -console.log(greeting + name); -// → Hello Ayda +var nombre = "Ayda"; +const saludo = "Hola "; +console.log(saludo + nombre); +// → Hola Ayda ``` {{index "var keyword"}} -The first, `var` (short for "variable"), is the way bindings were -declared in pre-2015 JavaScript. I'll get back to the precise way it -differs from `let` in the [next chapter](functions). For now, -remember that it mostly does the same thing, but we'll rarely use it -in this book because it has some confusing properties. +La primera, `var` (abreviatura de "variable"), es la forma en la que se +declaraban las vinculaciones en JavaScript previo al 2015. Volveremos a la forma +precisa en que difiere de `let` en el [próximo capítulo](funciones). Por ahora, +recuerda que generalmente hace lo mismo, pero raramente la usaremos +en este libro porque tiene algunas propiedades confusas. {{index "const keyword", naming}} -The word `const` stands for _((constant))_. It defines a constant -binding, which points at the same value for as long as it lives. This -is useful for bindings that give a name to a value so that you can -easily refer to it later. +La palabra `const` representa una _((constante))_. Define una vinculación +constante, que apunta al mismo valor por el tiempo que viva. Esto +es útil para vinculaciones que le dan un nombre a un valor para que +fácilmente puedas consultarlo más adelante. -## Binding names +## Nombres vinculantes {{index "underscore character", "dollar sign", [binding, naming]}} -Binding names can be any word. Digits can be part of binding -names—`catch22` is a valid name, for example—but the name must not -start with a digit. A binding name may include dollar signs (`$`) or -underscores (`_`) but no other punctuation or special characters. +Los nombres de las vinculaciones pueden ser cualquier palabra. Los dígitos pueden +ser parte de los nombres de las vinculaciones pueden, `catch22` es un nombre +válido, por ejemplo, pero el nombre no debe comenzar con un dígito. +El nombre de una vinculación puede incluir signos de dólar (`$`) o +caracteres de subrayado (`_`), pero no otros signos de puntuación o +caracteres especiales. -{{index [syntax, identifier], "implements (reserved word)", "interface (reserved word)", "package (reserved word)", "private (reserved word)", "protected (reserved word)", "public (reserved word)", "static (reserved word)", "void operator", "yield (reserved word)", "enum (reserved word)", "reserved word", [binding, naming]}} +{{index syntax, "implements (reserved word)", "interface (reserved word)", "package (reserved word)", "private (reserved word)", "protected (reserved word)", "public (reserved word)", "static (reserved word)", "void operator", "yield (reserved word)", "enum (reserved word)", "reserved word", [binding, naming]}} -Words with a special meaning, such as `let`, are _((keyword))s_, and -they may not be used as binding names. There are also a number of -words that are "reserved for use" in ((future)) versions of -JavaScript, which also can't be used as binding names. The full list -of keywords and reserved words is rather long. +Las palabras con un significado especial, como `let`, son _((palabras clave))s_, y +no pueden usarse como nombres vinculantes. También hay una cantidad de +palabras que están "reservadas para su uso" en ((futuras)) versiones de +JavaScript, que tampoco pueden ser usadas como nombres vinculantes. +La lista completa de palabras clave y palabras reservadas es bastante larga: ```{lang: "text/plain"} break case catch class const continue debugger default @@ -216,106 +224,106 @@ new package private protected public return static super switch this throw true try typeof var void while with yield ``` -{{index [syntax, error]}} +No te preocupes por memorizarlas. Cuando crear una vinculación produzca +un ((error de sintaxis)) inesperado, observa si estas tratando de definir una +palabra reservada. -Don't worry about memorizing this list. When creating a binding produces -an unexpected syntax error, see whether you're trying to define a -reserved word. +## El entorno -## The environment +{{index "standard environment"}} -{{index "standard environment", [browser, environment]}} +La colección de vinculaciones y sus valores que existen en un momento dado +se llama _((entorno))_. Cuando se inicia un programa, est +entorno no está vacío. Siempre contiene vinculaciones que son parte del +((estándar)) del lenguaje, y la mayoría de las veces, también tiene vinculaciones +que proporcionan formas de interactuar con el sistema circundante. Por +ejemplo, en el ((navegador)), hay funciones para interactuar con el +sitio web actualmente cargado y para leer entradas del ((mouse)) y ((teclado)). -The collection of bindings and their values that exist at a given time -is called the _((environment))_. When a program starts up, this -environment is not empty. It always contains bindings that are part of -the language ((standard)), and most of the time, it also has bindings -that provide ways to interact with the surrounding system. For -example, in a browser, there are functions to interact with the -currently loaded website and to read ((mouse)) and ((keyboard)) input. +## Funciones -## Functions +{{indexsee "application (of functions)", "function application"}} +{{indexsee "invoking (of functions)", "function application"}} +{{indexsee "calling (of functions)", "function application"}} +{{index output, function, [function, application]}} -{{indexsee "application (of functions)", [function, application]}} -{{indexsee "invoking (of functions)", [function, application]}} -{{indexsee "calling (of functions)", [function, application]}} -{{index output, function, [function, application], [browser, environment]}} - -A lot of the values provided in the default environment have the type -_((function))_. A function is a piece of program wrapped in a value. -Such values can be _applied_ in order to run the wrapped program. For -example, in a browser environment, the binding `prompt` holds a -function that shows a little ((dialog box)) asking for user input. It -is used like this: +Muchos de los valores proporcionados por el entorno predeterminado tienen el +tipo _((función))_. Una función es una pieza de programa envuelta en un valor. +Dichos valores pueden ser _aplicados_ para ejecutar el programa envuelto. Por +ejemplo, en un entorno ((navegador)), la vinculación `prompt` sostiene una +función que muestra un pequeño ((cuadro de diálogo)) preguntando por entrada +del usuario. Esta se usa así: ``` -prompt("Enter passcode"); +prompt("Introducir contraseña"); ``` {{figure {url: "img/prompt.png", alt: "A prompt dialog", width: "8cm"}}} -{{index parameter, [function, application], [parentheses, arguments]}} +{{index parameter, [function, application]}} -Executing a function is called _invoking_, _calling_, or _applying_ -it. You can call a function by putting parentheses after an -expression that produces a function value. Usually you'll directly use -the name of the binding that holds the function. The values between -the parentheses are given to the program inside the function. In the -example, the `prompt` function uses the string that we give it as the -text to show in the dialog box. Values given to functions are called -_((argument))s_. Different functions might need a different number or -different types of arguments. +Ejecutar una función también se conoce como _invocarla_, _llamarla_, o +_aplicarla_. Puedes llamar a una función poniendo ((paréntesis)) después de una +expresión que produzca un valor de función. Usualmente usarás directamente +el nombre de la vinculación que contenga la función. Los valores entre +los paréntesis se dan al programa dentro de la función. En el +ejemplo, la función `prompt` usa el string que le damos como el +texto a mostrar en el cuadro de diálogo. Los valores dados a las funciones +se llaman _((argumento))s_. Diferentes funciones pueden necesitar un número +diferente o diferentes tipos de argumentos -The `prompt` function isn't used much in modern web programming, -mostly because you have no control over the way the resulting dialog -looks, but can be helpful in toy programs and experiments. +La función `prompt` no se usa mucho en la programación web moderna, +sobre todo porque no tienes control sobre la forma en como se ve la caja de +diálogo resultante, pero puede ser útil en programas de juguete y experimentos. -## The console.log function +## La función console.log -{{index "JavaScript console", "developer tools", "Node.js", "console.log", output, [browser, environment]}} +{{index "JavaScript console", "developer tools", "Node.js", "console.log", output}} -In the examples, I used `console.log` to output values. Most JavaScript -systems (including all modern web browsers and Node.js) provide a -`console.log` function that writes out its arguments to _some_ text -output device. In browsers, the output lands in the ((JavaScript -console)). This part of the browser interface is hidden by default, -but most browsers open it when you press F12 or, on a Mac, [command]{keyname}-[option]{keyname}-I. -If that does not work, search through the menus for an item named Developer -Tools or similar. +En los ejemplos, utilicé `console.log` para dar salida a los valores. +La mayoría de los sistemas de JavaScript (incluidos todos los ((navegadore))s +web modernos y Node.js) proporcionan una función `console.log` que escribe +sus argumentos en _algun_ dispositivo de salida de texto. En los navegadores, +esta salida aterriza en la ((consola de JavaScript)). Esta parte de la interfaz +del navegador está oculta por defecto, pero la mayoría de los navegadores +la abren cuando presionas F12 o, en Mac, Command-Option-I. Si eso no funciona, +busca en los menús un elemento llamado "herramientas de desarrollador" o +algo similar. {{if interactive -When running the examples (or your own code) on the pages of this -book, `console.log` output will be shown after the example, instead of -in the browser's JavaScript console. +Al ejecutar los ejemplos (o tu propio código) en las páginas de este +libro, la salida de `console.log` se mostrará después del ejemplo, en lugar de +en la consola de JavaScript del navegador. ``` let x = 30; -console.log("the value of x is", x); -// → the value of x is 30 +console.log("el valor de x es", x); +// → el valor de x es 30 ``` if}} -{{index [object, property], [property, access]}} +{{index object}} -Though binding names cannot contain ((period character))s, -`console.log` does have one. This is because `console.log` isn't a -simple binding. It is actually an expression that retrieves the `log` -property from the value held by the `console` binding. We'll -find out exactly what this means in [Chapter ?](data#properties). +Aunque los nombres de las vinculaciones no puedan contener +((carácteres de punto))s, `console.log` tiene uno. Esto es porque `console.log` +no es un vinculación simple. En realidad, es una expresión que obtiene la +((propiedad)) `log` del valor mantenido por la vinculación `console`. +Averiguaremos qué significa esto exactamente en el +[Capítulo 4](datos#propiedades). {{id return_values}} -## Return values +## Valores de retorno {{index [comparison, "of numbers"], "return value", "Math.max function", maximum}} -Showing a dialog box or writing text to the screen is a _((side -effect))_. A lot of functions are useful because of the side effects -they produce. Functions may also produce values, in which case they -don't need to have a side effect to be useful. For example, the -function `Math.max` takes any amount of number arguments and gives -back the greatest. +Mostrar un cuadro de diálogo o escribir texto en la pantalla es un _((efecto +secundario))_. Muchas funciones son útiles debido a los efectos secundarios +que ellas producen. Las funciones también pueden producir valores, en cuyo caso +no necesitan tener un efecto secundario para ser útil. Por ejemplo, la +función `Math.max` toma cualquier cantidad de argumentos numéricos y devuelve +el mayor de ellos. ``` console.log(Math.max(2, 4)); @@ -324,156 +332,158 @@ console.log(Math.max(2, 4)); {{index [function, application], minimum, "Math.min function"}} -When a function produces a value, it is said to _return_ that value. -Anything that produces a value is an ((expression)) in JavaScript, -which means function calls can be used within larger expressions. Here -a call to `Math.min`, which is the opposite of `Math.max`, is used as -part of a plus expression: +Cuando una función produce un valor, se dice que _retorna_ ese valor. +Todo lo que produce un valor es una ((expresión)) en JavaScript, +lo que significa que las llamadas a funciones se pueden usar dentro de +expresiones más grandes. aquí una llamada a `Math.min`, que es lo opuesto +a `Math.max`, se usa como parte de una expresión de adición: ``` console.log(Math.min(2, 4) + 100); // → 102 ``` -The [next chapter](functions) explains how to write your own -functions. +El [próximo capítulo](funciones) explica cómo escribir tus propias +funciones. -## Control flow +## Flujo de control {{index "execution order", program, "control flow"}} -When your program contains more than one ((statement)), the statements -are executed as if they are a story, from top to bottom. This example -program has two statements. The first one asks the user for a number, -and the second, which is executed after the first, shows the -((square)) of that number. +Cuando tu programa contiene más de una ((declaración)), las declaraciones +se ejecutan como si fueran una historia, de arriba a abajo. Este programa de +ejemplo tiene dos declaraciones. La primera le pide al usuario un número, +y la segunda, que se ejecuta después de la primera, muestra el +((cuadrado)) de ese número. ``` -let theNumber = Number(prompt("Pick a number")); -console.log("Your number is the square root of " + - theNumber * theNumber); +let elNumero = Number(prompt("Elige un numero")); +console.log("Tu número es la raiz cuadrada de " + + elNumero * elNumero); ``` {{index [number, "conversion to"], "type coercion", "Number function", "String function", "Boolean function", [Boolean, "conversion to"]}} -The function `Number` converts a value to a number. We need that -conversion because the result of `prompt` is a string value, and we -want a number. There are similar functions called `String` and -`Boolean` that convert values to those types. +La función `Número` convierte un valor a un número. Necesitamos esa +conversión porque el resultado de `prompt` es un valor de string, y nosotros +queremos un numero. Hay funciones similares llamadas `String` y +`Boolean` que convierten valores a esos tipos. -Here is the rather trivial schematic representation of straight-line -control flow: +Aquí está la representación esquemática (bastante trivial) de un flujo de +control en línea recta: {{figure {url: "img/controlflow-straight.svg", alt: "Trivial control flow", width: "4cm"}}} -## Conditional execution +## Ejecución condicional -{{index Boolean, ["control flow", conditional]}} +{{index Boolean, "control flow"}} -Not all programs are straight roads. We may, for example, want to -create a branching road, where the program takes the proper branch -based on the situation at hand. This is called _((conditional -execution))_. +No todos los programas son caminos rectos. Podemos, por ejemplo, querer +crear un camino de ramificación, donde el programa toma la rama adecuada +basadandose en la situación en cuestión. Esto se llama _((ejecución +condicional))_. {{figure {url: "img/controlflow-if.svg", alt: "Conditional control flow",width: "4cm"}}} -{{index [syntax, statement], "Number function", "if keyword"}} +{{index syntax, "Number function", "if keyword"}} -Conditional execution is created with the `if` keyword in JavaScript. -In the simple case, we want some code to be executed if, and only if, -a certain condition holds. We might, for example, want to show the -square of the input only if the input is actually a number. +La ejecución condicional se crea con la palabra clave `if` en JavaScript. +En el caso simple, queremos que se ejecute algún código si, y solo si, +una cierta condición se cumple. Podríamos, por ejemplo, solo querer mostrar el +cuadrado de la entrada si la entrada es realmente un número. ```{test: wrap} -let theNumber = Number(prompt("Pick a number")); -if (!Number.isNaN(theNumber)) { - console.log("Your number is the square root of " + - theNumber * theNumber); +let elNumero = Number(prompt("Elige un numero")); +if (!Number.isNaN(elNumero)) { + console.log("Tu número es la raiz cuadrada de " + + elNumero * elNumero); } ``` -With this modification, if you enter "parrot", no output is shown. - -{{index [parentheses, statement]}} +Con esta modificación, si ingresas la palabra "loro", no se mostrara +ninguna salida. -The `if` keyword executes or skips a statement depending on the value -of a Boolean expression. The deciding expression is written after the -keyword, between parentheses, followed by the statement to -execute. +La palabra clave `if` ejecuta u omite una declaración dependiendo del valor +de una expresión booleana. La expresión decisiva se escribe después de la +palabra clave, entre ((paréntesis)), seguida de la declaración a +ejecutar. {{index "Number.isNaN function"}} -The `Number.isNaN` function is a standard JavaScript function that -returns `true` only if the argument it is given is `NaN`. The `Number` -function happens to return `NaN` when you give it a string that -doesn't represent a valid number. Thus, the condition translates to -"unless `theNumber` is not-a-number, do this". +La función `Number.isNaN` es una función estándar de JavaScript que +retorna `true` solo si el argumento que se le da es `NaN`. Resulta que +la función `Number` devuelve `NaN` cuando le pasas un string que +no representa un número válido. Por lo tanto, la condición se traduce a +"a menos que `elNumero` no sea un número, haz esto". -{{index grouping, "{} (block)", [braces, "block"]}} +{{index grouping, "{} (block)"}} -The statement after the `if` is wrapped in braces (`{` and -`}`) in this example. The braces can be used to group any number of -statements into a single statement, called a _((block))_. You could -also have omitted them in this case, since they hold only a single -statement, but to avoid having to think about whether they are needed, most JavaScript programmers use them in every wrapped -statement like this. We'll mostly follow that convention in this book, -except for the occasional one-liner. +La declaración debajo del `if` está envuelta en ((llaves)) (`{`y +`}`) en este ejemplo. Estos pueden usarse para agrupar cualquier cantidad de +declaraciones en una sola declaración, llamada un _((bloque))_. Podrías +también haberlas omitido en este caso, ya que solo tienes una sola +declaración, pero para evitar tener que pensar si se necesitan +o no, la mayoría de los programadores en JavaScript las usan en cada una +de sus declaraciones envueltas como esta. Seguiremos esta convención +en la mayoria de este libro, a excepción de la ocasional declaración +de una sola linea. ``` -if (1 + 1 == 2) console.log("It's true"); -// → It's true +if (1 + 1 == 2) console.log("Es verdad"); +// → Es verdad ``` {{index "else keyword"}} -You often won't just have code that executes when a condition holds -true, but also code that handles the other case. This alternate path -is represented by the second arrow in the diagram. You can use the `else` keyword, together with `if`, to create two separate, alternative -execution paths. +A menudo no solo tendrás código que se ejecuta cuando una condición es +verdadera, pero también código que maneja el otro caso. Esta ruta alternativa +está representado por la segunda flecha en el diagrama. La palabra clave `else` +se puede usar, junto con `if`, para crear dos caminos de ejecución +alternativos, de una manera separada. ```{test: wrap} -let theNumber = Number(prompt("Pick a number")); -if (!Number.isNaN(theNumber)) { - console.log("Your number is the square root of " + - theNumber * theNumber); +let elNumero = Number(prompt("Elige un numero")); +if (!Number.isNaN(elNumero)) { + console.log("Tu número es la raiz cuadrada de " + + elNumero * elNumero); } else { - console.log("Hey. Why didn't you give me a number?"); + console.log("Ey. Por qué no me diste un número?"); } ``` {{index ["if keyword", chaining]}} -If you have more than two paths to choose from, you can "chain" multiple `if`/`else` -pairs together. Here's an example: +Si tenemos más de dos rutas a elegir, múltiples pares de `if`/`else` +se pueden "encadenar". Aquí hay un ejemplo: ``` -let num = Number(prompt("Pick a number")); +let numero = Number(prompt("Elige un numero")); -if (num < 10) { - console.log("Small"); -} else if (num < 100) { - console.log("Medium"); +if (numero < 10) { + console.log("Pequeño"); +} else if (numero < 100) { + console.log("Mediano"); } else { - console.log("Large"); + console.log("Grande"); } ``` -The program will first check whether `num` is less than 10. If it is, -it chooses that branch, shows `"Small"`, and is done. If it isn't, it -takes the `else` branch, which itself contains a second `if`. If the -second condition (`< 100`) holds, that means the number is between 10 -and 100, and `"Medium"` is shown. If it doesn't, the second and last -`else` branch is chosen. +El programa primero comprobará si `numero` es menor que 10. Si lo es, +eligira esa rama, mostrara `"Pequeño"`, y está listo. Si no es así, +toma la rama `else`, que a su vez contiene un segundo `if`. Si +la segunda condición (`< 100`) es verdadera, eso significa que el número está +entre 10 y 100, y `"Mediano"` se muestra. Si no es así, la segunda y última +la rama `else` es elegida. -The schema for this program looks something like this: +El esquema de este programa se ve así: {{figure {url: "img/controlflow-nested-if.svg", alt: "Nested if control flow", width: "4cm"}}} {{id loops}} -## while and do loops +## Ciclos while y do -Consider a program that outputs all ((even number))s from 0 to 12. One -way to write this is as follows: +Considera un programa que muestra todos los ((números pare))s de 0 a 12. Una +forma de escribir esto es la siguiente: ``` console.log(0); @@ -485,200 +495,198 @@ console.log(10); console.log(12); ``` -{{index ["control flow", loop]}} +{{index "control flow"}} -That works, but the idea of writing a program is to make something -_less_ work, not more. If we needed all even numbers less than 1,000, -this approach would be unworkable. What we need is a way to run a -piece of code multiple times. This form of control flow is called a -_((loop))_. +Eso funciona, pero la idea de escribir un programa es hacer de algo +_menos_ trabajo, no más. Si necesitáramos todos los números pares menores a +1.000, este enfoque sería poco práctico. Lo que necesitamos es una forma de +ejecutar una pieza de código multiples veces. Esta forma de flujo de control +es llamada un _((ciclo))_ (o "loop"): {{figure {url: "img/controlflow-loop.svg", alt: "Loop control flow",width: "4cm"}}} -{{index [syntax, statement], "counter variable"}} +{{index syntax, "counter variable"}} -Looping control flow allows us to go back to some point in the program -where we were before and repeat it with our current program state. If -we combine this with a binding that counts, we can do something like -this: +El flujo de control de ciclos nos permite regresar a algún punto del programa +en donde estábamos antes y repetirlo con nuestro estado del programa actual. Si +combinamos esto con una vinculación que cuenta, podemos hacer algo como +esta: ``` -let number = 0; -while (number <= 12) { - console.log(number); - number = number + 2; +let numero = 0; +while (numero <= 12) { + console.log(numero); + numero = numero + 2; } // → 0 // → 2 // … etcetera ``` -{{index "while loop", Boolean, [parentheses, statement]}} +{{index "while loop", Boolean}} -A ((statement)) starting with the keyword `while` creates a loop. The -word `while` is followed by an ((expression)) in parentheses and -then a statement, much like `if`. The loop keeps entering that -statement as long as the expression produces a value that gives `true` -when converted to Boolean. +Una ((declaración)) que comienza con la palabra clave `while` crea un ciclo. +La palabra `while` es seguida por una ((expresión)) en ((paréntesis)) y +luego por una declaración, muy similar a `if`. El bucle sigue ingresando a +esta declaración siempre que la expresión produzca un valor que dé `true` +cuando sea convertida a Boolean. -{{index [state, in binding], [binding, as state]}} +{{index comparison, state}} -The `number` binding demonstrates the way a ((binding)) can track the -progress of a program. Every time the loop repeats, `number` gets a -value that is 2 more than its previous value. At the beginning of -every repetition, it is compared with the number 12 to decide whether -the program's work is finished. +La vinculación `numero` demuestra la forma en que una ((vinculación) +puede seguir el progreso de un programa. Cada vez que el ciclo se repite, +`numero` obtiene un valor que es 2 más que su valor anterior. Al comienzo de +cada repetición, se compara con el número 12 para decidir si +el trabajo del programa está terminado. {{index exponentiation}} -As an example that actually does something useful, we can now write a -program that calculates and shows the value of 2^10^ (2 to the 10th -power). We use two bindings: one to keep track of our result and one -to count how often we have multiplied this result by 2. The loop tests -whether the second binding has reached 10 yet and, if not, updates -both bindings. +Como un ejemplo que realmente hace algo útil, ahora podemos escribir un +programa que calcula y muestra el valor de 2^10^ (2 a la 10). +Usamos dos vinculaciones: una para realizar un seguimiento de nuestro resultado +y una para contar cuántas veces hemos multiplicado este resultado por 2. +El ciclo prueba si la segunda vinculación ha llegado a 10 todavía y, si no, +actualiza ambas vinculaciones. ``` -let result = 1; -let counter = 0; -while (counter < 10) { - result = result * 2; - counter = counter + 1; +let resultado = 1; +let contador = 0; +while (contador < 10) { + resultado = resultado * 2; + contador = contador + 1; } -console.log(result); +console.log(resultado); // → 1024 ``` -The counter could also have started at `1` and checked for `<= 10`, -but for reasons that will become apparent in [Chapter -?](data#array_indexing), it is a good idea to get used to -counting from 0. +El contador también podría haber comenzado en `1` y chequear para `<= 10`, +pero, por razones que serán evidentes en el [Capítulo 4](datos#array_indexing), +es una buena idea ir acostumbrandose a contar desde 0. -{{index "loop body", "do loop", ["control flow", loop]}} +{{index "loop body", "do loop", "control flow"}} -A `do` loop is a control structure similar to a `while` loop. It -differs only on one point: a `do` loop always executes its body at -least once, and it starts testing whether it should stop only after -that first execution. To reflect this, the test appears after the body -of the loop. +Un ciclo `do` es una estructura de control similar a un ciclo `while`. +Difiere solo en un punto: un ciclo `do` siempre ejecuta su cuerpo +al menos una vez, y comienza a chequear si debe detenerse solo después de +esa primera ejecución. Para reflejar esto, la prueba aparece después del cuerpo +del ciclo: ``` -let yourName; +let tuNombre; do { - yourName = prompt("Who are you?"); -} while (!yourName); -console.log(yourName); + tuNombre = prompt("Quien eres?"); +} while (!tuNombre); +console.log(tuNombre); ``` {{index [Boolean, "conversion to"], "! operator"}} -This program will force you to enter a name. It will ask again and -again until it gets something that is not an empty string. Applying -the `!` operator will convert a value to Boolean type before negating -it, and all strings except `""` convert to `true`. This means the loop -continues going round until you provide a non-empty name. +Este programa te obligará a ingresar un nombre. Preguntará de nuevo y +de nuevo hasta que obtenga algo que no sea un string vacío. Aplicar +el operador `!` convertirá un valor a tipo Booleano antes de negarlo +y todos los strings, excepto `""` seran convertidas a `true`. Esto significa que +el ciclo continúa dando vueltas hasta que proporciones un nombre no-vacío. -## Indenting Code +## Indentando Código -{{index [code, "structure of"], [whitespace, indentation], "programming style"}} +{{index "code structure", whitespace, "programming style"}} -In the examples, I've been adding spaces in front of statements that -are part of some larger statement. These spaces are not required—the computer -will accept the program just fine without them. In fact, even the -((line)) breaks in programs are optional. You could write a program as -a single long line if you felt like it. +En los ejemplos, he estado agregando espacios adelante de declaraciones que +son parte de una declaración más grande. Estos no son necesarios, la computadora +aceptará el programa normalmente sin ellos. De hecho, incluso las nuevas +((líneas)) en los programas son opcionales. Podrías escribir un +programa en una sola línea inmensa si asi quisieras. -The role of this ((indentation)) inside ((block))s is to make the -structure of the code stand out. In code where new blocks are opened -inside other blocks, it can become hard to see where one block ends -and another begins. With proper indentation, the visual shape of a -program corresponds to the shape of the blocks inside it. I like to -use two spaces for every open block, but tastes differ—some people use -four spaces, and some people use ((tab character))s. The important -thing is that each new block adds the same amount of space. +El rol de esta ((indentación)) dentro de los ((bloques)) es hacer que +la estructura del código se destaque. En código donde se abren nuevos bloques +dentro de otros bloques, puede ser difícil ver dónde termina un bloque +y donde comienza el otro. Con la indentación apropiada, la forma visual de un +programa corresponde a la forma de los bloques dentro de él. Me gusta +usar dos espacios para cada bloque abierto, pero los gustos varían, algunas +personas usan cuatro espacios, y algunas personas usan +((carácteres de tabulación)). Lo cosa importante es que cada bloque +nuevo agregue la misma cantidad de espacio. ``` if (false != true) { - console.log("That makes sense."); + console.log("Esto tiene sentido."); if (1 < 2) { - console.log("No surprise there."); + console.log("Ninguna sorpresa alli."); } } ``` -Most code ((editor)) programs[ (including the one in this book)]{if -interactive} will help by automatically indenting new lines the proper -amount. +La mayoría de los ((editores)) de código [(incluido el de este libro)]{if +interactive} ayudaran indentar automáticamente las nuevas líneas con la +cantidad adecuada. -## for loops +## Ciclos for -{{index [syntax, statement], "while loop", "counter variable"}} +{{index syntax, "while loop", "counter variable"}} -Many loops follow the pattern shown in the `while` examples. First a -"counter" binding is created to track the progress of the loop. Then -comes a `while` loop, usually with a test expression that checks whether the -counter has reached its end value. At the end of the loop body, the -counter is updated to track progress. +Muchos ciclos siguen el patrón visto en los ejemplos de `while`. Primero una +vinculación "contador" se crea para seguir el progreso del ciclo. Entonces +viene un ciclo `while`, generalmente con una expresión de prueba que verifica si +el contador ha alcanzado su valor final. Al final del cuerpo del ciclo, el +el contador se actualiza para mantener un seguimiento del progreso. {{index "for loop", loop}} -Because this pattern is so common, JavaScript and similar languages -provide a slightly shorter and more comprehensive form, the `for` -loop. +Debido a que este patrón es muy común, JavaScript y otros lenguajes similares +proporcionan una forma un poco más corta y más completa, el ciclo `for`: ``` -for (let number = 0; number <= 12; number = number + 2) { - console.log(number); +for (let numero = 0; numero <= 12; numero = numero + 2) { + console.log(numero); } // → 0 // → 2 // … etcetera ``` -{{index ["control flow", loop], state}} - -This program is exactly equivalent to the -[earlier](program_structure#loops) even-number-printing example. The -only change is that all the ((statement))s that are related to the -"state" of the loop are grouped together after `for`. +{{index "control flow", state}} -{{index [binding, as state], [parentheses, statement]}} +Este programa es exactamente equivalente al ejemplo +[anterior](estructura_de_programa#ciclos) de impresión de números pares. +El único cambio es que todos las ((declaraciónes)) que están relacionadas +con el "estado" del ciclo estan agrupadas después del `for`. -The parentheses after a `for` keyword must contain two -((semicolon))s. The part before the first semicolon _initializes_ the -loop, usually by defining a binding. The second part is the -((expression)) that _checks_ whether the loop must continue. The final -part _updates_ the state of the loop after every iteration. In most -cases, this is shorter and clearer than a `while` construct. +Los ((paréntesis)) después de una palabra clave `for` deben contener dos +((punto y coma))s. La parte antes del primer punto y coma _inicializa_ el +cicloe, generalmente definiendo una ((vinculación)). La segunda parte es la +((expresión)) que _chequea_ si el ciclo debe continuar. La parte final +_actualiza_ el estado del ciclo después de cada iteración. En la mayoría de +los casos, esto es más corto y conciso que un constructo `while`. {{index exponentiation}} -This is the code that computes 2^10^ using `for` instead of `while`: +Este es el código que calcula 2^10^, usando `for` en lugar de `while`: ```{test: wrap} -let result = 1; -for (let counter = 0; counter < 10; counter = counter + 1) { - result = result * 2; +let resultado = 1; +for (let contador = 0; contador < 10; contador = contador + 1) { + resultado = resultado * 2; } -console.log(result); +console.log(resultado); // → 1024 ``` -## Breaking Out of a Loop +## Rompiendo un ciclo {{index [loop, "termination of"], "break keyword"}} -Having the looping condition produce `false` is not the only way a -loop can finish. There is a special statement called `break` that has -the effect of immediately jumping out of the enclosing loop. +Hacer que la condición del ciclo produzca `false` no es la única forma en que +el ciclo puede terminar. Hay una declaración especial llamada `break` +("romper") que tiene el efecto de inmediatamente saltar afuera del ciclo +circundante. -This program illustrates the `break` statement. It finds the first number -that is both greater than or equal to 20 and divisible by 7. +Este programa ilustra la declaración `break`. Encuentra el primer número +que es a la vez mayor o igual a 20 y divisible por 7. ``` -for (let current = 20; ; current = current + 1) { - if (current % 7 == 0) { - console.log(current); +for (let actual = 20; ; actual = actual + 1) { + if (actual % 7 == 0) { + console.log(actual); break; } } @@ -687,244 +695,248 @@ for (let current = 20; ; current = current + 1) { {{index "remainder operator", "% operator"}} -Using the remainder (`%`) operator is an easy way to test whether a -number is divisible by another number. If it is, the remainder of -their division is zero. +Usar el operador restante (`%`) es una manera fácil de probar si +un número es divisible por otro número. Si lo es, el residuo de +su división es cero. {{index "for loop"}} -The `for` construct in the example does not have a part that checks -for the end of the loop. This means that the loop will never stop -unless the `break` statement inside is executed. +El constructo `for` en el ejemplo no tiene una parte que verifique +cuando finalizar el ciclo. Esto significa que el ciclo nunca se detendrá +a menos que se ejecute la declaración `break` dentro de el. -If you were to remove that `break` statement or you accidentally write -an end condition that always produces `true`, your program would get -stuck in an _((infinite loop))_. A program stuck in an infinite loop -will never finish running, which is usually a bad thing. +Si eliminases esa declaración `break` o escribieras accidentalmente +una condición final que siempre produciera `true`, tu programa estaria +atrapado en un _((ciclo infinito))_. Un programa atrapado en un ciclo infinito +nunca terminará de ejecutarse, lo que generalmente es algo malo. {{if interactive -If you create an infinite loop in one of the examples on these pages, -you'll usually be asked whether you want to stop the script after a -few seconds. If that fails, you will have to close the tab that you're -working in, or on some browsers close your whole browser, to recover. +Si creas un ciclo infinito en alguno de los ejemplos en estas páginas, +generalmente se te preguntará si deseas detener el script después de +algunos segundos. Si eso falla, tendrás que cerrar la pestaña en la que estás +trabajando, o en algunos navegadores, cerrar todo tu navegador, para poder +recuperarse. if}} {{index "continue keyword"}} -The `continue` keyword is similar to `break`, in that it influences -the progress of a loop. When `continue` is encountered in a loop body, -control jumps out of the body and continues with the loop's next -iteration. +La palabra clave `continue` ("continuar") es similar a `break`, en que influye +el progreso de un ciclo. Cuando `continue` se encuentre en el cuerpo de un +ciclo, el control salta afuera del cuerpo y continúa con la siguiente +iteración del ciclo. -## Updating bindings succinctly +## Actualizando vinculaciones de manera sucinta -{{index assignment, "+= operator", "-= operator", "/= operator", "*= operator", [state, in binding], "side effect"}} +{{index assignment, "+= operator", "-= operator", "/= operator", "*= operator*", state, "side effect"}} -Especially when looping, a program often needs to "update" a binding -to hold a value based on that binding's previous value. +Especialmente cuando realices un ciclo, un programa a menudo necesita +"actualizar" una vinculación para mantener un valor basadandose +en el valor anterior de esa vinculación. ```{test: no} -counter = counter + 1; +contador = contador + 1; ``` -JavaScript provides a shortcut for this. +JavaScript provee de un atajo para esto: ```{test: no} -counter += 1; +contador += 1; ``` -Similar shortcuts work for many other operators, such as `result *= 2` -to double `result` or `counter -= 1` to count downward. +Atajos similares funcionan para muchos otros operadores, como `resultado *= 2` +para duplicar `resultado` o `contador -= 1` para contar hacia abajo. -This allows us to shorten our counting example a little more. +Esto nos permite acortar un poco más nuestro ejemplo de conteo. ``` -for (let number = 0; number <= 12; number += 2) { - console.log(number); +for (let numero = 0; numero <= 12; numero += 2) { + console.log(numero); } ``` {{index "++ operator", "-- operator"}} -For `counter += 1` and `counter -= 1`, there are even shorter -equivalents: `counter++` and `counter--`. +Para `contador += 1` y `contador -= 1`, hay incluso equivalentes más cortos: +`contador++` y `contador --`. -## Dispatching on a value with switch +## Despachar en un valor con switch -{{index [syntax, statement], "conditional execution", dispatch, ["if keyword", chaining]}} +{{index syntax, "conditional execution", dispatching, ["if keyword", chaining]}} -It is not uncommon for code to look like this: +No es poco común que el código se vea así: ```{test: no} -if (x == "value1") action1(); -else if (x == "value2") action2(); -else if (x == "value3") action3(); -else defaultAction(); +if (x == "valor1") accion1(); +else if (x == "valor2") accion2(); +else if (x == "valor3") accion3(); +else accionPorDefault(); ``` {{index "colon character", "switch keyword"}} -There is a construct called `switch` that is intended to express such -a "dispatch" in a more direct way. Unfortunately, the syntax -JavaScript uses for this (which it inherited from the C/Java line of -programming languages) is somewhat awkward—a chain of `if` statements -may look better. Here is an example: +Existe un constructo llamado `switch` que está destinada a expresar tales +"despachos" de una manera más directa. Desafortunadamente, la sintaxis que +JavaScript usa para esto (que heredó de la línea lenguajes de programación +C/Java) es algo incómoda, una cadena de declaraciones `if` podria llegar a verse +mejor. Aquí hay un ejemplo: ``` -switch (prompt("What is the weather like?")) { - case "rainy": - console.log("Remember to bring an umbrella."); +switch (prompt("Como esta el clima?")) { + case "lluvioso": + console.log("Recuerda salir con un paraguas."); break; - case "sunny": - console.log("Dress lightly."); - case "cloudy": - console.log("Go outside."); + case "soleado": + console.log("Vistete con poca ropa."); + case "nublado": + console.log("Ve afuera."); break; default: - console.log("Unknown weather type!"); + console.log("Tipo de clima desconocido!"); break; } ``` -{{index fallthrough, "break keyword", "case keyword", "default keyword"}} +{{index fallthrough, comparison, "break keyword", "case keyword", "default keyword"}} -You may put any number of `case` labels inside the block opened by -`switch`. The program will start executing at the label that -corresponds to the value that `switch` was given, or at `default` if -no matching value is found. It will continue executing, even across -other labels, until it reaches a `break` statement. In some cases, -such as the `"sunny"` case in the example, this can be used to share -some code between cases (it recommends going outside for both sunny -and cloudy weather). But be careful—it is easy to forget such a -`break`, which will cause the program to execute code you do not want -executed. +Puedes poner cualquier número de etiquetas de `case` dentro del bloque +abierto por `switch`. El programa comenzará a ejecutarse en la etiqueta que +corresponde al valor que se le dio a `switch`, o en `default` si no se +encuentra ningún valor que coincida. Continuará ejecutándose, incluso a +través de otras etiquetas, hasta que llegue a una declaración `break`. +En algunos casos, como en el caso `"soleado"` del ejemplo, esto se puede usar +para compartir algo de código entre casos (recomienda salir para ambos +climas soleado y nublado). Pero ten cuidado, es fácil olvidarse de +`break`, lo que hará que el programa ejecute código que no quieres que sea +ejecutado. -## Capitalization +## Capitalización -{{index capitalization, [binding, naming], [whitespace, syntax]}} +{{index capitalization, [binding, naming], whitespace}} -Binding names may not contain spaces, yet it is often helpful to use -multiple words to clearly describe what the binding represents. These -are pretty much your choices for writing a binding name with several -words in it: +Los nombres de vinculaciones no pueden contener espacios, sin embargo, +a menudo es útil usar múltiples palabras para describir claramente lo que +representa la vinculación. Estas son más o menos tus opciones para escribir el +nombre de una vinculación con varias palabras en ella: ```{lang: null} -fuzzylittleturtle -fuzzy_little_turtle -FuzzyLittleTurtle -fuzzyLittleTurtle +pequeñatortugaverde +pequeña_tortuga_verde +PequeñaTortugaVerde +pequeñaTortugaVerde ``` {{index "camel case", "programming style", "underscore character"}} -The first style can be hard to read. I rather like the look of the -underscores, though that style is a little painful to type. The -((standard)) JavaScript functions, and most JavaScript programmers, -follow the bottom style—they capitalize every word except the first. -It is not hard to get used to little things like that, and code with -mixed naming styles can be jarring to read, so we follow this -((convention)). +El primer estilo puede ser difícil de leer. Me gusta mucho el aspecto del +estilo con los guiones bajos, aunque ese estilo es algo fastidioso de escribir. +Las funciones ((estándar)) de JavaScript, y la mayoría de los programadores de +JavaScript, siguen el estilo de abajo: capitalizan cada palabra excepto la +primera. No es difícil acostumbrarse a pequeñas cosas así, y programar con +estilos de nombres mixtos pueden ser algo discordante para leer, así que +seguiremos esta ((convención)). {{index "Number function", constructor}} -In a few cases, such as the `Number` function, the first letter of a -binding is also capitalized. This was done to mark this function as a -constructor. What a constructor is will become clear in [Chapter -?](object#constructors). For now, the important thing is not -to be bothered by this apparent lack of ((consistency)). +En algunos casos, como en la función `Number`, la primera letra de +la vinculación también está en mayúscula. Esto se hizo para marcar esta +función como un constructor. Lo que es un constructor quedará claro en el +[Capítulo 6](objeto#constructores). Por ahora, lo importante es no +ser molestado por esta aparente falta de ((consistencia)). -## Comments +## Comentarios {{index readability}} -Often, raw code does not convey all the information you want a program -to convey to human readers, or it conveys it in such a cryptic way -that people might not understand it. At other times, you might just -want to include some related thoughts as part of your program. This is -what _((comment))s_ are for. +A menudo, el código en si mismo no transmite toda la información que deseas +que un programa transmita a los lectores humanos, o lo transmite de una +manera tan críptica que la gente quizás no lo entienda. En otras ocasiones, +podrías simplemente querer incluir algunos pensamientos relacionados como +parte de tu programa. Esto es para lo qué son los _((comentario))s_. {{index "slash character", "line comment"}} -A comment is a piece of text that is part of a program but is -completely ignored by the computer. JavaScript has two ways of writing -comments. To write a single-line comment, you can use two slash -characters (`//`) and then the comment text after it. +Un comentario es una pieza de texto que es parte de un programa pero que es +completamente ignorado por la computadora. JavaScript tiene dos formas de +escribir comentarios. Para escribir un comentario de una sola línea, +puede usar dos caracteres de barras inclinadas (`//`) y luego el texto del +comentario después. ```{test: no} -let accountBalance = calculateBalance(account); -// It's a green hollow where a river sings -accountBalance.adjust(); -// Madly catching white tatters in the grass. -let report = new Report(); -// Where the sun on the proud mountain rings: -addToReport(accountBalance, report); -// It's a little valley, foaming like light in a glass. +let balanceDeCuenta = calcularBalance(cuenta); +// Es un claro del bosque donde canta un río +balanceDeCuenta.ajustar(); +// Cuelgan enloquecidamente de las hierbas harapos de plata +let reporte = new Reporte(); +// Donde el sol de la orgullosa montaña luce: +añadirAReporte(balanceDeCuenta, reporte); +// Un pequeño valle espumoso de luz. ``` {{index "block comment"}} -A `//` comment goes only to the end of the line. A section of text -between `/*` and `*/` will be ignored in its entirety, regardless of -whether it contains line breaks. This is useful for adding blocks of -information about a file or a chunk of program. +Un comentario `//` va solo haste el final de la línea. Una sección de texto +entre `/*` y `*/` se ignorará en su totalidad, independientemente de +si contiene saltos de línea. Esto es útil para agregar bloques de +información sobre un archivo o un pedazo de programa. ``` /* - I first found this number scrawled on the back of an old notebook. - Since then, it has often dropped by, showing up in phone numbers - and the serial numbers of products that I've bought. It obviously - likes me, so I've decided to keep it. + Primero encontré este número garabateado en la parte posterior de + un viejo cuaderno. Desde entonces, a menudo lo he visto, + apareciendo en números de teléfono y en los números de serie de + productos que he comprado. Obviamente me gusta, así que + decidí quedármelo */ -const myNumber = 11213; +const miNumero = 11213; ``` -## Summary +## Resumen -You now know that a program is built out of statements, which -themselves sometimes contain more statements. Statements tend to -contain expressions, which themselves can be built out of smaller -expressions. +Ahora sabes que un programa está construido a partir de declaraciones, las +cuales a veces pueden contener más declaraciones. Las declaraciones tienden a +contener expresiones, que a su vez se pueden construir a partir de +expresiones mas pequeñas. -Putting statements after one another gives you a program that is -executed from top to bottom. You can introduce disturbances in the -flow of control by using conditional (`if`, `else`, and `switch`) and -looping (`while`, `do`, and `for`) statements. +Poner declaraciones una despues de otras te da un programa que es +ejecutado de arriba hacia abajo. Puedes introducir alteraciones en el +flujo de control usando declaraciones condicionales (`if`, `else`, y `switch`) +y ciclos (`while`, `do`, y `for`). -Bindings can be used to file pieces of data under a name, and they are -useful for tracking state in your program. The environment is the set -of bindings that are defined. JavaScript systems always put a number -of useful standard bindings into your environment. +Las vinculaciones se pueden usar para archivar datos bajo un nombre, y son +utiles para el seguimiento de estado en tu programa. El entorno es el conjunto +de vinculaciones que se definen. Los sistemas de JavaScript siempre +incluyen por defecto un número de vinculaciones estándar útiles en tu entorno. -Functions are special values that encapsulate a piece of program. You -can invoke them by writing `functionName(argument1, argument2)`. Such -a function call is an expression and may produce a value. +Las funciones son valores especiales que encapsulan una parte del programa. +Puedes invocarlas escribiendo `nombreDeLaFuncion(argumento1, argumento2)`. Tal +llamada a función es una expresión, y puede producir un valor. -## Exercises +## Ejercicios {{index exercises}} -If you are unsure how to test your solutions to the exercises, refer to the -[Introduction](intro). +Si no estas seguro de cómo probar tus soluciones para los ejercicios, consulta +la [introducción](intro). -Each exercise starts with a problem description. Read this description and try to -solve the exercise. If you run into problems, consider reading the -hints [after the exercise]{if interactive}[at the [end of the -book](hints)]{if book}. Full solutions to the exercises are -not included in this book, but you can find them online at -[_https://eloquentjavascript.net/code_](https://eloquentjavascript.net/code#2). -If you want to learn something from the exercises, I recommend looking -at the solutions only after you've solved the exercise, or at least -after you've attacked it long and hard enough to have a slight -headache. +Cada ejercicio comienza con una descripción del problema. Lee eso y trata de +resolver el ejercicio. Si tienes problemas, considera leer las pistas +[después del ejercicio]{if interactive}[en el +[final del libro](pistas)]{if book}. Las soluciones completas para los +ejercicios no estan incluidas en este libro, pero puedes encontrarlas en línea +en [_eloquentjavascript.net/code_](https://eloquentjavascript.net/code#2). +Si quieres aprender algo de los ejercicios, te recomiendo mirar a las +soluciones solo despues de que hayas resuelto el ejercicio, o al menos despues +de que lo hayas intentando resolver por un largo tiempo y tengas un +ligero dolor de cabeza. -### Looping a triangle +### Ciclo de un triángulo {{index "triangle (exercise)"}} -Write a ((loop)) that makes seven calls to `console.log` to output the -following triangle: +Escriba un ((ciclo)) que haga siete llamadas a `console.log` para generar el +siguiente triángulo: ```{lang: null} # @@ -938,8 +950,8 @@ following triangle: {{index [string, length]}} -It may be useful to know that you can find the length of a string by -writing `.length` after it. +Puede ser útil saber que puedes encontrar la longitud de un string +escribiendo `.length` después de él: ``` let abc = "abc"; @@ -949,11 +961,12 @@ console.log(abc.length); {{if interactive -Most exercises contain a piece of code that you can modify to solve -the exercise. Remember that you can click code blocks to edit them. +La mayoría de los ejercicios contienen una pieza de código que puedes +modificar para resolver el ejercicio. Recuerda que puedes hacer clic en los +bloques de código para editarlos. ``` -// Your code here. +// Tu código aqui. ``` if}} @@ -961,15 +974,15 @@ if}} {{index "triangle (exercise)"}} -You can start with a program that prints out the numbers 1 to 7, which -you can derive by making a few modifications to the [even number -printing example](program_structure#loops) given earlier in the -chapter, where the `for` loop was introduced. +Puedes comenzar con un programa que imprima los números del 1 al 7, al que +puedes derivar haciendo algunas modificaciones al [ejemplo de impresión de +números pares](estructura_de_programa#ciclos) dado anteriormente en el +capítulo, donde se introdujo el ciclo `for`. -Now consider the equivalence between numbers and strings of hash -characters. You can go from 1 to 2 by adding 1 (`+= 1`). You can go -from `"#"` to `"##"` by adding a character (`+= "#"`). Thus, your -solution can closely follow the number-printing program. +Ahora considera la equivalencia entre números y strings de caracteres de +numeral. Puedes ir de 1 a 2 agregando 1 (`+= 1`). Puedes ir +de `"#"` a `"##"` agregando un caracter (`+= "#"`). Por lo tanto, tu +solución puede seguir de cerca el programa de impresión de números. hint}} @@ -977,22 +990,22 @@ hint}} {{index "FizzBuzz (exercise)", loop, "conditional execution"}} -Write a program that uses `console.log` to print all the numbers from -1 to 100, with two exceptions. For numbers divisible by 3, print -`"Fizz"` instead of the number, and for numbers divisible by 5 (and -not 3), print `"Buzz"` instead. +Escribe un programa que use `console.log` para imprimir todos los números de +1 a 100, con dos excepciones. Para números divisibles por 3, imprime +`"Fizz"` en lugar del número, y para los números divisibles por 5 (y +no 3), imprime `"Buzz"` en su lugar. -When you have that working, modify your program to print `"FizzBuzz"` -for numbers that are divisible by both 3 and 5 (and still print -`"Fizz"` or `"Buzz"` for numbers divisible by only one of those). +Cuando tengas eso funcionando, modifica tu programa para imprimir `"FizzBuzz"`, +para números que sean divisibles entre 3 y 5 (y aún imprimir +`"Fizz"` o `"Buzz"` para números divisibles por solo uno de ellos). -(This is actually an ((interview question)) that has been claimed to -weed out a significant percentage of programmer candidates. So if you -solved it, your labor market value just went up.) +(Esta es en realidad una ((pregunta de entrevista)) que se ha dicho +elimina un porcentaje significativo de candidatos a programadores. Así que +si la puedes resolver, tu valor en el mercado laboral acaba de subir). {{if interactive ``` -// Your code here. +// Tu código aquí. ``` if}} @@ -1000,54 +1013,55 @@ if}} {{index "FizzBuzz (exercise)", "remainder operator", "% operator"}} -Going over the numbers is clearly a looping job, and selecting what to -print is a matter of conditional execution. Remember the trick of -using the remainder (`%`) operator for checking whether a number is -divisible by another number (has a remainder of zero). +Ir a traves de los números es claramente el trabajo de un ciclo y seleccionar +qué imprimir es una cuestión de ejecución condicional. Recuerda el truco de +usar el operador restante (`%`) para verificar si un número es +divisible por otro número (tiene un residuo de cero). -In the first version, there are three possible outcomes for every -number, so you'll have to create an `if`/`else if`/`else` chain. +En la primera versión, hay tres resultados posibles para cada +número, por lo que tendrás que crear una cadena `if`/`else if`/`else`. {{index "|| operator", ["if keyword", chaining]}} -The second version of the program has a straightforward solution and a -clever one. The simple solution is to add another conditional "branch" to -precisely test the given condition. For the clever solution, build up a -string containing the word or words to output and print either this -word or the number if there is no word, potentially by making good use -of the `||` operator. +La segunda versión del programa tiene una solución directa y una +inteligente. La manera simple es agregar otra "rama" condicional para +probar precisamente la condición dada. Para el método inteligente, crea un +string que contenga la palabra o palabras a imprimir e imprimir ya sea esta +palabra o el número si no hay una palabra, posiblemente haciendo un buen uso +del operador `||`. hint}} -### Chessboard +### Tablero de ajedrez -{{index "chessboard (exercise)", loop, [nesting, "of loops"], "newline character"}} +{{index "chess board (exercise)", loop, [nesting, "of loops"], "newline character"}} -Write a program that creates a string that represents an 8×8 grid, -using newline characters to separate lines. At each position of the -grid there is either a space or a "#" character. The characters should -form a chessboard. +Escribe un programa que cree un string que represente una cuadrícula de 8 × 8, +usando caracteres de nueva línea para separar las líneas. En cada posición de la +cuadrícula hay un espacio o un carácter "#". Los caracteres deberían de +formar un tablero de ajedrez. -Passing this string to `console.log` should show something like this: +Pasar este string a `console.log` debería mostrar algo como esto: ```{lang: null} # # # # -# # # # +# # # # # # # # -# # # # +# # # # # # # # -# # # # +# # # # # # # # -# # # # +# # # # ``` -When you have a program that generates this pattern, define a -binding `size = 8` and change the program so that it works for -any `size`, outputting a grid of the given width and height. +Cuando tengas un programa que genere este patrón, define una +((vinculación)) `tamaño = 8` y cambia el programa para que funcione +con cualquier `tamaño`, dando como salida una cuadrícula con el alto y ancho +dados. {{if interactive ``` -// Your code here. +// Tu código aquí. ``` if}} @@ -1055,26 +1069,28 @@ if}} {{index "chess board (exercise)"}} -You can build the string by starting with an empty one (`""`) and -repeatedly adding characters. A newline character is written `"\n"`. +El string se puede construir comenzando con un string vacío (`""`) y +repetidamente agregando caracteres. Un carácter de nueva línea se escribe +`"\n"`. -{{index [nesting, "of loops"], [braces, "block"]}} +{{index [nesting, "of loops"]}} -To work with two ((dimensions)), you will need a ((loop)) inside of a -loop. Put braces around the bodies of both loops to make it -easy to see where they start and end. Try to properly indent these -bodies. The order of the loops must follow the order in which we build -up the string (line by line, left to right, top to bottom). So the -outer loop handles the lines, and the inner loop handles the characters -on a line. +Para trabajar con dos ((dimensiones)), necesitarás un ((ciclo)) dentro de un +ciclo. Coloca ((llaves)) alrededor de los cuerpos de ambos ciclos para hacer +fácil de ver dónde comienzan y terminan. Intenta indentar adecuadamente estos +cuerpos. El orden de los ciclos debe seguir el orden en el que construimos +el string (línea por línea, izquierda a derecha, arriba a abajo). Entonces el +ciclo externo maneja las líneas y el ciclo interno maneja los caracteres +en una sola linea. {{index "counter variable", "remainder operator", "% operator"}} -You'll need two bindings to track your progress. To know whether to -put a space or a hash sign at a given position, you could test whether -the sum of the two counters is even (`% 2`). +Necesitará dos vinculaciones para seguir tu progreso. Para saber si debes poner +un espacio o un signo de numeral en una posición determinada, podrías probar si +la suma de los dos contadores es par (`% 2`). -Terminating a line by adding a newline character must happen after the -line has been built up, so do this after the inner loop but inside the outer loop. +Terminar una línea al agregar un carácter de nueva línea debe suceder después +de que la línea ha sido creada, entonces haz esto después del ciclo interno +pero dentro del bucle externo. hint}} diff --git a/03_functions.md b/03_functions.md index 730ec57a3..2dc4fecf9 100644 --- a/03_functions.md +++ b/03_functions.md @@ -1,138 +1,140 @@ -# Functions +# Funciones {{quote {author: "Donald Knuth", chapter: true} -People think that computer science is the art of geniuses but the -actual reality is the opposite, just many people doing things that -build on each other, like a wall of mini stones. +La gente piensa que las ciencias de la computación son el arte de los genios, +pero la verdadera realidad es lo opuesto, estas solo consisten en mucha gente +haciendo cosas que se construyen una sobre la otra, al igual que un muro +hecho de piedras pequeñas. quote}} {{index "Knuth, Donald"}} -{{figure {url: "img/chapter_picture_3.jpg", alt: "Picture of fern leaves with a fractal shape", chapter: framed}}} +{{figure {url: "img/chapter_picture_3.jpg", alt: "Hojas de helecho con forma de fractal", chapter: framed}}} -{{index function, [code, "structure of"]}} +{{index function, "code structure"}} -Functions are the bread and butter of JavaScript programming. The -concept of wrapping a piece of program in a value has many uses. It -gives us a way to structure larger programs, to reduce repetition, to -associate names with subprograms, and to isolate these subprograms -from each other. +Las funciones son el pan y la mantequilla de la programación en JavaScript. +El concepto de envolver una pieza de programa en un valor tiene muchos usos. +Esto nos da una forma de estructurar programas más grandes, de reducir la +repetición, de asociar nombres con subprogramas y de aislar estos subprogramas +unos con otros. -The most obvious application of functions is defining new -((vocabulary)). Creating new words in prose is usually bad style. But -in programming, it is indispensable. +La aplicación más obvia de las funciones es definir nuevo +((vocabulario)). Crear nuevas palabras en la prosa suele ser un mal estilo. +Pero en la programación, es indispensable. {{index abstraction, vocabulary}} -Typical adult English speakers have some 20,000 words in their -vocabulary. Few programming languages come with 20,000 commands built -in. And the vocabulary that _is_ available tends to be more precisely -defined, and thus less flexible, than in human language. Therefore, we -usually _have_ to introduce new concepts to avoid repeating ourselves -too much. +En promedio, un tipico adulto que hable español tiene unas 20,000 palabras en su +vocabulario. Pocos lenguajes de programación vienen con 20,000 comandos +ya incorporados en el. Y el vocabulario que _está_ disponible tiende a ser +más precisamente definido, y por lo tanto menos flexible, que en el lenguaje +humano. Por lo tanto, nosotros por lo general _tenemos_ que introducir nuevos +conceptos para evitar repetirnos demasiado. -## Defining a function +## Definiendo una función -{{index "square example", [function, definition], [binding, definition]}} +{{index "square example", [function, definition]}} -A function definition is a regular binding where the value of the -binding is a function. For example, this code defines `square` to -refer to a function that produces the square of a given number: +Una definición de función es una ((vinculación)) regular donde el valor de +la vinculación es una función. Por ejemplo, este código define `cuadrado` para +referirse a una función que produce el cuadrado de un número dado: ``` -const square = function(x) { +const cuadrado = function(x) { return x * x; }; -console.log(square(12)); +console.log(cuadrado(12)); // → 144 ``` -{{indexsee "curly braces", braces}} -{{index [braces, "function body"], block, [syntax, function], "function keyword", [function, body], [function, "as value"], [parentheses, arguments]}} +{{indexsee braces, "curly braces"}} +{{index "curly braces", block, syntax, "function keyword", [function, body], [function, "as value"]}} -A function is created with an expression that starts with the keyword -`function`. Functions have a set of _((parameter))s_ (in this case, -only `x`) and a _body_, which contains the statements that are to be -executed when the function is called. The function body of a function -created this way must always be wrapped in braces, even when it -consists of only a single ((statement)). +Una función es creada con una expresión que comienza con la palabra clave +`function` ("función"). Las funciones tienen un conjunto de _((parámetro))s_ +(en este caso, solo `x`) y un _cuerpo_, que contiene las declaraciones que +deben ser ejecutadas cuando se llame a la función. El cuerpo de la +función de una función creada de esta manera siempre debe estar envuelto +en llaves, incluso cuando consista en una sola ((declaración)). {{index "power example"}} -A function can have multiple parameters or no parameters at all. In -the following example, `makeNoise` does not list any parameter names, -whereas `power` lists two: +Una función puede tener múltiples parámetros o ningún parámetro en absoluto. En +el siguiente ejemplo, `hacerSonido` no lista ningún nombre de parámetro, +mientras que `potencia` enumera dos: ``` -const makeNoise = function() { +const hacerSonido = function() { console.log("Pling!"); }; -makeNoise(); +hacerSonido(); // → Pling! -const power = function(base, exponent) { - let result = 1; - for (let count = 0; count < exponent; count++) { - result *= base; +const potencia = function(base, exponente) { + let resultado = 1; + for (let cuenta = 0; cuenta < exponente; cuenta++) { + resultado *= base; } - return result; + return resultado; }; -console.log(power(2, 10)); +console.log(potencia(2, 10)); // → 1024 ``` {{index "return value", "return keyword", undefined}} -Some functions produce a value, such as `power` and `square`, and some -don't, such as `makeNoise`, whose only result is a ((side effect)). A -`return` statement determines the value the function returns. When -control comes across such a statement, it immediately jumps out of the -current function and gives the returned value to the code that called -the function. A `return` keyword without an expression after it will -cause the function to return `undefined`. Functions that don't have a -`return` statement at all, such as `makeNoise`, similarly return +Algunas funciones producen un valor, como `potencia` y `cuadrado`, y algunas +no, como `hacerSonido`, cuyo único resultado es un ((efecto secundario)). Una +declaración de `return` determina el valor que es retornado por la función. +Cuando el control se encuentre con tal declaración, inmediatamente salta de la +función actual y devuelve el valor retornado al código que llamó +la función. Una declaración `return` sin una expresión después de ella +hace que la función retorne `undefined`. Funciones que no tienen una +declaración `return` en absoluto, como `hacerSonido`, de manera similar retornan `undefined`. {{index parameter, [function, application], [binding, "from parameter"]}} -Parameters to a function behave like regular bindings, but their -initial values are given by the _caller_ of the function, not the code -in the function itself. +Los parámetros de una función se comportan como vinculaciones regulares, pero +sus valores iniciales están dados por el _llamador_ de la función, no por +el código en la función en sí. -## Bindings and scopes +## Vinculaciones y alcances {{indexsee "top-level scope", "global scope"}} {{index "var keyword", "global scope", [binding, global], [binding, "scope of"]}} -Each binding has a _((scope))_, which is the part of the program -in which the binding is visible. For bindings defined outside of any -function or block, the scope is the whole program—you can refer to -such bindings wherever you want. These are called _global_. +Cada ((vinculación)) tiene un _((alcace))_, que corresponde a la parte del +programa en donde la vinculación es visible. Para vinculaciones definidas +fuera de cualquier función o bloque, el alcance es todo el +programa, puedes referir a estas vinculaciones en donde sea que quieras. +Estas son llamadas _globales_. {{index "local scope", [binding, local]}} -But bindings created for function ((parameter))s or declared inside a -function can be referenced only in that function, so they are known as -_local_ bindings. Every time the function is called, new instances of these -bindings are created. This provides some isolation between -functions—each function call acts in its own little world (its local -environment) and can often be understood without knowing a lot about -what's going on in the global environment. +Pero las vinculaciones creadas como ((parámetro))s de función o declaradas +dentro de una función solo puede ser referenciadas en esa función. +Estas se llaman _locales_. Cada vez que se llame a la función, se crean +nuevas instancias de estas vinculaciones. Esto proporciona cierto +aislamiento entre funciones, cada llamada de función actúa sobre su pequeño +propio mundo (su entorno local), y a menudo puede ser entendida sin saber +mucho acerca de lo qué está pasando en el entorno global. {{index "let keyword", "const keyword", "var keyword"}} -Bindings declared with `let` and `const` are in fact local to the -_((block))_ that they are declared in, so if you create one of those -inside of a loop, the code before and after the loop cannot "see" it. -In pre-2015 JavaScript, only functions created new scopes, so -old-style bindings, created with the `var` keyword, are visible -throughout the whole function that they appear in—or throughout the -global scope, if they are not in a function. +Vinculaciones declaradas con `let` y `const` son, de hecho, locales al +_((bloque))_ donde estén declarados, así que si creas uno de esas +dentro de un ciclo, el código antes y después del ciclo no puede "verlas". +En JavaScript anterior a 2015, solo las funciones creaban nuevos alcances, +por lo que las vinculaciones de estilo-antiguo, creadas con la palabra clave +`var`, son visibles a lo largo de toda la función en la que +aparecen, o en todo el alcance global, si no están dentro de una función. ``` let x = 10; @@ -142,841 +144,848 @@ if (true) { console.log(x + y + z); // → 60 } -// y is not visible here +// y no es visible desde aqui console.log(x + z); // → 40 ``` {{index [binding, visibility]}} -Each ((scope)) can "look out" into the scope around it, so `x` is -visible inside the block in the example. The exception is when -multiple bindings have the same name—in that case, code can see only -the innermost one. For example, when the code inside the `halve` -function refers to `n`, it is seeing its _own_ `n`, not the global -`n`. +Cada ((alcance)) puede "mirar afuera" hacia al alcance que lo rodee, por lo +que `x` es visible dentro del bloque en el ejemplo. La excepción es cuando +vinculaciones múltiples tienen el mismo nombre, en ese caso, el código solo +puede ver a la vinculación más interna. Por ejemplo, cuando el código +dentro de la función `dividirEnDos` se refiera a `numero`, +estara viendo su _propio_ `numero`, no el `numero` en el alcance global. ``` -const halve = function(n) { - return n / 2; +const dividirEnDos = function(numero) { + return numero / 2; }; -let n = 10; -console.log(halve(100)); +let numero = 10; +console.log(dividirEnDos(100)); // → 50 -console.log(n); +console.log(numero); // → 10 ``` {{id scoping}} -### Nested scope +### Alcance anidado {{index [nesting, "of functions"], [nesting, "of scope"], scope, "inner function", "lexical scoping"}} -JavaScript distinguishes not just _global_ and _local_ -bindings. Blocks and functions can be created inside other blocks and -functions, producing multiple degrees of locality. +JavaScript no solo distingue entre vinculaciones _globales_ y _locales_. +Bloques y funciones pueden ser creados dentro de otros bloques y +funciones, produciendo múltiples grados de localidad. {{index "landscape example"}} -For example, this function—which outputs the ingredients needed to -make a batch of hummus—has another function inside it: +Por ejemplo, esta función, que muestra los ingredientes necesarios para +hacer un lote de humus, tiene otra función dentro de ella: ``` -const hummus = function(factor) { - const ingredient = function(amount, unit, name) { - let ingredientAmount = amount * factor; - if (ingredientAmount > 1) { - unit += "s"; +const humus = function(factor) { + const ingrediente = function(cantidad, unidad, nombre) { + let cantidadIngrediente = cantidad * factor; + if (cantidadIngrediente > 1) { + unidad += "s"; } - console.log(`${ingredientAmount} ${unit} ${name}`); + console.log(`${cantidadIngrediente} ${unidad} ${nombre}`); }; - ingredient(1, "can", "chickpeas"); - ingredient(0.25, "cup", "tahini"); - ingredient(0.25, "cup", "lemon juice"); - ingredient(1, "clove", "garlic"); - ingredient(2, "tablespoon", "olive oil"); - ingredient(0.5, "teaspoon", "cumin"); + ingrediente(1, "lata", "garbanzos"); + ingrediente(0.25, "taza", "tahini"); + ingrediente(0.25, "taza", "jugo de limón"); + ingrediente(1, "clavo", "ajo"); + ingrediente(2, "cucharada", "aceite de oliva"); + ingrediente(0.5, "cucharadita", "comino"); }; ``` {{index [function, scope], scope}} -The code inside the `ingredient` function can see the `factor` binding -from the outer function. But its local bindings, such as `unit` or -`ingredientAmount`, are not visible in the outer function. +El código dentro de la función `ingrediente` puede ver la vinculación `factor` +de la función externa. Pero sus vinculaciones locales, como `unidad` o +`cantidadIngrediente`, no son visibles para la función externa. -The set of bindings visible inside a block is determined by the place of -that block in the program text. Each local scope can also see all the -local scopes that contain it, and all scopes can see the global scope. -This approach to binding visibility is called _((lexical scoping))_. +En resumen, cada alcance local puede ver también todos los alcances locales que +lo contengan. El conjunto de vinculaciones visibles dentro de un bloque +está determinado por el lugar de ese bloque en el texto del programa. +Cada alcance local puede también ver todos los alcances locales que lo +contengan, y todos los alcances pueden ver el alcance global. +Este enfoque para la visibilidad de vinculaciones es +llamado _((alcance léxico))_. -## Functions as values +## Funciones como valores -{{index [function, "as value"], [binding, definition]}} +{{index [function, "as value"]}} -A function binding usually simply acts as a name for a specific piece -of the program. Such a binding is defined once and never changed. This -makes it easy to confuse the function and its name. +Las ((vinculaciones)) de función simplemente actúan como nombres para +una pieza específica del programa. Tal vinculación se define una vez +y nunca cambia. Esto hace que sea fácil confundir la función con su nombre. {{index [binding, assignment]}} -But the two are different. A function value can do all the things that -other values can do—you can use it in arbitrary ((expression))s, not -just call it. It is possible to store a function value in a new -binding, pass it as an argument to a function, and so on. Similarly, a -binding that holds a function is still just a regular binding and can, -if not constant, be assigned a new value, like so: +Pero los dos son diferentes. Un valor de función puede hacer todas las cosas que +otros valores pueden hacer, puedes usarlo en ((expresion))es arbitrarias, no +solo llamarlo. Es posible almacenar un valor de función en una nueva +vinculación, pasarla como argumento a una función, y así sucesivamente. +Del mismo modo, una vinculación que contenga una función sigue siendo solo +una vinculación regular y se le puede asignar un nuevo valor, asi: ```{test: no} -let launchMissiles = function() { - missileSystem.launch("now"); +let lanzarMisiles = function() { + sistemaDeMisiles.lanzar("ahora"); }; -if (safeMode) { - launchMissiles = function() {/* do nothing */}; +if (modoSeguro) { + lanzarMisiles = function() {/* no hacer nada */}; } ``` {{index [function, "higher-order"]}} -In [Chapter ?](higher_order), we will discuss the interesting things -that can be done by passing around function values to other functions. +En el [Capitulo 5](orden_superior), discutiremos las cosas interesantes +que se pueden hacer al pasar valores de función a otras funciones. -## Declaration notation +## Notación de declaración -{{index [syntax, function], "function keyword", "square example", [function, definition], [function, declaration]}} +{{index syntax, "function keyword", "square example", [function, definition], [function, declaration]}} -There is a slightly shorter way to create a function binding. When the -`function` keyword is used at the start of a statement, it works -differently. +Hay una forma ligeramente más corta de crear una vinculación de función. Cuando +la palabra clave `function` es usada al comienzo de una declaración, funciona +de una manera diferente. ```{test: wrap} -function square(x) { +function cuadrado(x) { return x * x; } ``` {{index future, "execution order"}} -This is a function _declaration_. The statement defines the binding -`square` and points it at the given function. It is slightly easier -to write and doesn't require a semicolon after the function. +Esta es una _declaración_ de función. La declaración define la vinculación +`cuadrado` y la apunta a la función dada. Esto es un poco mas facil +de escribir, y no requiere un punto y coma después de la función. -There is one subtlety with this form of function definition. +Hay una sutileza con esta forma de definir una función. ``` -console.log("The future says:", future()); +console.log("El futuro dice:", futuro()); -function future() { - return "You'll never have flying cars"; +function futuro() { + return "Nunca tendran autos voladores"; } ``` -The preceding code works, even though the function is defined _below_ the code -that uses it. Function declarations are not part of the regular -top-to-bottom flow of control. They are conceptually moved to the top -of their scope and can be used by all the code in that scope. This is -sometimes useful because it offers the freedom to order code in a -way that seems meaningful, without worrying about having to define all -functions before they are used. +Este código funciona, aunque la función esté definida _debajo_ del código +que lo usa. Las declaraciones de funciones no son parte del flujo de control +regular de arriba hacia abajo. Estas son conceptualmente trasladadas a la cima +de su alcance y pueden ser utilizadas por todo el código en ese alcance. Esto es +a veces útil porque nos da la libertad de ordenar el código en una forma +que nos parezca significativa, sin preocuparnos por tener que definir todas +las funciones antes de que sean utilizadas. -## Arrow functions +## Funciones de flecha {{index function, "arrow function"}} -There's a third notation for functions, which looks very different -from the others. Instead of the `function` keyword, it uses an arrow -(`=>`) made up of an equal sign and a greater-than character (not to be -confused with the greater-than-or-equal operator, which is written +Existe una tercera notación para funciones, que se ve muy diferente +de las otras. En lugar de la palabra clave `function`, usa una flecha +(`=>`) compuesta de los caracteres igual y mayor que (no debe ser +confundida con el operador igual o mayor que, que se escribe `>=`). ```{test: wrap} -const power = (base, exponent) => { - let result = 1; - for (let count = 0; count < exponent; count++) { - result *= base; +const potencia = (base, exponente) => { + let resultado = 1; + for (let cuenta = 0; cuenta < exponente; cuenta++) { + resultado *= base; } - return result; + return resultado; }; ``` {{index [function, body]}} -The arrow comes _after_ the list of parameters and is followed by the -function's body. It expresses something like "this input (the -((parameter))s) produces this result (the body)". +La flecha viene _después_ de la lista de parámetros, y es seguida por +el cuerpo de la función. Expresa algo así como "esta entrada (los +((parámetro))s) produce este resultado (el cuerpo)". -{{index [braces, "function body"], "square example", [parentheses, arguments]}} +{{index "curly braces", "square example"}} -When there is only one parameter name, you can omit the parentheses around the -parameter list. If the body is a single expression, -rather than a ((block)) in braces, that expression will be returned -from the function. So, these two definitions of `square` do the same -thing: +Cuando solo haya un solo nombre de parámetro, los ((paréntesis)) alrededor de +la lista de parámetros pueden ser omitidos. Si el cuerpo es una sola expresión, +en lugar de un ((bloque)) en llaves, esa expresión será retornada por parte +de la función. Asi que estas dos definiciones de `cuadrado` hacen la misma +cosa: ``` -const square1 = (x) => { return x * x; }; -const square2 = x => x * x; +const cuadrado1 = (x) => { return x * x; }; +const cuadrado2 = x => x * x; ``` -{{index [parentheses, arguments]}} - -When an arrow function has no parameters at all, its parameter list is -just an empty set of parentheses. +Cuando una función de flecha no tiene parámetros, su lista de parámetros es +solo un conjunto vacío de ((paréntesis)). ``` -const horn = () => { +const bocina = () => { console.log("Toot"); }; ``` {{index verbosity}} -There's no deep reason to have both arrow functions and -`function` expressions in the language. Apart from a minor detail, -which we'll discuss in [Chapter ?](object), they do the same thing. -Arrow functions were added in 2015, mostly to make it possible to -write small function expressions in a less verbose way. We'll be using -them a lot in [Chapter ?](higher_order). +No hay una buena razón para tener ambas funciones de flecha y +expresiones `function` en el lenguaje. Aparte de un detalle menor, +que discutiremos en [Capítulo 6](objeto), estas hacen lo mismo. +Las funciones de flecha se agregaron en 2015, principalmente para que fuera +posible escribir pequeñas expresiones de funciones de una manera menos +verbosa. Las usaremos mucho en el [Capitulo 5](orden_superior). {{id stack}} -## The call stack +## La pila de llamadas {{indexsee stack, "call stack"}} {{index "call stack", [function, application]}} -The way control flows through functions is somewhat involved. Let's -take a closer look at it. Here is a simple program that makes a few -function calls: +La forma en que el control fluye a través de las funciones es algo +complicado. Vamos a écharle un vistazo más de cerca. Aquí hay un simple +programa que hace unas cuantas llamadas de función: ``` -function greet(who) { - console.log("Hello " + who); +function saludar(quien) { + console.log("Hola " + quien); } -greet("Harry"); -console.log("Bye"); +saludar("Harry"); +console.log("Adios"); ``` -{{index ["control flow", functions], "execution order", "console.log"}} +{{index "control flow", "execution order", "console.log"}} + +Un recorrido por este programa es más o menos así: la llamada a `saludar` +hace que el control salte al inicio de esa función (línea 2). +La función llama a `console.log`, la cual toma el control, hace su trabajo, y +entonces retorna el control a la línea 2. Allí llega al final de la +función `saludar`, por lo que vuelve al lugar que la llamó, que es la +línea 4. La línea que sigue llama a `console.log` nuevamente. Después +que esta función retorna, el programa llega a su fin. -A run through this program goes roughly like this: the call to `greet` -causes control to jump to the start of that function (line 2). The -function calls `console.log`, which takes control, does its job, and -then returns control to line 2. There it reaches the end of the -`greet` function, so it returns to the place that called it, which is -line 4. The line after that calls `console.log` again. After that -returns, the program reaches its end. +Podríamos mostrar el flujo de control esquemáticamente de esta manera: We could show the flow of control schematically like this: ```{lang: null} -not in function - in greet - in console.log - in greet -not in function - in console.log -not in function +no en una función + en saludar + en console.log + en saludar +no en una función + en console.log +no en una función ``` -{{index "return keyword", [memory, call stack]}} +{{index "return keyword", memory}} -Because a function has to jump back to the place that called it when -it returns, the computer must remember the context from which the call -happened. In one case, `console.log` has to return to the `greet` -function when it is done. In the other case, it returns to the end of -the program. +Ya que una función tiene que regresar al lugar donde fue llamada cuando +esta retorna, la computadora debe recordar el contexto de donde +sucedió la llamada. En un caso, `console.log` tiene que volver a la +función `saludar` cuando está lista. En el otro caso, vuelve al final del +programa. -The place where the computer stores this context is the _((call -stack))_. Every time a function is called, the current context is -stored on top of this stack. When a function returns, it removes the -top context from the stack and uses that context to continue execution. +El lugar donde la computadora almacena este contexto es la _((pila de +llamadas))_. Cada vez que se llama a una función, el contexto actual es +almacenado en la parte superior de esta "pila". Cuando una función retorna, +elimina el contexto superior de la pila y lo usa para continuar la ejecución. {{index "infinite loop", "stack overflow", recursion}} -Storing this stack requires space in the computer's memory. When the -stack grows too big, the computer will fail with a message like "out -of stack space" or "too much recursion". The following code -illustrates this by asking the computer a really hard question that -causes an infinite back-and-forth between two functions. Rather, it -_would_ be infinite, if the computer had an infinite stack. As it is, -we will run out of space, or "blow the stack". +Almacenar esta pila requiere espacio en la memoria de la computadora. Cuando +la pila crece demasiado grande, la computadora fallará con un mensaje como +"fuera de espacio de pila" o "demasiada recursividad". El siguiente código +ilustra esto haciendo una pregunta realmente difícil a la computadora, que +causara un ir y venir infinito entre las dos funciones. Mejor dicho, +_sería_ infinito, si la computadora tuviera una pila infinita. Como son +las cosas, nos quedaremos sin espacio, o "explotaremos la pila". ```{test: no} -function chicken() { - return egg(); +function gallina() { + return huevo(); } -function egg() { - return chicken(); +function huevo() { + return gallina(); } -console.log(chicken() + " came first."); +console.log(gallina() + " vino primero."); // → ?? ``` -## Optional Arguments +## Argumentos Opcionales {{index argument, [function, application]}} -The following code is allowed and executes without any problem: +El siguiente código está permitido y se ejecuta sin ningún problema: ``` -function square(x) { return x * x; } -console.log(square(4, true, "hedgehog")); +function cuadrado(x) { return x * x; } +console.log(cuadrado(4, true, "erizo")); // → 16 ``` -We defined `square` with only one ((parameter)). Yet when we call it -with three, the language doesn't complain. It ignores the extra -arguments and computes the square of the first one. +Definimos `cuadrado` con solo un ((parámetro)). Sin embargo, cuando lo llamamos +con tres, el lenguaje no se queja. Este ignora los argumentos extra +y calcula el cuadrado del primero. {{index undefined}} -JavaScript is extremely broad-minded about the number of arguments you -pass to a function. If you pass too many, the extra ones are ignored. -If you pass too few, the missing parameters get assigned the value +JavaScript es de extremadamente mente-abierta sobre la cantidad de argumentos +que puedes pasar a una función. Si pasa demasiados, los adicionales son +ignorados. Si pasas muy pocos, a los parámetros faltantes se les asigna el valor `undefined`. -The downside of this is that it is possible—likely, even—that you'll -accidentally pass the wrong number of arguments to functions. And no -one will tell you about it. +La desventaja de esto es que es posible, incluso probable, que +accidentalmente pases la cantidad incorrecta de argumentos a las funciones. +Y nadie te dira nada acerca de eso. -The upside is that this behavior can be used to allow a function to be -called with different numbers of arguments. For example, this `minus` -function tries to imitate the `-` operator by acting on either one or -two arguments: +La ventaja es que este comportamiento se puede usar para permitir que +una función sea llamada con diferentes cantidades de argumentos. Por ejemplo, +esta función `menos` intenta imitar al operador `-` actuando ya sea en uno o +dos argumentos ``` -function minus(a, b) { +function menos(a, b) { if (b === undefined) return -a; else return a - b; } -console.log(minus(10)); +console.log(menos(10)); // → -10 -console.log(minus(10, 5)); +console.log(menos(10, 5)); // → 5 ``` {{id power}} -{{index "optional argument", "default value", parameter, ["= operator", "for default value"]}} +{{index "optional argument", "default value", parameter, "= operator"}} -If you write an `=` operator after -a parameter, followed by an expression, the value of that expression -will replace the argument when it is not given. +Si escribes un operador `=` después un parámetro, seguido de una expresión, +el valor de esa expresión reemplazará al argumento cuando este no sea dado. {{index "power example"}} -For example, this version of `power` makes its second argument -optional. If you don't provide it or pass the value `undefined`, it will default to two, and the -function will behave like `square`. +Por ejemplo, esta versión de `potencia` hace que su segundo argumento +sea opcional. Si este no es proporcionado o si pasas el valor `undefined`, +este se establecerá en dos y la función se comportará como `cuadrado`. ```{test: wrap} -function power(base, exponent = 2) { - let result = 1; - for (let count = 0; count < exponent; count++) { - result *= base; +function potencia(base, exponente = 2) { + let resultado = 1; + for (let cuenta = 0; cuenta < exponente; cuenta++) { + resultado *= base; } - return result; + return resultado; } -console.log(power(4)); +console.log(potencia(4)); // → 16 -console.log(power(2, 6)); +console.log(potencia(2, 6)); // → 64 ``` {{index "console.log"}} -In the [next chapter](data#rest_parameters), we will see a way in -which a function body can get at the whole list of arguments it was -passed. This is helpful because it makes it possible for a function to -accept any number of arguments. For example, `console.log` does -this—it outputs all of the values it is given. +En el [próximo capítulo](datos#parametros_rest), veremos una forma en el +que el cuerpo de una función puede obtener una lista de todos los argumentos +que son pasados. Esto es útil porque hace posible que una función +acepte cualquier cantidad de argumentos. Por ejemplo, `console.log` hace +esto, muetra en la consola todos los valores que se le den. ``` console.log("C", "O", 2); // → C O 2 ``` -## Closure +## Cierre {{index "call stack", "local binding", [function, "as value"], scope}} -The ability to treat functions as values, combined with the fact that -local bindings are re-created every time a function is called, brings -up an interesting question. What happens to local bindings when the -function call that created them is no longer active? +La capacidad de tratar a las funciones como valores, combinado con el hecho de +que las vinculaciones locales se vuelven a crear cada vez que una sea función +es llamada, trae a la luz una pregunta interesante. Qué sucede con las +vinculaciones locales cuando la llamada de función que los creó ya no +está activa? -The following code shows an example of this. It defines a function, -`wrapValue`, that creates a local binding. It then returns a function -that accesses and returns this local binding. +El siguiente código muestra un ejemplo de esto. Define una función, +`envolverValor`, que crea una vinculación local. Luego retorna una función +que accede y devuelve esta vinculación local. ``` -function wrapValue(n) { +function envolverValor(n) { let local = n; return () => local; } -let wrap1 = wrapValue(1); -let wrap2 = wrapValue(2); -console.log(wrap1()); +let envolver1 = envolverValor(1); +let envolver2 = envolverValor(2); +console.log(envolver1()); // → 1 -console.log(wrap2()); +console.log(envolver2()); // → 2 ``` -This is allowed and works as you'd hope—both instances of the binding -can still be accessed. This situation is a good demonstration of the -fact that local bindings are created anew for every call, and -different calls can't trample on one another's local bindings. +Esto está permitido y funciona como es de esperar, ambas instancias de +las vinculaciones todavía pueden ser accedidas. Esta situación es una buena +demostración del hecho de que las vinculaciones locales se crean de nuevo para +cada llamada, y que las diferentes llamadas no pueden pisotear las +distintas vinculaciones locales entre sí. -This feature—being able to reference a specific instance of a local -binding in an enclosing scope—is called _((closure))_. A function that -references bindings from local scopes around it is called _a_ closure. This behavior -not only frees you from having to worry about lifetimes of bindings -but also makes it possible to use function values in some creative -ways. +Esta característica, poder hacer referencia a una instancia específica +de una vinculación local en un alcance encerrado, se llama _((cierre))_. +Una función que que hace referencia a vinculaciones de alcances locales +alrededor de ella es llamada _un_ cierre. +Este comportamiento no solo te libera de tener que preocuparte +por la duración de las vinculaciones pero también hace posible usar valores de +funciones en algunas formas bastante creativas. {{index "multiplier function"}} -With a slight change, we can turn the previous example into a way to -create functions that multiply by an arbitrary amount. +Con un ligero cambio, podemos convertir el ejemplo anterior en una forma de +crear funciones que multipliquen por una cantidad arbitraria. ``` -function multiplier(factor) { - return number => number * factor; +function multiplicador(factor) { + return numero => numero * factor; } -let twice = multiplier(2); -console.log(twice(5)); +let duplicar = multiplicador(2); +console.log(duplicar(5)); // → 10 ``` {{index [binding, "from parameter"]}} -The explicit `local` binding from the `wrapValue` example isn't really -needed since a parameter is itself a local binding. +La vinculación explícita `local` del ejemplo `envolverValor` no es realmente +necesaria ya que un parámetro es en sí misma una vinculación local. {{index [function, "model of"]}} -Thinking about programs like this takes some practice. A good mental -model is to think of function values as containing both the code in -their body and the environment in which they are created. When called, -the function body sees the environment in which it was created, not the -environment in which it is called. +Pensar en programas de esta manera requiere algo de práctica. Un buen modelo +mental es pensar en los valores de función como que contienen tanto el código en +su cuerpo tanto como el entorno en el que se crean. Cuando son llamadas, +el cuerpo de la función ve su entorno original, no el entorno +en el que se realiza la llamada. -In the example, `multiplier` is called and creates an environment in -which its `factor` parameter is bound to 2. The function value it -returns, which is stored in `twice`, remembers this environment. So -when that is called, it multiplies its argument by 2. +En el ejemplo, se llama a `multiplicador` y esta crea un entorno en el +que su parámetro `factor` está ligado a 2. El valor de función que +retorna, el cual se almacena en `duplicar`, recuerda este entorno. Asi que +cuando es es llamada, multiplica su argumento por 2. -## Recursion +## Recursión {{index "power example", "stack overflow", recursion, [function, application]}} -It is perfectly okay for a function to call itself, as long as it -doesn't do it so often that it overflows the stack. A function that calls -itself is called _recursive_. Recursion allows some functions to be -written in a different style. Take, for example, this alternative -implementation of `power`: +Está perfectamente bien que una función se llame a sí misma, siempre que +no lo haga tanto que desborde la pila. Una función que se llama +a si misma es llamada _recursiva_. La recursión permite que algunas +funciones sean escritas en un estilo diferente. Mira, por ejemplo, +esta implementación alternativa de `potencia`: ```{test: wrap} -function power(base, exponent) { - if (exponent == 0) { +function potencia(base, exponente) { + if (exponente == 0) { return 1; } else { - return base * power(base, exponent - 1); + return base * potencia(base, exponente - 1); } } -console.log(power(2, 3)); +console.log(potencia(2, 3)); // → 8 ``` {{index loop, readability, mathematics}} -This is rather close to the way mathematicians define exponentiation -and arguably describes the concept more clearly than the looping -variant. The function calls itself multiple times with ever smaller -exponents to achieve the repeated multiplication. +Esto es bastante parecido a la forma en la que los matemáticos definen +la exponenciación y posiblemente describa el concepto más claramente que +la variante con el ciclo. La función se llama a si misma muchas veces con +cada vez exponentes más pequeños para lograr la multiplicación repetida. {{index [function, application], efficiency}} -But this implementation has one problem: in typical JavaScript -implementations, it's about three times slower than the looping version. -Running through a simple loop is generally cheaper than calling a -function multiple times. +Pero esta implementación tiene un problema: en las implementaciones típicas de +JavaScript, es aproximadamente 3 veces más lenta que la versión que usa un +ciclo. Correr a través de un ciclo simple es generalmente más barato en +terminos de memoria que llamar a una función multiples veces. {{index optimization}} -The dilemma of speed versus ((elegance)) is an interesting one. You -can see it as a kind of continuum between human-friendliness and -machine-friendliness. Almost any program can be made faster by making -it bigger and more convoluted. The programmer has to decide on an -appropriate balance. +El dilema de velocidad versus ((elegancia)) es interesante. +Puedes verlo como una especie de compromiso entre accesibilidad-humana y +accesibilidad-maquina. Casi cualquier programa se puede hacer más +rápido haciendolo más grande y complicado. El programador tiene que +decidir acerca de cual es un equilibrio apropiado. -In the case of the `power` function, the inelegant (looping) version -is still fairly simple and easy to read. It doesn't make much sense to -replace it with the recursive version. Often, though, a program deals -with such complex concepts that giving up some efficiency in order to -make the program more straightforward is helpful. +En el caso de la función `potencia`, la versión poco elegante (con el ciclo) +sigue siendo bastante simple y fácil de leer. No tiene mucho sentido +reemplazarla con la versión recursiva. A menudo, sin embargo, un programa trata +con conceptos tan complejos que renunciar a un poco de eficiencia con el fin de +hacer que el programa sea más sencillo es útil. {{index profiling}} -Worrying about efficiency can be a distraction. It's yet another -factor that complicates program design, and when you're doing -something that's already difficult, that extra thing to worry about -can be paralyzing. +Preocuparse por la eficiencia puede ser una distracción. Es otro factor más +que complica el diseño del programa, y ​​cuando estás haciendo +algo que ya es difícil, añadir algo más de lo que preocuparse puede ser +paralizante. {{index "premature optimization"}} -Therefore, always start by writing something that's correct and easy -to understand. If you're worried that it's too slow—which it usually -isn't since most code simply isn't executed often enough to take any -significant amount of time—you can measure afterward and improve it -if necessary. +Por lo tanto, siempre comienza escribiendo algo que sea correcto y fácil de +comprender. Si te preocupa que sea demasiado lento, lo que generalmente +no sucede, ya que la mayoría del código simplemente no se ejecuta con la +suficiente frecuencia como para tomar cantidades significativas de +tiempo, puedes medir luego y mejorar si es necesario. {{index "branching recursion"}} -Recursion is not always just an inefficient alternative to looping. -Some problems really are easier to solve with recursion than with -loops. Most often these are problems that require exploring or -processing several "branches", each of which might branch out again -into even more branches. +La recursión no siempre es solo una alternativa ineficiente a los ciclos. +Algunos problemas son realmente más fáciles de resolver con recursión que con +ciclos. En la mayoría de los casos, estos son problemas que requieren explorar o +procesar varias "ramas", cada una de las cuales podría ramificarse de nuevo +en aún más ramas. {{id recursive_puzzle}} {{index recursion, "number puzzle example"}} -Consider this puzzle: by starting from the number 1 and repeatedly -either adding 5 or multiplying by 3, an infinite set of numbers -can be produced. How would you write a function that, given a number, -tries to find a sequence of such additions and multiplications that -produces that number? +Considera este acertijo: comenzando desde el número 1 y repetidamente +agregando 5 o multiplicando por 3, una cantidad infinita de números nuevos +pueden ser producidos. ¿Cómo escribirías una función que, dado un número, +intente encontrar una secuencia de tales adiciones y multiplicaciones que +produzca ese número? -For example, the number 13 could be reached by first multiplying by 3 -and then adding 5 twice, whereas the number 15 cannot be reached at -all. +Por ejemplo, se puede llegar al número 13 multiplicando primero por 3 +y luego agregando 5 dos veces, mientras que el número 15 no puede ser +alcanzado de ninguna manera. -Here is a recursive solution: +Aquí hay una solución recursiva: ``` -function findSolution(target) { - function find(current, history) { - if (current == target) { - return history; - } else if (current > target) { +function encontrarSolucion(objetivo) { + function encontrar(actual, historia) { + if (actual == objetivo) { + return historia; + } else if (actual > objetivo) { return null; } else { - return find(current + 5, `(${history} + 5)`) || - find(current * 3, `(${history} * 3)`); + return encontrar(actual + 5, `(${historia} + 5)`) || + encontrar(actual * 3, `(${historia} * 3)`); } } - return find(1, "1"); + return encontrar(1, "1"); } -console.log(findSolution(24)); +console.log(encontrarSolucion(24)); // → (((1 * 3) + 5) * 3) ``` -Note that this program doesn't necessarily find the _shortest_ -sequence of operations. It is satisfied when it finds any sequence at -all. +Ten en cuenta que este programa no necesariamente encuentra la secuencia de +operaciones _mas corta_. Este está satisfecho cuando encuentra cualquier +secuencia que funcione. -It is okay if you don't see how it works right away. Let's work -through it, since it makes for a great exercise in recursive thinking. +Está bien si no ves cómo funciona el programa de inmediato. Vamos a trabajar +a través de él, ya que es un gran ejercicio de pensamiento recursivo. -The inner function `find` does the actual recursing. It takes two -((argument))s: the current number and a string that records how we -reached this number. If it finds a solution, it returns a string that -shows how to get to the target. If no solution can be found starting -from this number, it returns `null`. +La función interna `encontrar` es la que hace uso de la recursión real. +Esta toma dos ((argumento))s, el número actual y un string que registra cómo +se ha alcanzado este número. Si encuentra una solución, devuelve un string que +muestra cómo llegar al objetivo. Si no puede encontrar una solución +a partir de este número, retorna `null`. {{index null, "|| operator", "short-circuit evaluation"}} -To do this, the function performs one of three actions. If the current -number is the target number, the current history is a way to reach -that target, so it is returned. If the current number is greater than -the target, there's no sense in further exploring this branch because -both adding and multiplying will only make the number bigger, so it -returns `null`. Finally, if we're still below the target number, -the function tries both possible paths that start from the current -number by calling itself twice, once for addition and once for -multiplication. If the first call returns something that is not -`null`, it is returned. Otherwise, the second call is returned, -regardless of whether it produces a string or `null`. +Para hacer esto, la función realiza una de tres acciones. Si el número actual +es el número objetivo, la historia actual es una forma de llegar a +ese objetivo, por lo que es retornada. Si el número actual es mayor que +el objetivo, no tiene sentido seguir explorando esta rama ya que +tanto agregar como multiplicar solo hara que el número sea mas grande, por lo que +retorna `null`. Y finalmente, si aún estamos por debajo del número objetivo, +la función intenta ambos caminos posibles que comienzan desde el número actual +llamandose a sí misma dos veces, una para agregar y otra para +multiplicar. Si la primera llamada devuelve algo que no es +`null`, esta es retornada. De lo contrario, se retorna la segunda llamada, +independientemente de si produce un string o el valor `null`. {{index "call stack"}} -To better understand how this function produces the effect we're -looking for, let's look at all the calls to `find` that are made when -searching for a solution for the number 13. +Para comprender mejor cómo esta función produce el efecto que estamos +buscando, veamos todas las llamadas a `encontrar` que se hacen cuando +buscamos una solución para el número 13. ```{lang: null} -find(1, "1") - find(6, "(1 + 5)") - find(11, "((1 + 5) + 5)") - find(16, "(((1 + 5) + 5) + 5)") - too big - find(33, "(((1 + 5) + 5) * 3)") - too big - find(18, "((1 + 5) * 3)") - too big - find(3, "(1 * 3)") - find(8, "((1 * 3) + 5)") - find(13, "(((1 * 3) + 5) + 5)") - found! +encontrar(1, "1") + encontrar(6, "(1 + 5)") + encontrar(11, "((1 + 5) + 5)") + encontrar(16, "(((1 + 5) + 5) + 5)") + muy grande + encontrar(33, "(((1 + 5) + 5) * 3)") + muy grande + encontrar(18, "((1 + 5) * 3)") + muy grande + encontrar(3, "(1 * 3)") + encontrar(8, "((1 * 3) + 5)") + encontrar(13, "(((1 * 3) + 5) + 5)") + ¡encontrado! ``` -The indentation indicates the depth of the call stack. The first time -`find` is called, it starts by calling itself to explore the solution -that starts with `(1 + 5)`. That call will further recurse to explore -_every_ continued solution that yields a number less than or equal to -the target number. Since it doesn't find one that hits the target, it -returns `null` back to the first call. There the `||` operator causes -the call that explores `(1 * 3)` to happen. This search has more -luck—its first recursive call, through yet _another_ recursive call, -hits upon the target number. That innermost call returns a string, and -each of the `||` operators in the intermediate calls passes that string -along, ultimately returning the solution. +La indentación indica la profundidad de la pila de llamadas. La primera vez +que `encontrar` es llamada, comienza llamandose a sí misma para explorar +la solución que comienza con `(1 + 5)`. Esa llamada hara uso de la recursión +aún más para explorar _cada_ solución continuada que produzca un número menor +o igual a el número objetivo. Como no encuentra uno que llegue al objetivo, +retorna `null` a la primera llamada. Ahí el operador `||` genera la llamada +que explora `(1 * 3)` para que esta suceda. Esta búsqueda tiene más +suerte, su primera llamada recursiva, a través de _otra_ llamada recursiva, +encuentra al número objetivo. Esa llamada más interna retorna un string, y +cada uno de los operadores `||` en las llamadas intermedias pasa ese string +a lo largo, en última instancia retornando la solución. -## Growing functions +## Funciones crecientes {{index [function, definition]}} -There are two more or less natural ways for functions to be introduced -into programs. +Hay dos formas más o menos naturales para que las funciones sean +introducidas en los programas. {{index repetition}} -The first is that you find yourself writing similar code multiple -times. You'd prefer not to do that. Having more code means more space -for mistakes to hide and more material to read for people trying to -understand the program. So you take the repeated functionality, find a -good name for it, and put it into a function. +La primera es que te encuentras escribiendo código muy similar múltiples +veces. Preferiríamos no hacer eso. Tener más código significa más espacio +para que los errores se oculten y más material que leer para las personas +que intenten entender el programa. Entonces tomamos la funcionalidad repetida, +buscamos un buen nombre para ella, y la ponemos en una función. -The second way is that you find you need some functionality that you -haven't written yet and that sounds like it deserves its own function. -You'll start by naming the function, and then you'll write its body. -You might even start writing code that uses the function before you -actually define the function itself. +La segunda forma es que encuentres que necesitas alguna funcionalidad que +aún no has escrito y parece que merece su propia función. +Comenzarás por nombrar a la función y luego escribirás su cuerpo. +Incluso podrías comenzar a escribir código que use la función antes de que +definas a la función en sí misma. {{index [function, naming], [binding, naming]}} -How difficult it is to find a good name for a function is a good -indication of how clear a concept it is that you're trying to wrap. -Let's go through an example. +Que tan difícil te sea encontrar un buen nombre para una función es una buena +indicación de cuán claro es el concepto que está tratando de envolver. +Veamos un ejemplo. {{index "farm example"}} -We want to write a program that prints two numbers: the numbers of -cows and chickens on a farm, with the words `Cows` and `Chickens` -after them and zeros padded before both numbers so that they are -always three digits long. +Queremos escribir un programa que imprima dos números, los números de +vacas y pollos en una granja, con las palabras `Vacas` y `Pollos` +después de ellos, y ceros acolchados antes de ambos números para que +siempre tengan tres dígitos de largo. ```{lang: null} -007 Cows -011 Chickens +007 Vacas +011 Pollos ``` -This asks for a function of two arguments—the number of cows and the -number of chickens. Let's get coding. +Esto pide una función de dos argumentos, el numero de vacas y el numero +de pollos. Vamos a programar. ``` -function printFarmInventory(cows, chickens) { - let cowString = String(cows); - while (cowString.length < 3) { - cowString = "0" + cowString; +function imprimirInventarioGranja(vacas, pollos) { + let stringVaca = String(vacas); + while (stringVaca.length < 3) { + stringVaca = "0" + stringVaca; } - console.log(`${cowString} Cows`); - let chickenString = String(chickens); - while (chickenString.length < 3) { - chickenString = "0" + chickenString; + console.log(`${stringVaca} Vacas`); + let stringPollos = String(pollos); + while (stringPollos.length < 3) { + stringPollos = "0" + stringPollos; } - console.log(`${chickenString} Chickens`); + console.log(`${stringPollos} Pollos`); } -printFarmInventory(7, 11); +imprimirInventarioGranja(7, 11); ``` {{index ["length property", "for string"], "while loop"}} -Writing `.length` after a string expression will give us the length of -that string. Thus, the `while` loops keep adding zeros in front of the -number strings until they are at least three characters long. +Escribir `.length` después de una expresión de string nos dará la longitud de +dicho string. Por lo tanto, los ciclos `while` seguiran sumando ceros delante +del string de numeros hasta que este tenga al menos tres caracteres de longitud. -Mission accomplished! But just as we are about to send the farmer the -code (along with a hefty invoice), she calls and tells us she's also -started keeping pigs, and couldn't we please extend the software to -also print pigs? +Misión cumplida! Pero justo cuando estamos por enviar el código a la +agricultora (junto con una considerable factura), ella nos llama y nos dice +que ella también comenzó a criar cerdos, y que si no podríamos extender +el software para imprimir cerdos también? {{index "copy-paste programming"}} -We sure can. But just as we're in the process of copying and pasting -those four lines one more time, we stop and reconsider. There has to -be a better way. Here's a first attempt: +Claro que podemos. Pero justo cuando estamos en el proceso de copiar y pegar +esas cuatro líneas una vez más, nos detenemos y reconsideramos. Tiene que haber +una mejor manera. Aquí hay un primer intento: ``` -function printZeroPaddedWithLabel(number, label) { - let numberString = String(number); - while (numberString.length < 3) { - numberString = "0" + numberString; +function imprimirEtiquetaAlcochadaConCeros(numero, etiqueta) { + let stringNumero = String(numero); + while (stringNumero.length < 3) { + stringNumero = "0" + stringNumero; } - console.log(`${numberString} ${label}`); + console.log(`${stringNumero} ${etiqueta}`); } -function printFarmInventory(cows, chickens, pigs) { - printZeroPaddedWithLabel(cows, "Cows"); - printZeroPaddedWithLabel(chickens, "Chickens"); - printZeroPaddedWithLabel(pigs, "Pigs"); +function imprimirInventarioGranja(vacas, pollos, cerdos) { + imprimirEtiquetaAlcochadaConCeros(vacas, "Vacas"); + imprimirEtiquetaAlcochadaConCeros(pollos, "Pollos"); + imprimirEtiquetaAlcochadaConCeros(cerdos, "Cerdos"); } -printFarmInventory(7, 11, 3); +imprimirInventarioGranja(7, 11, 3); ``` {{index [function, naming]}} -It works! But that name, `printZeroPaddedWithLabel`, is a little -awkward. It conflates three things—printing, zero-padding, and adding -a label—into a single function. +Funciona! Pero ese nombre, `imprimirEtiquetaAlcochadaConCeros`, es un poco +incómodo. Combina tres cosas, impresión, alcochar con ceros y añadir +una etiqueta, en una sola función. {{index "zeroPad function"}} -Instead of lifting out the repeated part of our program wholesale, -let's try to pick out a single _concept_. +En lugar de sacar la parte repetida de nuestro programa al por mayor, +intentemos elegir un solo _concepto_. ``` -function zeroPad(number, width) { - let string = String(number); - while (string.length < width) { +function alcocharConCeros(numero, amplitud) { + let string = String(numero); + while (string.length < amplitud) { string = "0" + string; } return string; } -function printFarmInventory(cows, chickens, pigs) { - console.log(`${zeroPad(cows, 3)} Cows`); - console.log(`${zeroPad(chickens, 3)} Chickens`); - console.log(`${zeroPad(pigs, 3)} Pigs`); +function imprimirInventarioGranja(vacas, pollos, cerdos) { + console.log(`${alcocharConCeros(vacas, 3)} Vacas`); + console.log(`${alcocharConCeros(pollos, 3)} Pollos`); + console.log(`${alcocharConCeros(cerdos, 3)} Cerdos`); } -printFarmInventory(7, 16, 3); +imprimirInventarioGranja(7, 16, 3); ``` {{index readability, "pure function"}} -A function with a nice, obvious name like `zeroPad` makes it easier -for someone who reads the code to figure out what it does. And such a -function is useful in more situations than just this specific program. -For example, you could use it to help print nicely aligned tables of -numbers. +Una función con un nombre agradable y obvio como `alcocharConCeros` hace +que sea más fácil de entender lo que hace para alguien que lee el código. +Y tal función es útil en situaciones más alla de este programa en específico. +Por ejemplo, podrías usarla para ayudar a imprimir tablas de +números en una manera alineada. {{index [interface, design]}} -How smart and versatile _should_ our function be? We could write -anything, from a terribly simple function that can only pad a number -to be three characters wide to a complicated generalized -number-formatting system that handles fractional numbers, negative -numbers, alignment of decimal dots, padding with different characters, -and so on. +Que tan inteligente y versátil _deberia_ de ser nuestra función? Podríamos +escribir cualquier cosa, desde una función terriblemente simple que solo +pueda alcochar un número para que tenga tres caracteres de ancho, +a un complicado sistema generalizado de formateo de números que maneje +números fraccionarios, números negativos, alineación de puntos decimales, +relleno con diferentes caracteres, y así sucesivamente. -A useful principle is to not add cleverness unless you are absolutely -sure you're going to need it. It can be tempting to write general -"((framework))s" for every bit of functionality you come across. -Resist that urge. You won't get any real work done—you'll just be -writing code that you never use. +Un principio útil es no agregar mucho ingenio a menos que estes absolutamente +seguro de que lo vas a necesitar. Puede ser tentador escribir "((framework))s" +generalizados para cada funcionalidad que encuentres. +Resiste ese impulso. No realizarás ningún trabajo real de esta manera, solo +estarás escribiendo código que nunca usarás. {{id pure}} -## Functions and side effects +## Funciones y efectos secundarios {{index "side effect", "pure function", [function, purity]}} -Functions can be roughly divided into those that are called for their -side effects and those that are called for their return value. (Though -it is definitely also possible to both have side effects and return a -value.) +Las funciones se pueden dividir aproximadamente en aquellas que se llaman +por su efectos secundarios y aquellas que son llamadas por su valor de +retorno. (Aunque definitivamente también es posible tener tanto efectos +secundarios como devolver un valor en una misma función.) {{index reuse}} -The first helper function in the ((farm example)), -`printZeroPaddedWithLabel`, is called for its side effect: it prints a -line. The second version, `zeroPad`, is called for its return value. -It is no coincidence that the second is useful in more situations than -the first. Functions that create values are easier to combine in new -ways than functions that directly perform side effects. +La primera función auxiliar en el ((ejemplo de la granja)), +`imprimirEtiquetaAlcochadaConCeros`, se llama por su efecto secundario: +imprime una línea. La segunda versión, `alcocharConCeros`, se llama por su +valor de retorno. No es coincidencia que la segunda sea útil en más situaciones +que la primera. Las funciones que crean valores son más fáciles de combinar +en nuevas formas que las funciones que directamente realizan efectos +secundarios. {{index substitution}} -A _pure_ function is a specific kind of value-producing function that -not only has no side effects but also doesn't rely on side effects -from other code—for example, it doesn't read global bindings whose -value might change. A pure function has the pleasant property that, -when called with the same arguments, it always produces the same value -(and doesn't do anything else). A call to such a function can be -substituted by its return value without changing the meaning of the -code. When you are not sure that a pure function is working correctly, -you can test it by simply calling it and know that if it works in -that context, it will work in any context. Nonpure functions tend to -require more scaffolding to test. +Una función _pura_ es un tipo específico de función de producción-de-valores +que no solo no tiene efectos secundarios pero que tampoco depende de los +efectos secundarios de otro código, por ejemplo, no lee vinculaciones globales +cuyos valores puedan cambiar. Una función pura tiene la propiedad agradable +de que cuando se le llama con los mismos argumentos, siempre produce el +mismo valor (y no hace nada más). Una llamada a tal función puede ser +sustituida por su valor de retorno sin cambiar el significado del código. +Cuando no estás seguro de que una función pura esté funcionando +correctamente, puedes probarla simplemente llamándola, y saber que si +funciona en ese contexto, funcionará en cualquier contexto. Las funciones +no puras tienden a requerir más configuración para poder ser probadas. {{index optimization, "console.log"}} -Still, there's no need to feel bad when writing functions that are not -pure or to wage a holy war to purge them from your code. Side effects -are often useful. There'd be no way to write a pure version of -`console.log`, for example, and `console.log` is good to have. Some -operations are also easier to express in an efficient way when we use -side effects, so computing speed can be a reason to avoid purity. +Aún así, no hay necesidad de sentirse mal cuando escribas funciones que no son +puras o de hacer una guerra santa para purgarlas de tu código. +Los efectos secundarios a menudo son útiles. No habría forma de escribir una +versión pura de `console.log`, por ejemplo, y `console.log` es bueno de tener. +Algunas operaciones también son más fáciles de expresar de una manera +eficiente cuando usamos efectos secundarios, por lo que la velocidad de +computación puede ser una razón para evitar la pureza. -## Summary +## Resumen -This chapter taught you how to write your own functions. The -`function` keyword, when used as an expression, can create a function -value. When used as a statement, it can be used to declare a binding -and give it a function as its value. Arrow functions are yet another -way to create functions. +Este capítulo te enseñó a escribir tus propias funciones. La palabra clave +`function`, cuando se usa como una expresión, puede crear un valor de función. +Cuando se usa como una declaración, se puede usar para declarar una vinculación +y darle una función como su valor. Las funciones de flecha son otra forma +más de crear funciones. ``` -// Define f to hold a function value +// Define f para sostener un valor de función const f = function(a) { console.log(a + 2); }; -// Declare g to be a function +// Declara g para ser una función function g(a, b) { return a * b * 3.5; } -// A less verbose function value +// Un valor de función menos verboso let h = a => a % 3; ``` -A key aspect in understanding functions is understanding scopes. Each -block creates a new scope. Parameters and bindings declared in a given -scope are local and not visible from the outside. Bindings declared -with `var` behave differently—they end up in the nearest function -scope or the global scope. +Un aspecto clave en para comprender a las funciones es comprender los alcances. Cada +bloque crea un nuevo alcance. Los parámetros y vinculaciones declaradas en +un determinado alcance son locales y no son visibles desde el exterior. +Vinculaciones declaradas con `var` se comportan de manera +diferente, terminan en el alcance de la función más cercana o en el alcance global. -Separating the tasks your program performs into different functions is -helpful. You won't have to repeat yourself as much, and functions can -help organize a program by grouping code into pieces that do specific -things. +Separar las tareas que realiza tu programa en diferentes funciones es +util. No tendrás que repetirte tanto, y las funciones pueden +ayudar a organizar un programa agrupando el código en piezas que hagan +cosas especificas. -## Exercises +## Ejercicios -### Minimum +### Mínimo {{index "Math object", "minimum (exercise)", "Math.min function", minimum}} -The [previous chapter](program_structure#return_values) introduced the -standard function `Math.min` that returns its smallest argument. We -can build something like that now. Write a function `min` that takes -two arguments and returns their minimum. +El [capítulo anterior](estructura_de_programa#valores_de_retorno) introdujo +la función estándar `Math.min` que devuelve su argumento más pequeño. Nosotros +podemos construir algo como eso ahora. Escribe una función `min` que tome +dos argumentos y retorne su mínimo. {{if interactive ```{test: no} -// Your code here. +// Tu codigo aqui. console.log(min(0, 10)); // → 0 @@ -989,50 +998,50 @@ if}} {{index "minimum (exercise)"}} -If you have trouble putting braces and -parentheses in the right place to get a valid function definition, -start by copying one of the examples in this chapter and modifying it. +Si tienes problemas para poner llaves y +paréntesis en los lugares correctos para obtener una definición válida de función, +comienza copiando uno de los ejemplos en este capítulo y modificándolo. {{index "return keyword"}} -A function may contain multiple `return` statements. +Una función puede contener múltiples declaraciones de `return`. hint}} -### Recursion +### Recursión {{index recursion, "isEven (exercise)", "even number"}} -We've seen that `%` (the remainder operator) can be used to test -whether a number is even or odd by using `% 2` to see whether it's -divisible by two. Here's another way to define whether a positive -whole number is even or odd: +Hemos visto que `%` (el operador de residuo) se puede usar para probar +si un número es par o impar usando `% 2` para ver si es +divisible entre dos. Aquí hay otra manera de definir si un +número entero positivo es par o impar: -- Zero is even. +- Zero es par. -- One is odd. +- Uno es impar. -- For any other number _N_, its evenness is the same as _N_ - 2. +- Para cualquier otro número _N_, su paridad es la misma que _N_ - 2. -Define a recursive function `isEven` corresponding to this -description. The function should accept a single parameter (a -positive, whole number) and return a Boolean. +Define una función recursiva `esPar` que corresponda a esta +descripción. La función debe aceptar un solo parámetro (un +número entero, positivo) y devolver un Booleano. {{index "stack overflow"}} -Test it on 50 and 75. See how it behaves on -1. Why? Can you think of -a way to fix this? +Pruébalo con 50 y 75. Observa cómo se comporta con -1. Por qué? +Puedes pensar en una forma de arreglar esto? {{if interactive ```{test: no} -// Your code here. +// Tu codigo aqui. -console.log(isEven(50)); +console.log(esPar(50)); // → true -console.log(isEven(75)); +console.log(esPar(75)); // → false -console.log(isEven(-1)); +console.log(esPar(-1)); // → ?? ``` @@ -1042,52 +1051,52 @@ if}} {{index "isEven (exercise)", ["if keyword", chaining], recursion}} -Your function will likely look somewhat similar to the inner `find` -function in the recursive `findSolution` -[example](functions#recursive_puzzle) in this chapter, with an -`if`/`else if`/`else` chain that tests which of the three cases -applies. The final `else`, corresponding to the third case, makes the -recursive call. Each of the branches should contain a `return` -statement or in some other way arrange for a specific value to be -returned. +Es probable que tu función se vea algo similar a la función interna +`encontrar` en la función recursiva `encontrarSolucion` de +[ejemplo](funciones#recursive_puzzle) en este capítulo, con una cadena +`if`/`else if`/`else` que prueba cuál de los tres casos +aplica. El `else` final, correspondiente al tercer caso, hace la +llamada recursiva. Cada una de las ramas debe contener una declaración +de `return` u organizarse de alguna otra manera para que un valor específico +sea retornado. {{index "stack overflow"}} -When given a negative number, the function will recurse again and -again, passing itself an ever more negative number, thus getting -further and further away from returning a result. It will eventually -run out of stack space and abort. +Cuando se le dé un número negativo, la función volverá a repetirse una y +otra vez, pasándose a si misma un número cada vez más negativo, quedando así +más y más lejos de devolver un resultado. Eventualmente +quedandose sin espacio en la pila y abortando el programa. hint}} -### Bean counting +### Conteo de frijoles {{index "bean counting (exercise)", [string, indexing], "zero-based counting", ["length property", "for string"]}} -You can get the Nth character, or letter, from a string by writing -`"string"[N]`. The returned value will be a string containing only one -character (for example, `"b"`). The first character has position 0, -which causes the last one to be found at position `string.length - 1`. -In other words, a two-character string has length 2, and its -characters have positions 0 and 1. +Puedes obtener el N-ésimo carácter, o letra, de un string escribiendo +`"string"[N]`. El valor devuelto será un string que contiene solo un +carácter (por ejemplo, `"f"`). El primer carácter tiene posición cero, +lo que hace que el último se encuentre en la posición `string.length - 1`. +En otras palabras, un string de dos caracteres tiene una longitud de 2, +y sus carácteres tendrán las posiciones 0 y 1. -Write a function `countBs` that takes a string as its only argument -and returns a number that indicates how many uppercase "B" characters -there are in the string. +Escribe una función `contarFs` que tome un string como su único argumento +y devuelva un número que indica cuántos caracteres "F" en mayúsculas +haya en el string. -Next, write a function called `countChar` that behaves like `countBs`, -except it takes a second argument that indicates the character that is -to be counted (rather than counting only uppercase "B" characters). -Rewrite `countBs` to make use of this new function. +Despues, escribe una función llamada `contarCaracteres` que se comporte +como `contarFs`, excepto que toma un segundo argumento que indica el carácter +que debe ser contado (en lugar de contar solo caracteres "F" en mayúscula). +Reescribe `contarFs` para que haga uso de esta nueva función. {{if interactive ```{test: no} -// Your code here. +// Tu código aquí. -console.log(countBs("BBC")); +console.log(contarFs("FFC")); // → 2 -console.log(countChar("kakkerlak", "k")); +console.log(contarCaracteres("kakkerlak", "k")); // → 4 ``` @@ -1097,15 +1106,15 @@ if}} {{index "bean counting (exercise)", ["length property", "for string"], "counter variable"}} -Your function will need a ((loop)) that looks at every character in -the string. It can run an index from zero to one below its length (`< -string.length`). If the character at the current position is the same -as the one the function is looking for, it adds 1 to a counter -variable. Once the loop has finished, the counter can be returned. +TU función necesitará de un ((ciclo)) que examine cada carácter en +el string. Puede correr desde un índice de cero a uno por debajo de su longitud (`< +string.length`). Si el carácter en la posición actual es el mismo +al que se está buscando en la función, agrega 1 a una variable contador. +Una vez que el ciclo haya terminado, puedes retornat el contador. {{index "local binding"}} -Take care to make all the bindings used in the function _local_ to the -function by properly declaring them with the `let` or `const` keyword. +Ten cuidado de hacer que todos las vinculaciones utilizadas en la función sean +_locales_ a la función usando la palabra clave `let` o `const`. hint}} diff --git a/04_data.md b/04_data.md index 15b5768bc..f1170af6b 100644 --- a/04_data.md +++ b/04_data.md @@ -1,13 +1,13 @@ {{meta {load_files: ["code/journal.js", "code/chapter/04_data.js"], zip: "node/html"}}} -# Data Structures: Objects and Arrays +# Estructuras de Datos: Objetos y Arrays {{quote {author: "Charles Babbage", title: "Passages from the Life of a Philosopher (1864)", chapter: true} -On two occasions I have been asked, 'Pray, Mr. Babbage, if you put -into the machine wrong figures, will the right answers come out?' -[...] I am not able rightly to apprehend the kind of confusion of -ideas that could provoke such a question. +En dos ocasiones me han preguntado, 'Dinos, Sr. Babbage, si pones +montos equivocadas en la máquina, saldrán las respuestas correctas? +[...] No soy capaz de comprender correctamente el tipo de confusión de +ideas que podrían provocar tal pregunta. quote}} @@ -17,133 +17,135 @@ quote}} {{index object, "data structure"}} -Numbers, Booleans, and strings are the atoms that ((data)) structures -are built from. Many types of information require more than one -atom, though. _Objects_ allow us to group values—including other -objects—to build more complex structures. +Los números, los booleanos y los strings son los átomos que constituyen las +estructuras de ((datos)). Sin embargo, muchos tipos de información +requieren más de un átomo. Los _objetos_ nos permiten agrupar valores—incluidos +otros objetos— para construir estructuras más complejas. -The programs we have built so far have been limited by the fact that -they were operating only on simple data types. This chapter will -introduce basic data structures. By the end of it, you'll know enough -to start writing useful programs. +Los programas que hemos construido hasta ahora han estado limitados por el +hecho de que estaban operando solo en tipos de datos simples. Este capítulo +introducira estructuras de datos básicas. Al final de el, sabrás lo suficiente +como para comenzar a escribir programas útiles. -The chapter will work through a more or less realistic programming -example, introducing concepts as they apply to the problem at hand. -The example code will often build on functions and bindings that were -introduced earlier in the text. +El capítulo trabajara a través de un ejemplo de programación más o menos +realista, presentando nuevos conceptos según se apliquen al problema en +cuestión. El código de ejemplo a menudo se basara en funciones y vinculaciones +que fueron introducidas anteriormente en el texto. {{if book -The online coding ((sandbox)) for the book -([_https://eloquentjavascript.net/code_](https://eloquentjavascript.net/code)) -provides a way to run code in the context of a specific chapter. If -you decide to work through the examples in another environment, be -sure to first download the full code for this chapter from the sandbox -page. +La ((caja de arena)) en línea para el libro +([_eloquentjavascript.net/code_](https://eloquentjavascript.net/code)] +proporciona una forma de ejecutar código en el contexto de un capítulo en +específico. Si decides trabajar con los ejemplos en otro entorno, +asegúrate de primero descargar el código completo de este capítulo +desde la página de la caja de arena. if}} -## The weresquirrel +## El Hombre Ardilla {{index "weresquirrel example", lycanthropy}} -Every now and then, usually between 8 p.m. and 10 p.m., -((Jacques)) finds himself transforming into a small furry rodent with -a bushy tail. - -On one hand, Jacques is quite glad that he doesn't have classic -lycanthropy. Turning into a squirrel does cause fewer problems than -turning into a wolf. Instead of having to worry about accidentally -eating the neighbor (_that_ would be awkward), he worries about being -eaten by the neighbor's cat. After two occasions where he woke up on a -precariously thin branch in the crown of an oak, naked and -disoriented, he has taken to locking the doors and windows of his room -at night and putting a few walnuts on the floor to keep himself busy. - -That takes care of the cat and tree problems. But Jacques would prefer -to get rid of his condition entirely. The irregular occurrences of the -transformation make him suspect that they might be triggered by -something. For a while, he believed that it happened only on days when -he had been near oak trees. But avoiding oak trees did not stop the -problem. +De vez en cuando, generalmente entre las ocho y las diez de la noche, +((Jacques)) se encuentra a si mismo +transformándose en un pequeño roedor peludo con una cola espesa. + +Por un lado, Jacques está muy contento de no tener la licantropía clásica. +Convertirse en una ardilla causa menos problemas que convertirse en un lobo. +En lugar de tener que preocuparse por accidentalmente comerse al vecino +(_eso_ sería incómodo), le preocupa ser comido por el gato del vecino. +Después de dos ocasiones en las que se despertó en una rama precariamente +delgada de la copa de un roble, desnudo y desorientado, Jacques se ha dedicado +a bloquear las puertas y ventanas de su habitación por la noche y +pone algunas nueces en el piso para mantenerse ocupado. + +Eso se ocupa de los problemas del gato y el árbol. Pero Jacques preferiría +deshacerse de su condición por completo. Las ocurrencias irregulares de la +transformación lo hacen sospechar que estas podrían ser provocadas por +algo en especifico. Por un tiempo, creyó que solo sucedia en los días +en los que el había estado cerca de árboles de roble. Pero evitar los robles +no detuvo el problema. {{index journal}} -Switching to a more scientific approach, Jacques has started keeping a -daily log of everything he does on a given day and whether he changed -form. With this data he hopes to narrow down the conditions that -trigger the transformations. +Cambiando a un enfoque más científico, Jacques ha comenzado a mantener un +registro diario de todo lo que hace en un día determinado y si su forma +cambio. Con esta información el espera reducir las condiciones que +desencadenan las transformaciones. -The first thing he needs is a data structure to store this -information. +Lo primero que el necesita es una estructura de datos para almacenar esta +información. -## Data sets +## Conjuntos de datos -{{index ["data structure", collection], [memory, organization]}} +{{index "data structure"}} -To work with a chunk of digital data, we'll first have to find a way -to represent it in our machine's memory. Say, for example, that we -want to represent a ((collection)) of the numbers 2, 3, 5, 7, and 11. +Para trabajar con una porción de datos digitales, primero debemos encontrar +una manera de representarlo en la ((memoria)) de nuestra máquina. Digamos, +por ejemplo, que queremos representar una ((colección)) de los números +2, 3, 5, 7 y 11. {{index string}} -We could get creative with strings—after all, strings can have any -length, so we can put a lot of data into them—and use `"2 3 5 7 11"` -as our representation. But this is awkward. You'd have to somehow -extract the digits and convert them back to numbers to access them. +Podríamos ponernos creativos con los strings—después de todo, los strings pueden +tener cualquier longitud, por lo que podemos poner una gran cantidad de +datos en ellos—y usar `"2 3 5 7 11"` como nuestra representación. +Pero esto es incómodo. Tendrías que extraer los dígitos de alguna manera +y convertirlos a números para acceder a ellos. {{index [array, creation], "[] (array)"}} -Fortunately, JavaScript provides a data type specifically for storing -sequences of values. It is called an _array_ and is written as a list -of values between ((square brackets)), separated by commas. +Afortunadamente, JavaScript proporciona un tipo de datos específicamente +para almacenar secuencias de valores. Es llamado _array_ y está escrito +como una lista de valores entre ((corchetes)), separados por comas. ``` -let listOfNumbers = [2, 3, 5, 7, 11]; -console.log(listOfNumbers[2]); +let listaDeNumeros = [2, 3, 5, 7, 11]; +console.log(listaDeNumeros[2]); // → 5 -console.log(listOfNumbers[0]); +console.log(listaDeNumeros[0]); // → 2 -console.log(listOfNumbers[2 - 1]); +console.log(listaDeNumeros[2 - 1]); // → 3 ``` {{index "[] (subscript)", [array, indexing]}} -The notation for getting at the elements inside an array also uses -((square brackets)). A pair of square brackets immediately after an -expression, with another expression inside of them, will look up the -element in the left-hand expression that corresponds to the -_((index))_ given by the expression in the brackets. +La notación para llegar a los elementos dentro de un array también utiliza +((corchetes)). Un par de corchetes inmediatamente después de una +expresión, con otra expresión dentro de ellos, buscará al +elemento en la expresión de la izquierda que corresponde al +_((índice))_ dado por la expresión entre corchetes. {{id array_indexing}} {{index "zero-based counting"}} -The first index of an array is zero, not one. So the first element is -retrieved with `listOfNumbers[0]`. Zero-based counting has a long -tradition in technology and in certain ways makes a lot of sense, but -it takes some getting used to. Think of the index as the amount of -items to skip, counting from the start of the array. +El primer índice de un array es cero, no uno. Entonces el primer elemento es +alcanzado con `listaDeNumeros[0]`. El conteo basado en cero tiene una larga +tradición en el mundo de la tecnología, y en ciertas maneras tiene mucho +sentido, pero toma algo de tiempo acostumbrarse. Piensa en el índice como +la cantidad de elementos a saltar, contando desde el comienzo del array. {{id properties}} -## Properties +## Propiedades -{{index "Math object", "Math.max function", ["length property", "for string"], [object, property], "period character", [property, access]}} +{{index "Math object", "Math.max function", ["length property", "for string"], [object, property], "period character"}} -We've seen a few suspicious-looking expressions like `myString.length` -(to get the length of a string) and `Math.max` (the maximum function) -in past chapters. These are expressions that access a _property_ -of some value. In the first case, we access the `length` property of -the value in `myString`. In the second, we access the property named -`max` in the `Math` object (which is a collection of -mathematics-related constants and functions). +Hasta ahora hemos visto algunas expresiones sospechosas como `miString.length` +(para obtener la longitud de un string) y `Math.max` (la función máxima) +en capítulos anteriores. Estas son expresiones que acceden a la _((propiedad))_ +de algún valor. En el primer caso, accedemos a la propiedad `length` de +el valor en `miString`. En el segundo, accedemos a la propiedad llamada +`max` en el objeto `Math` (que es una colección de +constantes y funciones relacionadas con las matemáticas). -{{index [property, access], null, undefined}} +{{index property, null, undefined}} -Almost all JavaScript values have properties. The exceptions are -`null` and `undefined`. If you try to access a property on one of -these nonvalues, you get an error. +Casi todos los valores de JavaScript tienen propiedades. Las excepciones son +`null` y `undefined`. Si intentas acceder a una propiedad en alguno de +estos no-valores, obtienes un error. ```{test: no} null.length; @@ -151,400 +153,408 @@ null.length; ``` {{indexsee "dot character", "period character"}} -{{index "[] (subscript)", "period character", "square brackets", "computed property", [property, access]}} - -The two main ways to access properties in JavaScript are with a dot -and with square brackets. Both `value.x` and `value[x]` access a -property on `value`—but not necessarily the same property. The -difference is in how `x` is interpreted. When using a dot, the word -after the dot is the literal name of the property. When using square -brackets, the expression between the brackets is _evaluated_ to get -the property name. Whereas `value.x` fetches the property of `value` -named "x", `value[x]` tries to evaluate the expression `x` and uses -the result, converted to a string, as the property name. - -So if you know that the property you are interested in is called -_color_, you say `value.color`. If you want to extract the property -named by the value held in the binding `i`, you say `value[i]`. -Property names are strings. They can be any string, but the dot notation works only with -names that look like valid binding names. So if you want to access a -property named _2_ or _John Doe_, you must use square brackets: -`value[2]` or `value["John Doe"]`. - -The elements in an ((array)) are stored as the array's properties, using -numbers as property names. Because you can't use the dot notation with -numbers and usually want to use a binding that holds the index -anyway, you have to use the bracket notation to get at them. +{{index "[] (subscript)", "period character", "square brackets", "computed property"}} + +Las dos formas principales de acceder a las propiedades en JavaScript son +con un punto y con corchetes. Tanto `valor.x` como `valor[x]` acceden una +((propiedad)) en `valor`—pero no necesariamente la misma propiedad. La +diferencia está en cómo se interpreta `x`. Cuando se usa un punto, la palabra +después del punto es el nombre literal de la propiedad. Cuando usas +corchetes, la expresión entre corchetes es _evaluada_ para obtener +el nombre de la propiedad. Mientras `valor.x` obtiene la propiedad de `valor` +llamada "x", `valor[x]` intenta evaluar la expresión `x` y usa +el resultado, convertido en un string, como el nombre de la propiedad. + +Entonces, si sabes que la propiedad que te interesa se llama +_color_, dices `valor.color`. Si quieres extraer la propiedad +nombrado por el valor mantenido en la vinculación `i`, dices `valor[i]`. +Los nombres de las propiedades son strings. Pueden ser cualquier string, +pero la notación de puntos solo funciona con nombres que se vean como nombres +de vinculaciones válidos. Entonces, si quieres acceder a una +propiedad llamada _2_ o _Juan Perez_, debes usar corchetes: +`valor[2]` o `valor["Juan Perez"]`. + +Los elementos en un ((array)) son almacenados como propiedades del array, +usando números como nombres de propiedad. Ya que no puedes usar la notación +de puntos con números, y que generalmente quieres utilizar una vinculación +que contenga el índice de cualquier manera, +debes de usar la notación de corchetes para llegar a ellos. {{index ["length property", "for array"], [array, "length of"]}} -The `length` property of an array tells us how many elements it has. -This property name is a valid binding name, and we know its name in -advance, so to find the length of an array, you typically write -`array.length` because that's easier to write than `array["length"]`. +La propiedad `length` de un array nos dice cuántos elementos este tiene. +Este nombre de propiedad es un nombre de vinculación válido, y +sabemos su nombre en avance, así que para encontrar la longitud de un array, +normalmente escribes `array.length` ya que es más fácil de escribir que +`array["length"]`. {{id methods}} -## Methods +## Métodos {{index [function, "as property"], method, string}} -Both string and array objects contain, in addition to the `length` -property, a number of properties that hold function values. +Ambos objetos de string y array contienen, además de la propiedad +`length`, una serie de propiedades que tienen valores de función. ``` -let doh = "Doh"; -console.log(typeof doh.toUpperCase); +let ouch = "Ouch"; +console.log(typeof ouch.toUpperCase); // → function -console.log(doh.toUpperCase()); -// → DOH +console.log(ouch.toUpperCase()); +// → OUCH ``` {{index "case conversion", "toUpperCase method", "toLowerCase method"}} -Every string has a `toUpperCase` property. When called, it will return -a copy of the string in which all letters have been converted to -uppercase. There is also `toLowerCase`, going the other way. +Cada string tiene una propiedad `toUpperCase` ("a mayúsculas"). +Cuando se llame, regresará una copia del string en la que todas las +letras han sido convertido a mayúsculas. También hay `toLowerCase` +("a minúsculas"), que hace lo contrario. -{{index "this binding"}} +{{index this}} -Interestingly, even though the call to `toUpperCase` does not pass any -arguments, the function somehow has access to the string `"Doh"`, the -value whose property we called. How this works is described in -[Chapter ?](object#obj_methods). +Curiosamente, a pesar de que la llamada a `toUpperCase` no pasa ningún +argumento, la función de alguna manera tiene acceso al string `"Ouch"`, el +valor de cuya propiedad llamamos. Cómo funciona esto se describe en el +[Capítulo 6](objeto#metodos_de_objeto). -Properties that contain functions are generally called _methods_ of -the value they belong to, as in "`toUpperCase` is a method of a +Las propiedades que contienen funciones generalmente son llamadas _metodos_ +del valor al que pertenecen. Como en, "`toUpperCase` es un método de string". {{id array_methods}} -This example demonstrates two methods you can use to manipulate +Este ejemplo demuestra dos métodos que puedes usar para manipular arrays: ``` -let sequence = [1, 2, 3]; -sequence.push(4); -sequence.push(5); -console.log(sequence); +let secuencia = [1, 2, 3]; +secuencia.push(4); +secuencia.push(5); +console.log(secuencia); // → [1, 2, 3, 4, 5] -console.log(sequence.pop()); +console.log(secuencia.pop()); // → 5 -console.log(sequence); +console.log(secuencia); // → [1, 2, 3, 4] ``` {{index collection, array, "push method", "pop method"}} -The `push` method adds values to the end of an array, and the -`pop` method does the opposite, removing the last value in the array -and returning it. +El método `push` agrega valores al final de un array, y el +el método `pop` hace lo contrario, eliminando el último valor en el array +y retornandolo. -{{index ["data structure", stack]}} +Estos nombres algo tontos son los términos tradicionales para las operaciones en +una _((pila))_. Una pila, en programación, es una ((estructura de datos)) que +te permite agregar valores a ella y volverlos a sacar en el +orden opuesto, de modo que lo que se agregó de último se elimine primero. +Estas son comunes en la programación—es posible que recuerdes la ((pila)) +de llamadas en [el capítulo anterior](funciones#pila), que es una +instancia de la misma idea. -These somewhat silly names are the traditional terms for operations on -a _((stack))_. A stack, in programming, is a data structure that -allows you to push values into it and pop them out again in the -opposite order so that the thing that was added last is removed first. -These are common in programming—you might remember the function ((call -stack)) from [the previous chapter](functions#stack), which is an -instance of the same idea. - -## Objects +## Objetos {{index journal, "weresquirrel example", array, record}} -Back to the weresquirrel. A set of daily log entries can be -represented as an array. But the entries do not consist of just a -number or a string—each entry needs to store a list of activities and -a Boolean value that indicates whether Jacques turned into a squirrel -or not. Ideally, we would like to group these together into a single -value and then put those grouped values into an array of log entries. +De vuelta al Hombre-Ardilla. Un conjunto de entradas diarias puede ser +representado como un array. Pero estas entradas no consisten en solo un +número o un string—cada entrada necesita almacenar una lista de actividades y +un valor booleano que indica si Jacques se convirtió en una ardilla +o no. Idealmente, nos gustaría agrupar estos en un solo +valor y luego agrupar estos valores en un array de registro de entradas. -{{index [syntax, object], [property, definition], [braces, object], "{} (object)"}} +{{index syntax, property, "curly braces", "{} (object)"}} -Values of the type _((object))_ are arbitrary collections of -properties. One way to create an object is by using braces as an -expression. +Los valores del tipo _((objeto))_ son colecciones arbitrarias de +propiedades. Una forma de crear un objeto es mediante el uso de llaves +como una expresión. ``` -let day1 = { - squirrel: false, - events: ["work", "touched tree", "pizza", "running"] +let dia1 = { + ardilla: false, + eventos: ["trabajo", "toque un arbol", "pizza", "salir a correr"] }; -console.log(day1.squirrel); +console.log(dia1.ardilla); // → false -console.log(day1.wolf); +console.log(dia1.lobo); // → undefined -day1.wolf = false; -console.log(day1.wolf); +dia1.lobo = false; +console.log(dia1.lobo); // → false ``` {{index [quoting, "of object properties"], "colon character"}} -Inside the braces, there is a list of properties separated by commas. -Each property has a name followed by a colon and a value. When an -object is written over multiple lines, indenting it like in the -example helps with readability. Properties whose names aren't valid -binding names or valid numbers have to be quoted. +Dentro de las llaves, hay una lista de propiedades separadas por comas. +Cada propiedad tiene un nombre seguido de dos puntos y un valor. Cuando un +objeto está escrito en varias líneas, indentar como en el +ejemplo ayuda con la legibilidad. Las propiedades cuyos nombres no sean +nombres válidos de vinculaciones o números válidos deben estar entre comillas. ``` -let descriptions = { - work: "Went to work", - "touched tree": "Touched a tree" +let descripciones = { + trabajo: "Fui a trabajar", + "toque un arbol": "Toque un arbol" }; ``` -{{index [braces, object]}} - -This means that braces have _two_ meanings in JavaScript. At -the start of a ((statement)), they start a ((block)) of statements. In -any other position, they describe an object. Fortunately, it is rarely -useful to start a statement with an object in braces, so the -ambiguity between these two is not much of a problem. +Esto significa que las ((llaves)) tienen _dos_ significados en JavaScript. Al +comienzo de una ((declaración)), comienzan un ((bloque)) de declaraciones. En +cualquier otra posición, describen un objeto. Afortunadamente, es raramente +útil comenzar una declaración con un objeto en llaves, por lo que +la ambigüedad entre estas dos acciones no es un gran problema. {{index undefined}} -Reading a property that doesn't exist will give you the value -`undefined`. +Leer una propiedad que no existe te dará el valor `undefined`. {{index [property, assignment], mutability, "= operator"}} -It is possible to assign a value to a property expression with the `=` -operator. This will replace the property's value if it already existed -or create a new property on the object if it didn't. +Es posible asignarle un valor a una expresión de propiedad con un +operador `=`. Esto reemplazará el valor de la propiedad si ya tenia uno +o crea una nueva propiedad en el objeto si no fuera así. -{{index "tentacle (analogy)", [property, "model of"], [binding, "model of"]}} +{{index "tentacle (analogy)", [property, "model of"]}} -To briefly return to our tentacle model of ((binding))s—property -bindings are similar. They _grasp_ values, but other bindings and -properties might be holding onto those same values. You may think of -objects as octopuses with any number of tentacles, each of which has a -name tattooed on it. +Para volver brevemente a nuestro modelo de ((vinculaciones)) como +tentáculos—Las vinculaciones de propiedad son similares. Ellas _agarran_ +valores, pero otras vinculaciones y propiedades pueden estar agarrando +esos mismos valores. Puedes pensar en los objetos como pulpos con cualquier +cantidad de tentáculos, cada uno de los cuales tiene un nombre tatuado en él. {{index "delete operator", [property, deletion]}} -The `delete` operator cuts off a tentacle from such an octopus. It is -a unary operator that, when applied to an object property, -will remove the named property from the object. This is not a common -thing to do, but it is possible. +El operador `delete` ("eliminar") corta un tentáculo de dicho pulpo. Es +un operador unario que, cuando se aplica a la propiedad de un objeto, +eliminará la propiedad nombrada de dicho objeto. Esto no es algo que hagas +todo el tiempo, pero es posible. ``` -let anObject = {left: 1, right: 2}; -console.log(anObject.left); +let unObjeto = {izquierda: 1, derecha: 2}; +console.log(unObjeto.izquierda); // → 1 -delete anObject.left; -console.log(anObject.left); +delete unObjeto.izquierda; +console.log(unObjeto.izquierda); // → undefined -console.log("left" in anObject); +console.log("izquierda" in unObjeto); // → false -console.log("right" in anObject); +console.log("derecha" in unObjeto); // → true ``` {{index "in operator", [property, "testing for"], object}} -The binary `in` operator, when applied to a string and an object, -tells you whether that object has a property with that name. The difference -between setting a property to `undefined` and actually deleting it is -that, in the first case, the object still _has_ the property (it just -doesn't have a very interesting value), whereas in the second case the -property is no longer present and `in` will return `false`. +El operador binario `in` ("en"), cuando se aplica a un string y un objeto, +te dice si ese objeto tiene una propiedad con ese nombre. La diferencia +entre darle un valor de `undefined` a una propiedad y eliminarla realmente es +que, en el primer caso, el objeto todavía _tiene_ la propiedad (solo que +no tiene un valor muy interesante), mientras que en el segundo caso +la propiedad ya no está presente e `in` retornara `false`. {{index "Object.keys function"}} -To find out what properties an object has, you can use the -`Object.keys` function. You give it an object, and it returns an array -of strings—the object's property names. +Para saber qué propiedades tiene un objeto, puedes usar la función +`Object.keys`. Le das un objeto y devuelve un array +de strings—los nombres de las propiedades del objeto. ``` console.log(Object.keys({x: 0, y: 0, z: 2})); // → ["x", "y", "z"] ``` -There's an `Object.assign` function that copies all properties from -one object into another. +Hay una función `Object.assign` que copia todas las propiedades de +un objeto a otro. ``` -let objectA = {a: 1, b: 2}; -Object.assign(objectA, {b: 3, c: 4}); -console.log(objectA); +let objetoA = {a: 1, b: 2}; +Object.assign(objetoA, {b: 3, c: 4}); +console.log(objetoA); // → {a: 1, b: 3, c: 4} ``` {{index array, collection}} -Arrays, then, are just a kind of object specialized for storing -sequences of things. If you evaluate `typeof []`, it produces -`"object"`. You can see them as long, flat octopuses with all their -tentacles in a neat row, labeled with numbers. +Los arrays son, entonces, solo un tipo de objeto especializado para almacenar +secuencias de cosas. Si evalúas `typeof []`, este produce +`"object"`. Podrias imaginarlos como pulpos largos y planos con todos sus +tentáculos en una fila ordenada, etiquetados con números. {{index journal, "weresquirrel example"}} -We will represent the journal that Jacques keeps as an array of objects. +Representaremos el diario de Jacques como un array de objetos. ```{test: wrap} -let journal = [ - {events: ["work", "touched tree", "pizza", - "running", "television"], - squirrel: false}, - {events: ["work", "ice cream", "cauliflower", - "lasagna", "touched tree", "brushed teeth"], - squirrel: false}, - {events: ["weekend", "cycling", "break", "peanuts", - "beer"], - squirrel: true}, - /* and so on... */ +let diario = [ + {eventos: ["trabajo", "toque un arbol", "pizza", + "sali a correr", "television"], + ardilla: false}, + {eventos: ["trabajo", "helado", "coliflor", + "lasaña", "toque un arbol", "me cepille los dientes"], + ardilla: false}, + {eventos: ["fin de semana", "monte la bicicleta", "descanso", "nueces", + "cerveza"], + ardilla: true}, + /* y asi sucesivamente... */ ]; ``` -## Mutability +## Mutabilidad -We will get to actual programming _real_ soon now. First there's one -more piece of theory to understand. +Llegaremos a la programación real _pronto_. Pero primero, hay una pieza +más de teoría por entender. -{{index mutability, "side effect", number, string, Boolean, [object, mutability]}} +{{index mutability, "side effect", number, string, Boolean, object}} -We saw that object values can be modified. The types of values -discussed in earlier chapters, such as numbers, strings, and Booleans, -are all _((immutable))_—it is impossible to change values of those -types. You can combine them and derive new values from them, but when -you take a specific string value, that value will always remain the -same. The text inside it cannot be changed. If you have a string that -contains `"cat"`, it is not possible for other code to change a -character in your string to make it spell `"rat"`. +Vimos que los valores de objeto pueden ser modificados. Los tipos de valores +discutidos en capítulos anteriores, como números, strings y booleanos, +son todos _((inmutables))_—es imposible cambiar los valores de aquellos +tipos. Puedes combinarlos y obtener nuevos valores a partir de ellos, pero cuando +tomas un valor de string específico, ese valor siempre será el +mismo. El texto dentro de él no puede ser cambiado. Si tienes un string que +contiene `"gato"`, no es posible que otro código cambie un +carácter en tu string para que deletree `"rato"`. -Objects work differently. You _can_ change their properties, -causing a single object value to have different content at different times. +Los objetos funcionan de una manera diferente. Tu _puedes_ cambiar sus +propiedades, haciendo que un único valor de objeto tenga contenido diferente en +diferentes momentos. -{{index [object, identity], identity, [memory, organization], mutability}} +{{index [object, identity], identity, memory, mutability}} -When we have two numbers, 120 and 120, we can consider them precisely -the same number, whether or not they refer to the same physical bits. -With objects, there is a difference between having two references to -the same object and having two different objects that contain the same -properties. Consider the following code: +Cuando tenemos dos números, 120 y 120, podemos considerarlos el mismo número +precisamente, ya sea que hagan referencia o no a los mismos bits físicos. +Con los objetos, hay una diferencia entre tener dos referencias a +el mismo objeto y tener dos objetos diferentes que contengan las mismas +propiedades. Considera el siguiente código: ``` -let object1 = {value: 10}; -let object2 = object1; -let object3 = {value: 10}; +let objeto1 = {valor: 10}; +let objeto2 = objeto1; +let objeto3 = {valor: 10}; -console.log(object1 == object2); +console.log(objeto1 == objeto2); // → true -console.log(object1 == object3); +console.log(objeto1 == objeto3); // → false -object1.value = 15; -console.log(object2.value); +objeto1.valor = 15; +console.log(objeto2.valor); // → 15 -console.log(object3.value); +console.log(objeto3.valor); // → 10 ``` {{index "tentacle (analogy)", [binding, "model of"]}} -The `object1` and `object2` bindings grasp the _same_ object, which is -why changing `object1` also changes the value of `object2`. They are -said to have the same _identity_. The binding `object3` points to a -different object, which initially contains the same properties as -`object1` but lives a separate life. +Las vinculaciones `objeto1` y `objeto2` agarran el _mismo_ objeto, que es +la razon por la cual cambiar `objeto1` también cambia el valor de `objeto2`. +Se dice que tienen la misma _identidad_. La vinculación `objeto3` apunta a un +objeto diferente, que inicialmente contiene las mismas propiedades que +`objeto1` pero vive una vida separada. -{{index "const keyword", "let keyword", [binding, "as state"]}} +{{index "const keyword", "let keyword"}} -Bindings can also be changeable or constant, but this is separate from -the way their values behave. Even though number values don't change, -you can use a `let` binding to keep track of a changing number by -changing the value the binding points at. Similarly, though a `const` -binding to an object can itself not be changed and will continue to -point at the same object, the _contents_ of that object might change. +Las vinculaciones también pueden ser cambiables o constantes, pero esto es +independiente de la forma en la que se comportan sus valores. Aunque los +valores numéricos no cambian, puedes usar una ((vinculación)) `let` para hacer +un seguimiento de un número que cambia al cambiar el valor al que apunta la +vinculación. Del mismo modo, aunque una vinculación `const` a un objeto no +pueda ser cambiada en si misma y continuará apuntando al mismo objeto, +los _contenidos_ de ese objeto pueden cambiar. ```{test: no} -const score = {visitors: 0, home: 0}; -// This is okay -score.visitors = 1; -// This isn't allowed -score = {visitors: 1, home: 1}; +const puntuacion = {visitantes: 0, locales: 0}; +// Esto esta bien +puntuacion.visitantes = 1; +// Esto no esta permitido +puntuacion = {visitantes: 1, locales: 1}; ``` {{index "== operator", [comparison, "of objects"], "deep comparison"}} -When you compare objects with JavaScript's `==` operator, it compares -by identity: it will produce `true` only if both objects are precisely -the same value. Comparing different objects will return `false`, even -if they have identical properties. There is no "deep" comparison -operation built into JavaScript, which compares objects by contents, -but it is possible to write it yourself (which is one of the -[exercises](data#exercise_deep_compare) at the end of this chapter). +Cuando comparas objetos con el operador `==` en JavaScript, este los compara +por identidad: producirá `true` solo si ambos objetos son precisamente +el mismo valor. Comparar diferentes objetos retornara `false`, incluso +si tienen propiedades idénticas. No hay una operación de comparación "profunda" +incorporada en JavaScript, que compare objetos por contenidos, +pero es posible que la escribas tu mismo (que es uno de los +[ejercicios](datos#ejercicio_comparacion_profunda) al final de este capítulo). -## The lycanthrope's log +## El diario del licántropo {{index "weresquirrel example", lycanthropy, "addEntry function"}} -So, Jacques starts up his JavaScript interpreter and sets up the -environment he needs to keep his ((journal)). +Asi que Jacques inicia su intérprete de JavaScript y establece el +entorno que necesita para mantener su ((diario)). ```{includeCode: true} -let journal = []; +let diario = []; -function addEntry(events, squirrel) { - journal.push({events, squirrel}); +function añadirEntrada(eventos, ardilla) { + diario.push({eventos, ardilla}); } ``` -{{index [braces, object], "{} (object)", [property, definition]}} +{{index "curly braces", "{} (object)"}} + +Ten en cuenta que el objeto agregado al diario se ve un poco extraño. En lugar +de declarar propiedades como `eventos: eventos`, simplemente da un +nombre de ((propiedad)). Este es un atajo que representa lo mismo—si el +nombre de propiedad en la notación de llaves no es seguido por un valor, su +el valor se toma de la vinculación con el mismo nombre. + +Entonces, todas las noches a las diez—o algunas veces a la mañana siguiente, +después de bajar del estante superior de su biblioteca—Jacques registra el +día. -Note that the object added to the journal looks a little odd. Instead -of declaring properties like `events: events`, it just gives a -property name. This is shorthand that means the same thing—if a -property name in brace notation isn't followed by a value, its -value is taken from the binding with the same name. -So then, every evening at 10 p.m.—or sometimes the next morning, after +So then, every evening at ten—or sometimes the next morning, after climbing down from the top shelf of his bookcase—Jacques records the day. ``` -addEntry(["work", "touched tree", "pizza", "running", +añadirEntrada(["trabajo", "toque un arbol", "pizza", "sali a correr", "television"], false); -addEntry(["work", "ice cream", "cauliflower", "lasagna", - "touched tree", "brushed teeth"], false); -addEntry(["weekend", "cycling", "break", "peanuts", - "beer"], true); +añadirEntrada(["trabajo", "helado", "coliflor", "lasaña", + "toque un arbol", "me cepille los dientes"], false); +añadirEntrada(["fin de semana", "monte la bicicleta", "descanso", "nueces", + "cerveza"], true); ``` -Once he has enough data points, he intends to use statistics to find -out which of these events may be related to the squirrelifications. +Una vez que tiene suficientes puntos de datos, tiene la intención de utilizar +estadísticas para encontrar cuál de estos eventos puede estar +relacionado con la transformación a ardilla. {{index correlation}} -_Correlation_ is a measure of ((dependence)) between statistical -variables. A statistical variable is not quite the same as a -programming variable. In statistics you typically have a set of -_measurements_, and each variable is measured for every measurement. -Correlation between variables is usually expressed as a value that -ranges from -1 to 1. Zero correlation means the variables are not -related. A correlation of one indicates that the two are perfectly -related—if you know one, you also know the other. Negative one also -means that the variables are perfectly related but that they are -opposites—when one is true, the other is false. +La _correlación_ es una medida de ((dependencia)) entre variables estadísticas. +Una variable estadística no es lo mismo que una variable de programación. +En las estadísticas, normalmente tienes un conjunto de _medidas_, +y cada variable se mide para cada medida. La correlación entre variables +generalmente se expresa como un valor que +varia de -1 a 1. Una correlación de cero significa que las variables no estan +relacionadas. Una correlación de uno indica que las dos están perfectamente +relacionadas—si conoces una, también conoces la otra. Uno negativo también +significa que las variables están perfectamente relacionadas pero que son +opuestas—cuando una es verdadera, la otra es falsa. {{index "phi coefficient"}} -To compute the measure of correlation between two Boolean variables, -we can use the _phi coefficient_ (_ϕ_). This is a formula whose input -is a ((frequency table)) containing the number of times the different -combinations of the variables were observed. The output of the formula -is a number between -1 and 1 that describes the correlation. +Para calcular la medida de correlación entre dos variables booleanas, +podemos usar el _coeficiente phi_ (_ϕ_). Esta es una fórmula cuya entrada +es una ((tabla de frecuencias)) que contiene la cantidad de veces que +las diferentes combinaciones de las variables fueron observadas. +El resultado de la fórmula es un número entre -1 y 1 que describe +la correlación. -We could take the event of eating ((pizza)) and put that in a -frequency table like this, where each number indicates the amount of -times that combination occurred in our measurements: +Podríamos tomar el evento de comer ((pizza)) y poner eso en una +tabla de frecuencias como esta, donde cada número indica la cantidad de +veces que ocurrió esa combinación en nuestras mediciones: {{figure {url: "img/pizza-squirrel.svg", alt: "Eating pizza versus turning into a squirrel", width: "7cm"}}} -If we call that table _n_, we can compute _ϕ_ using the following formula: +Si llamamos a esa tabla _n_, podemos calcular _ϕ_ usando la siguiente fórmula: {{if html @@ -569,63 +579,63 @@ if}} if}} -(If at this point you're putting the book down to focus on a terrible -flashback to 10th grade math class—hold on! I do not intend to torture -you with endless pages of cryptic notation—it's just this one formula for -now. And even with this one, all we do is turn it into JavaScript.) +(Si en este momento estas bajando el libro para enfocarte en un terrible +flashback a la clase de matemática de 10° grado—espera! +No tengo la intención de torturarte con infinitas páginas de notación +críptica—solo esta fórmula para ahora. E incluso con esta, +todo lo que haremos es convertirla en JavaScript.) -The notation [_n_~01~]{if html}[[$n_{01}$]{latex}]{if tex} indicates -the number of measurements where the first variable (squirrelness) is -false (0) and the second variable (pizza) is true (1). In the pizza -table, [_n_~01~]{if html}[[$n_{01}$]{latex}]{if tex} is 9. +La notación [_n_~01~]{if html}[[$n_{01}$]{latex}]{if tex} indica +el número de mediciones donde la primera variable (ardilla) es +falso (0) y la segunda variable (pizza) es verdadera (1). En la tabla +de pizza, [_n_~01~]{if html}[[$n_{01}$]{latex}]{if tex} es 9. -The value [_n_~1•~]{if html}[[$n_{1\bullet}$]{latex}]{if tex} refers -to the sum of all measurements where the first variable is true, which -is 5 in the example table. Likewise, [_n_~•0~]{if -html}[[$n_{\bullet0}$]{latex}]{if tex} refers to the sum of the -measurements where the second variable is false. +El valor [_n_~1•~]{if html}[[$n_{1\bullet}$]{latex}]{if tex} se refiere +a la suma de todas las medidas donde la primera variable es verdadera, que +es 5 en la tabla de ejemplo. Del mismo modo, [_n_~•0~]{if +html}[[$n_{\bullet0}$]{latex}]{if tex} se refiere a la suma de las +mediciones donde la segunda variable es falsa. {{index correlation, "phi coefficient"}} -So for the pizza table, the part above the division line (the -dividend) would be 1×76−4×9 = 40, and the part below it (the -divisor) would be the square root of 5×85×10×80, or [√340000]{if -html}[[$\sqrt{340000}$]{latex}]{if tex}. This comes out to _ϕ_ ≈ -0.069, which is tiny. Eating ((pizza)) does not appear to have -influence on the transformations. +Entonces para la tabla de pizza, la parte arriba de la línea de división (el +dividendo) sería 1×76−4×9 = 40, y la parte inferior (el +divisor) sería la raíz cuadrada de 5×85×10×80, o [√340000]{if +html}[[$\sqrt{340000}$]{latex}]{if tex}. Esto da _ϕ_ ≈ +0.069, que es muy pequeño. Comer ((pizza)) no parece tener +influencia en las transformaciones. -## Computing correlation +## Calculando correlación {{index [array, "as table"], [nesting, "of arrays"]}} -We can represent a two-by-two ((table)) in JavaScript with a -four-element array (`[76, 9, 4, 1]`). We could also use other -representations, such as an array containing two two-element arrays -(`[[76, 9], [4, 1]]`) or an object with property names like `"11"` and -`"01"`, but the flat array is simple and makes the expressions that -access the table pleasantly short. We'll interpret the indices to the -array as two-((bit)) ((binary number))s, where the leftmost (most -significant) digit refers to the squirrel variable and the rightmost -(least significant) digit refers to the event variable. For example, -the binary number `10` refers to the case where Jacques did turn into -a squirrel, but the event (say, "pizza") didn't occur. This happened -four times. And since binary `10` is 2 in decimal notation, we will -store this number at index 2 of the array. +Podemos representar una ((tabla)) de dos-por-dos en JavaScript con un +array de cuatro elementos (`[76, 9, 4, 1]`). También podríamos usar otras +representaciones, como un array que contiene dos arrays de dos elementos +(`[[76, 9], [4, 1]]`) o un objeto con nombres de propiedad como `"11"` y +`"01"`, pero el array plano es simple y hace que las expresiones que +acceden a la tabla agradablemente cortas. Interpretaremos los índices del +array como ((número binario))s de dos-((bits)) , donde el dígito más a la +izquierda (más significativo) se refiere a la variable ardilla y el digito +mas a la derecha (menos significativo) se refiere a la variable de evento. +Por ejemplo, el número binario `10` se refiere al caso en que Jacques se +convirtió en una ardilla, pero el evento (por ejemplo, "pizza") no ocurrió. +Esto ocurrió cuatro veces. Y dado que el `10` binario es 2 en notación decimal, +almacenaremos este número en el índice 2 del array. {{index "phi coefficient", "phi function"}} {{id phi_function}} -This is the function that computes the _ϕ_ coefficient from such an -array: +Esta es la función que calcula el coeficiente _ϕ_ de tal array: ```{includeCode: strip_log, test: clip} -function phi(table) { - return (table[3] * table[0] - table[2] * table[1]) / - Math.sqrt((table[2] + table[3]) * - (table[0] + table[1]) * - (table[1] + table[3]) * - (table[0] + table[2])); +function phi(tabla) { + return (tabla[3] * tabla[0] - tabla[2] * tabla[1]) / + Math.sqrt((tabla[2] + tabla[3]) * + (tabla[0] + tabla[1]) * + (tabla[1] + tabla[3]) * + (tabla[0] + tabla[2])); } console.log(phi([76, 9, 4, 1])); @@ -634,249 +644,247 @@ console.log(phi([76, 9, 4, 1])); {{index "square root", "Math.sqrt function"}} -This is a direct translation of the _ϕ_ formula into JavaScript. -`Math.sqrt` is the square root function, as provided by the `Math` -object in a standard JavaScript environment. We have to add two fields -from the table to get fields like [n~1•~]{if -html}[[$n_{1\bullet}$]{latex}]{if tex} because the sums of rows or -columns are not stored directly in our data structure. +Esta es una traducción directa de la fórmula _ϕ_ a JavaScript. +`Math.sqrt` es la función de raíz cuadrada, proporcionada por el objeto +`Math` en un entorno de JavaScript estándar. Tenemos que sumar dos campos +de la tabla para obtener campos como [n~1•~]{if +html}[[$n_{1\bullet}$]{latex}]{if tex} porque las sumas de filas o +columnas no se almacenan directamente en nuestra estructura de datos. {{index "JOURNAL data set"}} -Jacques kept his journal for three months. The resulting ((data set)) -is available in the [coding -sandbox](https://eloquentjavascript.net/code#4) for this chapter[ -([_https://eloquentjavascript.net/code#4_](https://eloquentjavascript.net/code#4))]{if -book}, where it is stored in the `JOURNAL` binding and in a -downloadable -[file](https://eloquentjavascript.net/code/journal.js). +Jacques mantuvo su diario por tres meses. El ((conjunto de datos)) resultante +está disponible en la [caja de arena](https://eloquentjavascript.net/code#4) +para este capítulo[([_eloquentjavascript.net/code#4_](https://eloquentjavascript.net/code#4))]{if +book}, donde se almacena en la vinculación `JOURNAL`, y en un +[archivo](https://eloquentjavascript.net/code/journal.js) descargable. {{index "tableFor function"}} -To extract a two-by-two ((table)) for a specific event from the -journal, we must loop over all the entries and tally how many times -the event occurs in relation to squirrel transformations. +Para extraer una ((tabla)) de dos por dos para un evento en específico del +diario, debemos hacer un ciclo a traves de todas las entradas y contar +cuántas veces ocurre el evento en relación a las transformaciones de ardilla. ```{includeCode: strip_log} -function tableFor(event, journal) { - let table = [0, 0, 0, 0]; - for (let i = 0; i < journal.length; i++) { - let entry = journal[i], index = 0; - if (entry.events.includes(event)) index += 1; - if (entry.squirrel) index += 2; - table[index] += 1; +function tablaPara(evento, diario) { + let tabla = [0, 0, 0, 0]; + for (let i = 0; i < diario.length; i++) { + let entrada = diario[i], index = 0; + if (entrada.eventos.includes(evento)) index += 1; + if (entrada.ardilla) index += 2; + tabla[index] += 1; } - return table; + return tabla; } -console.log(tableFor("pizza", JOURNAL)); +console.log(tablaPara("pizza", JOURNAL)); // → [76, 9, 4, 1] ``` {{index [array, searching], "includes method"}} -Arrays have an `includes` method that checks whether a given value -exists in the array. The function uses that to determine whether the -event name it is interested in is part of the event list for a given -day. +Los array tienen un método `includes` ("incluye") que verifica si un valor dado +existe en el array. La función usa eso para determinar si el nombre del evento +en el que estamos interesados forma parte de la lista de eventos para +un determinado día. {{index [array, indexing]}} -The body of the loop in `tableFor` figures out which box in the table -each journal entry falls into by checking whether the entry contains -the specific event it's interested in and whether the event happens -alongside a squirrel incident. The loop then adds one to the correct -box in the table. +El cuerpo del ciclo en `tablaPara` determina en cual caja de la tabla +cae cada entrada del diario al verificar si la entrada contiene +el evento específico que nos interesa y si el evento ocurre +junto con un incidente de ardilla. El ciclo luego agrega uno a la caja correcta +en la tabla. -We now have the tools we need to compute individual ((correlation))s. -The only step remaining is to find a correlation for every type of -event that was recorded and see whether anything stands out. +Ahora tenemos las herramientas que necesitamos para calcular las +((correlaciónes)) individuales. El único paso que queda es encontrar una +correlación para cada tipo de evento que se escribio en el diario +y ver si algo se destaca. {{id for_of_loop}} -## Array loops +## Ciclos de array {{index "for loop", loop, [array, iteration]}} -In the `tableFor` function, there's a loop like this: +En la función `tablaPara`, hay un ciclo como este: ``` -for (let i = 0; i < JOURNAL.length; i++) { - let entry = JOURNAL[i]; - // Do something with entry +for (let i = 0; i < DIARIO.length; i++) { + let entrada = DIARIO[i]; + // Hacer con algo con la entrada } ``` -This kind of loop is common in classical JavaScript—going over arrays -one element at a time is something that comes up a lot, and to do that -you'd run a counter over the length of the array and pick out each -element in turn. +Este tipo de ciclo es común en JavaScript clasico—ir a traves de los arrays +un elemento a la vez es algo que surge mucho, y para hacer eso +correrias un contador sobre la longitud del array y elegirías cada +elemento en turnos. -There is a simpler way to write such loops in modern JavaScript. +Hay una forma más simple de escribir tales ciclos en JavaScript moderno. ``` -for (let entry of JOURNAL) { - console.log(`${entry.events.length} events.`); +for (let entrada of DIARIO) { + console.log(`${entrada.eventos.length} eventos.`); } ``` {{index "for/of loop"}} -When a `for` loop looks like this, with the word `of` after a variable -definition, it will loop over the elements of the value given after -`of`. This works not only for arrays but also for strings and some -other data structures. We'll discuss _how_ it works in [Chapter -?](object). +Cuando un ciclo `for` se vea de esta manera, con la palabra `of` ("de") +después de una definición de variable, recorrerá los elementos del valor +dado después `of`. Esto funciona no solo para arrays, sino también para +strings y algunas otras estructuras de datos. Vamos a discutir _como_ funciona +en el [Capítulo 6](objeto). {{id analysis}} -## The final analysis +## El análisis final {{index journal, "weresquirrel example", "journalEvents function"}} -We need to compute a correlation for every type of event that occurs -in the data set. To do that, we first need to _find_ every type of -event. +Necesitamos calcular una correlación para cada tipo de evento que ocurra +en el conjunto de datos. Para hacer eso, primero tenemos que _encontrar_ +cada tipo de evento. {{index "includes method", "push method"}} ```{includeCode: "strip_log"} -function journalEvents(journal) { - let events = []; - for (let entry of journal) { - for (let event of entry.events) { - if (!events.includes(event)) { - events.push(event); +function eventosDiario(diario) { + let eventos = []; + for (let entrada of diario) { + for (let evento of entrada.eventos) { + if (!eventos.includes(evento)) { + eventos.push(evento); } } } - return events; + return eventos; } -console.log(journalEvents(JOURNAL)); -// → ["carrot", "exercise", "weekend", "bread", …] +console.log(eventosDiario(DIARIO)); +// → ["zanahoria", "ejercicio", "fin de semana", "pan", …] ``` -By going over all the events and adding those that aren't already in -there to the `events` array, the function collects every type of -event. +Yendo a traves de todos los eventos, y agregando aquellos que aún no están en +allí en el array `eventos`, la función recolecta cada tipo de evento. -Using that, we can see all the ((correlation))s. +Usando eso, podemos ver todos las ((correlaciones)). ```{test: no} -for (let event of journalEvents(JOURNAL)) { - console.log(event + ":", phi(tableFor(event, JOURNAL))); +for (let evento of eventosDiario(DIARIO)) { + console.log(evento + ":", phi(tablaPara(evento, DIARIO))); } -// → carrot: 0.0140970969 -// → exercise: 0.0685994341 -// → weekend: 0.1371988681 -// → bread: -0.0757554019 -// → pudding: -0.0648203724 +// → zanahoria: 0.0140970969 +// → ejercicio: 0.0685994341 +// → fin de semana: 0.1371988681 +// → pan: -0.0757554019 +// → pudin: -0.0648203724 // and so on... ``` -Most correlations seem to lie close to zero. Eating carrots, bread, or -pudding apparently does not trigger squirrel-lycanthropy. It _does_ -seem to occur somewhat more often on weekends. Let's filter the -results to show only correlations greater than 0.1 or less than -0.1. +La mayoría de las correlaciones parecen estar cercanas a cero. Come +zanahorias, pan o pudín aparentemente no desencadena la licantropía de ardilla. +_Parece_ ocurrir un poco más a menudo los fines de semana. Filtremos los +resultados para solo mostrar correlaciones mayores que 0.1 o menores que -0.1. ```{test: no, startCode: true} -for (let event of journalEvents(JOURNAL)) { - let correlation = phi(tableFor(event, JOURNAL)); - if (correlation > 0.1 || correlation < -0.1) { - console.log(event + ":", correlation); +for (let evento of eventosDiario(DIARIO)) { + let correlacion = phi(tablaPara(evento, DIARIO)); + if (correlacion > 0.1 || correlacion < -0.1) { + console.log(evento + ":", correlacion); } } -// → weekend: 0.1371988681 -// → brushed teeth: -0.3805211953 -// → candy: 0.1296407447 -// → work: -0.1371988681 -// → spaghetti: 0.2425356250 -// → reading: 0.1106828054 -// → peanuts: 0.5902679812 +// → fin de semana: 0.1371988681 +// → me cepille los dientes: -0.3805211953 +// → dulces: 0.1296407447 +// → trabajo: -0.1371988681 +// → spaghetti: 0.2425356250 +// → leer: 0.1106828054 +// → nueces: 0.5902679812 ``` -Aha! There are two factors with a ((correlation)) that's clearly stronger -than the others. Eating ((peanuts)) has a strong positive effect on -the chance of turning into a squirrel, whereas brushing his teeth has -a significant negative effect. +A-ha! Hay dos factores con una ((correlación)) que es claramente más fuerte +que las otras. Comer ((nueces)) tiene un fuerte efecto positivo en +la posibilidad de convertirse en una ardilla, mientras que cepillarse +los dientes tiene un significativo efecto negativo. -Interesting. Let's try something. +Interesante. Intentemos algo. -``` -for (let entry of JOURNAL) { - if (entry.events.includes("peanuts") && - !entry.events.includes("brushed teeth")) { - entry.events.push("peanut teeth"); +```{includeCode: strip_log} +for (let entrada of DIARIO) { + if (entrada.eventos.includes("nueces") && + !entrada.eventos.includes("me cepille los dientes")) { + entrada.eventos.push("dientes con nueces"); } } -console.log(phi(tableFor("peanut teeth", JOURNAL))); +console.log(phi(tablaPara("dientes con nueces", DIARIO))); // → 1 ``` -That's a strong result. The phenomenon occurs precisely when Jacques -eats ((peanuts)) and fails to brush his teeth. If only he weren't such -a slob about dental hygiene, he'd have never even noticed his -affliction. +Ese es un resultado fuerte. El fenómeno ocurre precisamente cuando Jacques +come ((nueces)) y no se cepilla los dientes. Si tan solo él no hubiese sido +tan flojo con su higiene dental, él nunca habría notado su aflicción. -Knowing this, Jacques stops eating peanuts altogether and finds that -his transformations don't come back. +Sabiendo esto, Jacques deja de comer nueces y descubre que sus transformaciones +no vuelven. {{index "weresquirrel example"}} -For a few years, things go great for Jacques. But at some point he -loses his job. Because he lives in a nasty country where having no job -means having no medical services, he is forced to take employment with -a ((circus)) where he performs as _The Incredible Squirrelman_, -stuffing his mouth with peanut butter before every show. +Durante algunos años, las cosas van bien para Jacques. Pero en algún momento él +pierde su trabajo. Porque vive en un país desagradable donde no tener trabajo +significa que no tiene servicios médicos, se ve obligado a trabajar con +a ((circo)) donde actua como _El Increible Hombre-Ardilla_, +llenando su boca con mantequilla de maní antes de cada presentación. -One day, fed up with this pitiful existence, Jacques fails to change -back into his human form, hops through a crack in the circus tent, and -vanishes into the forest. He is never seen again. +Un día, harto de esta existencia lamentable, Jacques no puede cambiar +de vuelta a su forma humana, salta a través de una grieta en la carpa del +circo, y se desvanece en el bosque. Nunca se le ve de nuevo. -## Further arrayology +## Arrayología avanzada -{{index [array, methods], [method, array]}} +{{index [array, methods], method}} -Before finishing the chapter, I want to introduce you to a few more -object-related concepts. I'll start by introducing some generally -useful array methods. +Antes de terminar el capítulo, quiero presentarte algunos conceptos extras +relacionados a los objetos. Comenzaré introduciendo algunos en métodos de +arrays útiles generalmente. {{index "push method", "pop method", "shift method", "unshift method"}} -We saw `push` and `pop`, which add and remove elements at the -end of an array, [earlier](data#array_methods) in this -chapter. The corresponding methods for adding and removing things at -the start of an array are called `unshift` and `shift`. +Vimos `push` y `pop`, que agregan y removen elementos en el +final de un array, [anteriormente](datos#array_methods) en este +capítulo. Los métodos correspondientes para agregar y remover cosas en +el comienzo de un array se llaman `unshift` y `shift`. ``` -let todoList = []; -function remember(task) { - todoList.push(task); +let listaDeTareas = []; +function recordar(tarea) { + listaDeTareas.push(tarea); } -function getTask() { - return todoList.shift(); +function obtenerTarea() { + return listaDeTareas.shift(); } -function rememberUrgently(task) { - todoList.unshift(task); +function recordarUrgentemente(tarea) { + listaDeTareas.unshift(tarea); } ``` {{index "task management example"}} -That program manages a queue of tasks. You add tasks to the end of the -queue by calling `remember("groceries")`, and when you're ready to do -something, you call `getTask()` to get (and remove) the front item -from the queue. The `rememberUrgently` function also adds a task but -adds it to the front instead of the back of the queue. +Ese programa administra una cola de tareas. Agregas tareas al final de la +cola al llamar `recordar("verduras")`, y cuando estés listo para hacer +algo, llamas a `obtenerTarea()` para obtener (y eliminar) el elemento frontal +de la cola. La función `recordarUrgentemente` también agrega una tarea pero +la agrega al frente en lugar de a la parte posterior de la cola. {{index [array, searching], "indexOf method", "lastIndexOf method"}} -To search for a specific value, arrays provide an `indexOf` method. The method -searches through the array from the start to the end and returns the -index at which the requested value was found—or -1 if it wasn't found. -To search from the end instead of the start, there's a similar method -called `lastIndexOf`. +Para buscar un valor específico, los arrays proporcionan un método `indexOf` +("indice de"). +Este busca a través del array desde el principio hasta el final y retorna el +índice en el que se encontró el valor solicitado—o -1 si este no fue encontrado. +Para buscar desde el final en lugar del inicio, hay un método similar +llamado `lastIndexOf` ("ultimo indice de"). ``` console.log([1, 2, 3, 2, 1].indexOf(2)); @@ -885,14 +893,14 @@ console.log([1, 2, 3, 2, 1].lastIndexOf(2)); // → 3 ``` -Both `indexOf` and `lastIndexOf` take an optional second argument that -indicates where to start searching. +Tanto `indexOf` como `lastIndexOf` toman un segundo argumento opcional que +indica dónde comenzar la búsqueda. {{index "slice method", [array, indexing]}} -Another fundamental array method is `slice`, which takes start and end -indices and returns an array that has only the elements between them. -The start index is inclusive, the end index exclusive. +Otro método fundamental de array es `slice` ("rebanar"), que toma índices de +inicio y fin y retorna un array que solo tiene los elementos entre ellos. +El índice de inicio es inclusivo, el índice final es exclusivo. ``` console.log([0, 1, 2, 3, 4].slice(2, 4)); @@ -903,85 +911,85 @@ console.log([0, 1, 2, 3, 4].slice(2)); {{index [string, indexing]}} -When the end index is not given, `slice` will take all of the elements -after the start index. You can also omit the start index to copy the -entire array. +Cuando no se proporcione el índice final, `slice` tomará todos los elementos +después del índice de inicio. También puedes omitir el índice de inicio +para copiar todo el array. {{index concatenation, "concat method"}} -The `concat` method can be used to glue arrays together to create a -new array, similar to what the `+` operator does for strings. +El método `concat` ("concatenar") se puede usar para unir arrays y asi crear un +nuevo array, similar a lo que hace el operador `+` para los strings. -The following example shows both `concat` and `slice` in action. It takes -an array and an index, and it returns a new array that is a copy of -the original array with the element at the given index removed. +El siguiente ejemplo muestra tanto `concat` como `slice` en acción. Toma un +array y un índice, y retorna un nuevo array que es una copia del +array original pero eliminando al elemento en el índice dado: ``` -function remove(array, index) { - return array.slice(0, index) - .concat(array.slice(index + 1)); +function remover(array, indice) { + return array.slice(0, indice) + .concat(array.slice(indice + 1)); } -console.log(remove(["a", "b", "c", "d", "e"], 2)); +console.log(remover(["a", "b", "c", "d", "e"], 2)); // → ["a", "b", "d", "e"] ``` -If you pass `concat` an argument that is not an array, that value will -be added to the new array as if it were a one-element array. +Si a `concat` le pasas un argumento que no es un array, ese valor +sera agregado al nuevo array como si este fuera un array de un solo elemento. -## Strings and their properties +## Strings y sus propiedades {{index [string, properties]}} -We can read properties like `length` and `toUpperCase` from string -values. But if you try to add a new property, it doesn't stick. +Podemos leer propiedades como `length` y `toUpperCase` de valores string. +Pero si intentas agregar una nueva propiedad, esta no se mantiene. ``` let kim = "Kim"; -kim.age = 88; -console.log(kim.age); +kim.edad = 88; +console.log(kim.edad); // → undefined ``` -Values of type string, number, and Boolean are not objects, and though -the language doesn't complain if you try to set new properties on -them, it doesn't actually store those properties. As mentioned earlier, -such values are immutable and cannot be changed. +Los valores de tipo string, número, y Booleano no son objetos, y aunque +el lenguaje no se queja si intentas establecer nuevas propiedades en +ellos, en realidad no almacena esas propiedades. Como se mencionó antes, +tales valores son inmutables y no pueden ser cambiados. {{index [string, methods], "slice method", "indexOf method", [string, searching]}} -But these types do have built-in properties. Every string value has a -number of methods. Some very useful ones are `slice` and `indexOf`, -which resemble the array methods of the same name. +Pero estos tipos tienen propiedades integradas. Cada valor de string tiene un +numero de metodos. Algunos muy útiles son `slice` e `indexOf`, +que se parecen a los métodos de array de los mismos nombres. ``` -console.log("coconuts".slice(4, 7)); -// → nut -console.log("coconut".indexOf("u")); -// → 5 +console.log("panaderia".slice(0, 3)); +// → pan +console.log("panaderia".indexOf("a")); +// → 1 ``` -One difference is that a string's `indexOf` can search for a string -containing more than one character, whereas the corresponding array -method looks only for a single element. +Una diferencia es que el `indexOf` de un string puede buscar por un string +que contenga más de un carácter, mientras que el método correspondiente al +array solo busca por un elemento único. ``` -console.log("one two three".indexOf("ee")); -// → 11 +console.log("uno dos tres".indexOf("tres")); +// → 8 ``` -{{index [whitespace, trimming], "trim method"}} +{{index whitespace, "trim method"}} -The `trim` method removes whitespace (spaces, newlines, tabs, and -similar characters) from the start and end of a string. +El método `trim` ("recortar") elimina los espacios en blanco (espacios, saltos +de linea, tabulaciones y caracteres similares) del inicio y final de un string. ``` -console.log(" okay \n ".trim()); -// → okay +console.log(" okey \n ".trim()); +// → okey ``` -The `zeroPad` function from the [previous chapter](functions) also -exists as a method. It is called `padStart` and takes the desired -length and padding character as arguments. +La función `alcocharConCeros` del [capítulo anterior](funciones) también +existe como un método. Se llama `padStart` ("alcohar inicio") y +toma la longitud deseada y el carácter de relleno como argumentos. ``` console.log(String(6).padStart(3, "0")); @@ -990,23 +998,23 @@ console.log(String(6).padStart(3, "0")); {{id split}} -You can split a string on every occurrence of another string with -`split` and join it again with `join`. +Puedes dividir un string en cada ocurrencia de otro string con el metodo +`split` ("dividir"), y unirlo nuevamente con `join` ("unir"). ``` -let sentence = "Secretarybirds specialize in stomping"; -let words = sentence.split(" "); -console.log(words); -// → ["Secretarybirds", "specialize", "in", "stomping"] -console.log(words.join(". ")); -// → Secretarybirds. specialize. in. stomping +let oracion = "Los pajaros secretarios se especializan en pisotear"; +let palabras = oracion.split(" "); +console.log(palabras); +// → ["Los", "pajaros", "secretarios", "se", "especializan", "en", "pisotear"] +console.log(palabras.join(". ")); +// → Los. pajaros. secretarios. se. especializan. en. pisotear ``` {{index "repeat method"}} -A string can be repeated with the `repeat` method, which creates a new -string containing multiple copies of the original string, glued -together. +Se puede repetir un string con el método `repeat` ("repetir"), el cual +crea un nuevo string que contiene múltiples copias concatenadas +del string original. ``` console.log("LA".repeat(3)); @@ -1015,10 +1023,10 @@ console.log("LA".repeat(3)); {{index ["length property", "for string"], [string, indexing]}} -We have already seen the string type's `length` property. Accessing -the individual characters in a string looks like accessing array -elements (with a caveat that we'll discuss in [Chapter -?](higher_order#code_units)). +Ya hemos visto la propiedad `length` en los valores de tipo string. Acceder a +los caracteres individuales en un string es similar a acceder a los elementos +de un array (con una diferencia que discutiremos en el +[Capítulo 6](orden_superior#unidades_de_codigo)). ``` let string = "abc"; @@ -1030,125 +1038,131 @@ console.log(string[1]); {{id rest_parameters}} -## Rest parameters +## Parámetros restantes {{index "Math.max function"}} -It can be useful for a function to accept any number of ((argument))s. -For example, `Math.max` computes the maximum of _all_ the arguments it -is given. +Puede ser útil para una función aceptar cualquier cantidad de ((argumento))s. +Por ejemplo, `Math.max` calcula el máximo de _todos_ los argumentos que le +son dados. -{{index "period character", "max example", spread}} +{{indexsee "period character", "max example", spread}} -To write such a function, you put three dots before the function's -last ((parameter)), like this: +Para escribir tal función, pones tres puntos antes del ultimo ((parámetro)) +de la función, asi: -```{includeCode: strip_log} -function max(...numbers) { - let result = -Infinity; - for (let number of numbers) { - if (number > result) result = number; +``` +function maximo(...numeros) { + let resultado = -Infinity; + for (let numero of numeros) { + if (numero > resultado) resultado = numero; } - return result; + return resultado; } -console.log(max(4, 1, 9, -2)); +console.log(maximo(4, 1, 9, -2)); // → 9 ``` -When such a function is called, the _((rest parameter))_ is bound to -an array containing all further arguments. If there are other -parameters before it, their values aren't part of that array. When, as -in `max`, it is the only parameter, it will hold all arguments. +Cuando se llame a una función como esa, el _((parámetro restante))_ está +vinculado a un array que contiene todos los argumentos adicionales. Si hay +otros parámetros antes que él, sus valores no seran parte de ese array. +Cuando, tal como en `maximo`, sea el único parámetro, contendrá todos +los argumentos. -{{index [function, application]}} +{{index "function application"}} -You can use a similar three-dot notation to _call_ a function with an -array of arguments. +Puedes usar una notación de tres-puntos similar para _llamar_ una función con +un array de argumentos. ``` -let numbers = [5, 1, 7]; -console.log(max(...numbers)); +let numeros = [5, 1, 7]; +console.log(max(...numeros)); // → 7 ``` -This "((spread))s" out the array into the function call, passing its -elements as separate arguments. It is possible to include an array -like that along with other arguments, as in `max(9, ...numbers, 2)`. +Esto "((extiende))" al array en la llamada de la función, pasando sus +elementos como argumentos separados. Es posible incluir un array de esa +manera, junto con otros argumentos, como en `max(9, ...numeros, 2)`. + +{{index array, "square brackets"}} + +La notación de corchetes para crear arrays permite al operador de tres-puntos +extender otro array en el nuevo array: -{{index [array, "of rest arguments"], "square brackets"}} Square bracket array notation similarly allows the triple-dot operator -to spread another array into the new array. +to spread another array into the new array: ``` -let words = ["never", "fully"]; -console.log(["will", ...words, "understand"]); -// → ["will", "never", "fully", "understand"] +let palabras = ["nunca", "entenderas"]; +console.log(["tu", ...palabras, "completamente"]); +// → ["tu", "nunca", "entenderas", "completamente"] ``` -## The Math object +## El objeto Math {{index "Math object", "Math.min function", "Math.max function", "Math.sqrt function", minimum, maximum, "square root"}} -As we've seen, `Math` is a grab bag of number-related utility -functions, such as `Math.max` (maximum), `Math.min` (minimum), and -`Math.sqrt` (square root). +Como hemos visto, `Math` es una bolsa de sorpresas de utilidades relacionadas +a los numeros, como `Math.max` (máximo), `Math.min` (mínimo) y +`Math.sqrt` (raíz cuadrada). -{{index namespace, [object, property]}} +{{index namespace, "namespace pollution", object}} {{id namespace_pollution}} -The `Math` object is used as a container to group a bunch of related -functionality. There is only one `Math` object, and it is almost never -useful as a value. Rather, it provides a _namespace_ so that all these -functions and values do not have to be global bindings. +El objeto `Math` es usado como un contenedor que agrupa un grupo de +funcionalidades relacionadas. Solo hay un objeto `Math`, y casi nunca es +útil como un valor. Más bien, proporciona un _espacio de nombre_ +para que todos estas funciones y valores no tengan que ser vinculaciones +globales. {{index [binding, naming]}} -Having too many global bindings "pollutes" the namespace. The more -names have been taken, the more likely you are to accidentally -overwrite the value of some existing binding. For example, it's not -unlikely to want to name something `max` in one of your programs. -Since JavaScript's built-in `max` function is tucked safely inside the -`Math` object, we don't have to worry about overwriting it. +Tener demasiadas vinculaciones globales "contamina" el espacio de nombres. +Cuanto más nombres hayan sido tomados, es más probable que accidentalmente +sobrescribas el valor de algunas vinculaciones existentes. Por ejemplo, no es +es poco probable que quieras nombrar algo `max` en alguno de tus programas. +Dado que la función `max` ya incorporada en JavaScript está escondida dentro del +Objeto `Math`, no tenemos que preocuparnos por sobrescribirla. {{index "let keyword", "const keyword"}} -Many languages will stop you, or at least warn you, when you are -defining a binding with a name that is already taken. JavaScript does -this for bindings you declared with `let` or `const` -but—perversely—not for standard bindings nor for bindings declared -with `var` or `function`. +Muchos lenguajes te detendrán, o al menos te advertirán, cuando estes por +definir una vinculación con un nombre que ya este tomado. JavaScript hace +esto para vinculaciones que hayas declarado con `let` o` const` +pero-perversamente-no para vinculaciones estándar, ni para +vinculaciones declaradas con `var` o `function`. {{index "Math.cos function", "Math.sin function", "Math.tan function", "Math.acos function", "Math.asin function", "Math.atan function", "Math.PI constant", cosine, sine, tangent, "PI constant", pi}} -Back to the `Math` object. If you need to do ((trigonometry)), `Math` -can help. It contains `cos` (cosine), `sin` (sine), and `tan` -(tangent), as well as their inverse functions, `acos`, `asin`, and -`atan`, respectively. The number π (pi)—or at least the closest -approximation that fits in a JavaScript number—is available as -`Math.PI`. There is an old programming tradition of writing the names -of ((constant)) values in all caps. +De vuelta al objeto `Math`. Si necesitas hacer ((trigonometría)), `Math` te +puede ayudar. Contiene `cos` (coseno), `sin` (seno) y `tan` +(tangente), así como sus funciones inversas, `acos`, `asin`, y +`atan`, respectivamente. El número π (pi)—o al menos la aproximación +más cercano que cabe en un número de JavaScript—está disponible como +`Math.PI`. Hay una vieja tradición en la programación de escribir los nombres +de los valores ((constantes)) en mayúsculas. ```{test: no} -function randomPointOnCircle(radius) { - let angle = Math.random() * 2 * Math.PI; - return {x: radius * Math.cos(angle), - y: radius * Math.sin(angle)}; +function puntoAleatorioEnCirculo(radio) { + let angulo = Math.random() * 2 * Math.PI; + return {x: radio * Math.cos(angulo), + y: radio * Math.sin(angulo)}; } -console.log(randomPointOnCircle(2)); +console.log(puntoAleatorioEnCirculo(2)); // → {x: 0.3667, y: 1.966} ``` -If sines and cosines are not something you are familiar with, don't -worry. When they are used in this book, in [Chapter ?](dom#sin_cos), -I'll explain them. +Si los senos y los cosenos son algo con lo que no estas familiarizado, +no te preocupes. Cuando se usen en este libro, en el +[Capítulo 14](dom#seno_coseno), te los explicaré. {{index "Math.random function", "random number"}} -The previous example used `Math.random`. This is a function that -returns a new pseudorandom number between zero (inclusive) and one -(exclusive) every time you call it. +El ejemplo anterior usó `Math.random`. Esta es una función que +retorna un nuevo número pseudoaleatorio entre cero (inclusivo) y uno +(exclusivo) cada vez que la llamas. ```{test: no} console.log(Math.random()); @@ -1161,60 +1175,61 @@ console.log(Math.random()); {{index "pseudorandom number", "random number"}} -Though computers are deterministic machines—they always react the same -way if given the same input—it is possible to have them produce -numbers that appear random. To do that, the machine keeps some hidden -value, and whenever you ask for a new random number, it performs -complicated computations on this hidden value to create a new value. -It stores a new value and returns some number derived from it. That -way, it can produce ever new, hard-to-predict numbers in a way that -_seems_ random. +Aunque las computadoras son máquinas deterministas—siempre reaccionan de la +misma manera manera dada la misma entrada—es posible hacer que +produzcan números que parecen aleatorios. Para hacer eso, la máquina mantiene +algun valor escondido, y cada vez que le pidas un nuevo número aleatorio, +realiza calculos complicados en este valor oculto para crear un nuevo valor. +Esta almacena un nuevo valor y retorna un número derivado de él. +De esta manera, puede producir números nuevos y difíciles de predecir de +una manera que _parece_ aleatoria. {{index rounding, "Math.floor function"}} -If we want a whole random number instead of a fractional one, we can -use `Math.floor` (which rounds down to the nearest whole number) on -the result of `Math.random`. +Si queremos un número entero al azar en lugar de uno fraccionario, podemos +usar `Math.floor` (que redondea hacia abajo al número entero más cercano) con +el resultado de `Math.random`. ```{test: no} console.log(Math.floor(Math.random() * 10)); // → 2 ``` -Multiplying the random number by 10 gives us a number greater than or -equal to 0 and below 10. Since `Math.floor` rounds down, this -expression will produce, with equal chance, any number from 0 through +Multiplicar el número aleatorio por 10 nos da un número mayor que o +igual a cero e inferior a 10. Como `Math.floor` redondea hacia abajo, esta +expresión producirá, con la misma probabilidad, cualquier número desde 0 hasta 9. {{index "Math.ceil function", "Math.round function", "Math.abs function", "absolute value"}} -There are also the functions `Math.ceil` (for "ceiling", which rounds -up to a whole number), `Math.round` (to the nearest whole number), and -`Math.abs`, which takes the absolute value of a number, meaning it -negates negative values but leaves positive ones as they are. +También están las funciones `Math.ceil` (que redondea hacia arriba hasta llegar +al número entero mas cercano), `Math.round` (al número entero más cercano), y +`Math.abs`, que toma el valor absoluto de un número, lo que significa que +niega los valores negativos pero deja los positivos tal y como estan. -## Destructuring +## Desestructurar {{index "phi function"}} -Let's go back to the `phi` function for a moment. +Volvamos a la función `phi` por un momento: ```{test: wrap} -function phi(table) { - return (table[3] * table[0] - table[2] * table[1]) / - Math.sqrt((table[2] + table[3]) * - (table[0] + table[1]) * - (table[1] + table[3]) * - (table[0] + table[2])); +function phi(tabla) { + return (tabla[3] * tabla[0] - tabla[2] * tabla[1]) / + Math.sqrt((tabla[2] + tabla[3]) * + (tabla[0] + tabla[1]) * + (tabla[1] + tabla[3]) * + (tabla[0] + tabla[2])); } ``` {{index "destructuring binding", parameter}} -One of the reasons this function is awkward to read is that we have a -binding pointing at our array, but we'd much prefer to have bindings -for the _elements_ of the array, that is, `let n00 = table[0]` and so on. -Fortunately, there is a succinct way to do this in JavaScript. +Una de las razones por las que esta función es incómoda de leer es que +tenemos una vinculación apuntando a nuestro array, pero preferiríamos tener +vinculaciones para los _elementos_ del array, es decir, `let n00 = tabla[0]` +y así sucesivamente. Afortunadamente, hay una forma concisa de hacer esto en +JavaScript. ``` function phi([n00, n01, n10, n11]) { @@ -1224,157 +1239,157 @@ function phi([n00, n01, n10, n11]) { } ``` -{{index "let keyword", "var keyword", "const keyword", [binding, destructuring]}} +{{index "let keyword", "var keyword", "const keyword"}} -This also works for bindings created with `let`, `var`, or -`const`. If you know the value you are binding is an array, you can -use ((square brackets)) to "look inside" of the value, binding its -contents. +Esto también funciona para ((vinculaciones)) creadas con `let`, `var`, o +`const`. Si sabes que el valor que estas vinculando es un array, puedes +usar ((corchetes)) para "mirar dentro" del valor, y asi vincular sus +contenidos. -{{index [object, property], [braces, object]}} +{{index object, "curly braces"}} -A similar trick works for objects, using braces instead of square -brackets. +Un truco similar funciona para objetos, utilizando llaves en lugar de corchetes. ``` -let {name} = {name: "Faraji", age: 23}; -console.log(name); +let {nombre} = {nombre: "Faraji", edad: 23}; +console.log(nombre); // → Faraji ``` {{index null, undefined}} -Note that if you try to destructure `null` or `undefined`, you get an -error, much as you would if you directly try to access a property -of those values. +Ten en cuenta que si intentas desestructurar `null` o `undefined`, obtendrás un +error, igual como te pasaria si intentaras acceder directamente a una propiedad +de esos valores. ## JSON -{{index [array, representation], [object, representation], "data format", [memory, organization]}} +{{index [array, representation], [object, representation], "data format"}} -Because properties only grasp their value, rather than contain it, -objects and arrays are stored in the computer's memory as -sequences of bits holding the _((address))es_—the place in memory—of -their contents. So an array with another array inside of it consists -of (at least) one memory region for the inner array, and another for -the outer array, containing (among other things) a binary number that -represents the position of the inner array. +Ya que las propiedades solo agarran su valor, en lugar de contenerlo, +los objetos y arrays se almacenan en la ((memoria)) de la computadora como +secuencias de bits que contienen las _((dirección))es_—el lugar en la memoria—de +sus contenidos. Asi que un array con otro array dentro de el consiste +en (al menos) una región de memoria para el array interno, y otra para +el array externo, que contiene (entre otras cosas) un número binario que +representa la posición del array interno. -If you want to save data in a file for later or send it to another -computer over the network, you have to somehow convert these tangles -of memory addresses to a description that can be stored or sent. You -_could_ send over your entire computer memory along with the address -of the value you're interested in, I suppose, but that doesn't seem -like the best approach. +Si deseas guardar datos en un archivo para más tarde, o para enviarlo a otra +computadora a través de la red, tienes que convertir de alguna manera estos +enredos de direcciones de memoria a una descripción que se puede almacenar o +enviar. Supongo, que _podrías_ enviar toda la memoria de tu computadora +junto con la dirección del valor que te interesa, pero ese no parece el +mejor enfoque. {{indexsee "JavaScript Object Notation", JSON}} {{index serialization, "World Wide Web"}} -What we can do is _serialize_ the data. That means it is converted -into a flat description. A popular serialization format is called -_((JSON))_ (pronounced "Jason"), which stands for JavaScript Object -Notation. It is widely used as a data storage and communication format -on the Web, even in languages other than JavaScript. +Lo que podemos hacer es _serializar_ los datos. Eso significa que son +convertidos a una descripción plana. Un formato de serialización popular llamado +_((JSON))_ (pronunciado "Jason"), que significa JavaScript Object +Notation ("Notación de Objetos JavaScript"). Es ampliamente utilizado +como un formato de almacenamiento y comunicación de datos +en la Web, incluso en otros lenguajes diferentes a JavaScript. -{{index [array, notation], [object, creation], [quoting, "in JSON"], comment}} +{{index array, object, [quoting, "in JSON"], comment}} -JSON looks similar to JavaScript's way of writing arrays and objects, -with a few restrictions. All property names have to be surrounded by -double quotes, and only simple data expressions are allowed—no -function calls, bindings, or anything that involves actual -computation. Comments are not allowed in JSON. +JSON es similar a la forma en que JavaScript escribe arrays y objetos, +con algunas restricciones. Todos los nombres de propiedad deben estar +rodeados por comillas dobles, y solo se permiten expresiones de datos +simples—sin llamadas a función, vinculaciones o cualquier otra cosa que +involucre computaciones reales. Los comentarios no están permitidos en JSON. -A journal entry might look like this when represented as JSON data: +Una entrada de diario podria verse así cuando se representa como datos JSON: ```{lang: "application/json"} { - "squirrel": false, - "events": ["work", "touched tree", "pizza", "running"] + "ardilla": false, + "eventos": ["trabajo", "toque un arbol", "pizza", "sali a correr"] } ``` {{index "JSON.stringify function", "JSON.parse function", serialization, deserialization, parsing}} -JavaScript gives us the functions `JSON.stringify` and `JSON.parse` to -convert data to and from this format. The first takes a JavaScript -value and returns a JSON-encoded string. The second takes such a -string and converts it to the value it encodes. +JavaScript nos da las funciones `JSON.stringify` y `JSON.parse` para +convertir datos hacia y desde este formato. El primero toma un valor en +JavaScript y retorna un string codificado en JSON. La segunda toma +un string como ese y lo convierte al valor que este codifica. ``` -let string = JSON.stringify({squirrel: false, - events: ["weekend"]}); +let string = JSON.stringify({ardilla: false, + eventos: ["fin de semana"]}); console.log(string); -// → {"squirrel":false,"events":["weekend"]} -console.log(JSON.parse(string).events); -// → ["weekend"] +// → {"ardilla":false,"eventos":["fin de semana"]} +console.log(JSON.parse(string).eventos); +// → ["fin de semana"] ``` -## Summary +## Resumen -Objects and arrays (which are a specific kind of object) provide ways -to group several values into a single value. Conceptually, this allows -us to put a bunch of related things in a bag and run around with the -bag, instead of wrapping our arms around all of the individual things -and trying to hold on to them separately. +Los objetos y arrays (que son un tipo específico de objeto) proporcionan formas +de agrupar varios valores en un solo valor. Conceptualmente, esto nos permite +poner un montón de cosas relacionadas en un bolso y correr alredor con el +bolso, en lugar de envolver nuestros brazos alrededor de todas las cosas +individuales, tratando de aferrarnos a ellas por separado. -Most values in JavaScript have properties, the exceptions being `null` -and `undefined`. Properties are accessed using `value.prop` or -`value["prop"]`. Objects tend to use names for their properties -and store more or less a fixed set of them. Arrays, on the other hand, -usually contain varying amounts of conceptually identical values and -use numbers (starting from 0) as the names of their properties. +La mayoría de los valores en JavaScript tienen propiedades, las excepciones +son `null` y `undefined`. Se accede a las propiedades usando `valor.propiedad` o +`valor["propiedad"]`. Los objetos tienden a usar nombres para sus propiedades +y almacenar más o menos un conjunto fijo de ellos. Los arrays, por el otro lado, +generalmente contienen cantidades variables de valores conceptualmente +idénticos y usa números (comenzando desde 0) como los nombres de sus propiedades. -There _are_ some named properties in arrays, such as `length` and a -number of methods. Methods are functions that live in properties and -(usually) act on the value they are a property of. +Hay _algunas_ propiedades con nombre en los arrays, como `length` y un +numero de metodos. Los métodos son funciones que viven en propiedades y +(por lo general) actuan sobre el valor del que son una propiedad. -You can iterate over arrays using a special kind of `for` loop—`for -(let element of array)`. +Puedes iterar sobre los arrays utilizando un tipo especial de ciclo `for`—`for +(let elemento of array)`. -## Exercises +## Ejercicios -### The sum of a range +### La suma de un rango {{index "summing (exercise)"}} -The [introduction](intro) of this book alluded to the following as a -nice way to compute the sum of a range of numbers: +La [introducción](intro) de este libro aludía a lo siguiente como una +buena forma de calcular la suma de un rango de números: ```{test: no} -console.log(sum(range(1, 10))); +console.log(suma(rango(1, 10))); ``` {{index "range function", "sum function"}} -Write a `range` function that takes two arguments, `start` and `end`, -and returns an array containing all the numbers from `start` up to -(and including) `end`. +Escribe una función `rango` que tome dos argumentos, `inicio` y `final`, +y retorne un array que contenga todos los números desde `inicio` hasta +(e incluyendo) `final`. -Next, write a `sum` function that takes an array of numbers and -returns the sum of these numbers. Run the example program and see -whether it does indeed return 55. +Luego, escribe una función `suma` que tome un array de números y +retorne la suma de estos números. Ejecuta el programa de ejemplo y ve +si realmente retorna 55. {{index "optional argument"}} -As a bonus assignment, modify your `range` function to take an -optional third argument that indicates the "step" value used when -building the array. If no step is given, the elements go up by -increments of one, corresponding to the old behavior. The function -call `range(1, 10, 2)` should return `[1, 3, 5, 7, 9]`. Make sure it -also works with negative step values so that `range(5, 2, -1)` -produces `[5, 4, 3, 2]`. +Como una misión extra, modifica tu función `rango` para tomar un +tercer argumento opcional que indique el valor de "paso" utilizado para cuando +construyas el array. Si no se da ningún paso, los elementos suben en +incrementos de uno, correspondiedo al comportamiento anterior. La llamada +a la función `rango(1, 10, 2)` deberia retornar `[1, 3, 5, 7, 9]`. Asegúrate de +que también funcione con valores de pasos negativos para que `rango(5, 2, -1)` +produzca `[5, 4, 3, 2]`. {{if interactive ```{test: no} -// Your code here. +// Tu código aquí. -console.log(range(1, 10)); +console.log(rango(1, 10)); // → [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] -console.log(range(5, 2, -1)); +console.log(rango(5, 2, -1)); // → [5, 4, 3, 2] -console.log(sum(range(1, 10))); +console.log(sum(rango(1, 10))); // → 55 ``` @@ -1384,65 +1399,66 @@ if}} {{index "summing (exercise)", [array, creation], "square brackets"}} -Building up an array is most easily done by first initializing a -binding to `[]` (a fresh, empty array) and repeatedly calling its -`push` method to add a value. Don't forget to return the array at the -end of the function. +Construir un array se realiza más fácilmente al inicializar primero una +vinculación a `[]` (un array nuevo y vacío) y llamando repetidamente a su +método `push` para agregar un valor. No te olvides de retornar el array al +final de la función. {{index [array, indexing], comparison}} -Since the end boundary is inclusive, you'll need to use the `<=` -operator rather than `<` to check for the end of your loop. +Dado que el límite final es inclusivo, deberias usar el operador `<=` +en lugar de `<` para verificar el final de tu ciclo. {{index "arguments object"}} -The step parameter can be an optional parameter that defaults (using -the `=` operator) to 1. +El parámetro de paso puede ser un parámetro opcional que por defecto (usando +el operador `=`) tenga el valor 1. {{index "range function", "for loop"}} -Having `range` understand negative step values is probably best done -by writing two separate loops—one for counting up and one for counting -down—because the comparison that checks whether the loop is finished -needs to be `>=` rather than `<=` when counting downward. +Hacer que `rango` entienda valores de paso negativos es probablemente +mas facil de realizar al escribir dos ciclos por separado—uno para contar +hacia arriba y otro para contar hacia abajo—ya que la comparación que verifica +si el ciclo está terminado necesita ser `>=` en lugar de `<=` cuando se cuenta +hacia abajo. -It might also be worthwhile to use a different default step, namely, --1, when the end of the range is smaller than the start. That way, -`range(5, 2)` returns something meaningful, rather than getting stuck -in an ((infinite loop)). It is possible to refer to previous -parameters in the default value of a parameter. +También puede que valga la pena utilizar un paso predeterminado +diferente, es decir -1, cuando el final del rango sea menor que el inicio. +De esa manera, `rango(5, 2)` retornaria algo significativo, en lugar de quedarse +atascado en un ((ciclo infinito)). Es posible referirse a parámetros anteriores +en el valor predeterminado de un parámetro. hint}} -### Reversing an array +### Revirtiendo un array {{index "reversing (exercise)", "reverse method", [array, methods]}} -Arrays have a `reverse` method that changes the array by inverting -the order in which its elements appear. For this exercise, write two -functions, `reverseArray` and `reverseArrayInPlace`. The first, -`reverseArray`, takes an array as argument and produces a _new_ array -that has the same elements in the inverse order. The second, -`reverseArrayInPlace`, does what the `reverse` method does: it -_modifies_ the array given as argument by reversing its elements. -Neither may use the standard `reverse` method. +Los arrays tienen un método `reverse` que cambia al array invirtiendo +el orden en que aparecen sus elementos. Para este ejercicio, escribe dos +funciones, `revertirArray` y `revertirArrayEnSuLugar`. El primero, +`revertirArray`, toma un array como argumento y produce un _nuevo_ array +que tiene los mismos elementos pero en el orden inverso. El segundo, +`revertirArrayEnSuLugar`, hace lo que hace el método` reverse`: +_modifica_ el array dado como argumento invirtiendo sus elementos. +Ninguno de los dos puede usar el método `reverse` estándar. {{index efficiency, "pure function", "side effect"}} -Thinking back to the notes about side effects and pure functions in -the [previous chapter](functions#pure), which variant do you expect to -be useful in more situations? Which one runs faster? +Pensando en las notas acerca de los efectos secundarios y las funciones puras en +el [capítulo anterior](funciones#pura), qué variante esperas que sea +útil en más situaciones? Cuál corre más rápido? {{if interactive ```{test: no} -// Your code here. +// Tu código aquí. -console.log(reverseArray(["A", "B", "C"])); +console.log(revertirArray(["A", "B", "C"])); // → ["C", "B", "A"]; -let arrayValue = [1, 2, 3, 4, 5]; -reverseArrayInPlace(arrayValue); -console.log(arrayValue); +let valorArray = [1, 2, 3, 4, 5]; +revertirArrayEnSuLugar(valorArray); +console.log(valorArray); // → [5, 4, 3, 2, 1] ``` @@ -1452,95 +1468,96 @@ if}} {{index "reversing (exercise)"}} -There are two obvious ways to implement `reverseArray`. The first is -to simply go over the input array from front to back and use the -`unshift` method on the new array to insert each element at its start. -The second is to loop over the input array backwards and use the `push` -method. Iterating over an array backwards requires a (somewhat awkward) -`for` specification, like `(let i = array.length - 1; i >= 0; i--)`. +Hay dos maneras obvias de implementar `revertirArray`. La primera es +simplemente pasar a traves del array de entrada de adelante hacia atrás y +usar el metodo `unshift` en el nuevo array para insertar cada elemento en su +inicio. La segundo es hacer un ciclo sobre el array de entrada de atrás hacia +adelante y usar el método `push`. Iterar sobre un array al revés requiere de +una especificación (algo incómoda) del ciclo `for`, como +`(let i = array.length - 1; i >= 0; i--)`. {{index "slice method"}} -Reversing the array in place is harder. You have to be careful not to -overwrite elements that you will later need. Using `reverseArray` or -otherwise copying the whole array (`array.slice(0)` is a good way to -copy an array) works but is cheating. - -The trick is to _swap_ the first and last elements, then the second -and second-to-last, and so on. You can do this by looping over half -the length of the array (use `Math.floor` to round down—you don't need -to touch the middle element in an array with an odd number of -elements) and swapping the element at position `i` with the one at -position `array.length - 1 - i`. You can use a local binding to -briefly hold on to one of the elements, overwrite that one with its -mirror image, and then put the value from the local binding in the -place where the mirror image used to be. +Revertir al array en su lugar es más difícil. Tienes que tener cuidado de no +sobrescribir elementos que necesitarás luego. Usar `revertirArray` o +de lo contrario, copiar toda el array (`array.slice(0)` es una buena forma de +copiar un array) funciona pero estás haciendo trampa. + +El truco consiste en _intercambiar_ el primer y el último elemento, +luego el segundo y el penúltimo, y así sucesivamente. Puedes hacer esto +haciendo un ciclo basandote en la mitad de la longitud del array +(use `Math.floor` para redondear—no necesitas tocar el elemento del medio en +un array con un número impar de elementos) e intercambiar el elemento en +la posición `i` con el de la posición `array.length - 1 - i`. Puedes usar una +vinculación local para aferrarse brevemente a uno de los elementos, +sobrescribirlo con su imagen espejo, y luego poner el valor de la vinculación +local en el lugar donde solía estar la imagen espejo. hint}} {{id list}} -### A list +### Una lista -{{index ["data structure", list], "list (exercise)", "linked list", array, collection}} +{{index "data structure", "list (exercise)", "linked list", object, array, collection}} -Objects, as generic blobs of values, can be used to build all sorts of -data structures. A common data structure is the _list_ (not to be -confused with array). A list is a nested set of objects, with the -first object holding a reference to the second, the second to the -third, and so on. +Los objetos, como conjuntos genéricos de valores, se pueden usar para construir +todo tipo de estructuras de datos. Una estructura de datos común es la +_lista_ (no confundir con un array). Una lista es un conjunto anidado +de objetos, con el primer objeto conteniendo una referencia al segundo, el +segundo al tercero, y así sucesivamente. ```{includeCode: true} -let list = { - value: 1, - rest: { - value: 2, - rest: { - value: 3, - rest: null +let lista = { + valor: 1, + resto: { + valor: 2, + resto: { + valor: 3, + resto: null } } }; ``` -The resulting objects form a chain, like this: +Los objetos resultantes forman una cadena, como esta: -{{figure {url: "img/linked-list.svg", alt: "A linked list",width: "8cm"}}} +{{figure {url: "img/linked-list.svg", alt: "Una lista vinculada",width: "8cm"}}} -{{index "structure sharing", [memory, structure sharing]}} +{{index "structure sharing", memory}} -A nice thing about lists is that they can share parts of their -structure. For example, if I create two new values `{value: 0, rest: -list}` and `{value: -1, rest: list}` (with `list` referring to the -binding defined earlier), they are both independent lists, but they -share the structure that makes up their last three elements. The -original list is also still a valid three-element list. +Algo bueno de las listas es que pueden compartir partes de su +estructura. Por ejemplo, si creo dos nuevos valores `{valor: 0, resto: +lista}` y `{valor: -1, resto: lista}` (con `lista` refiriéndose a la +vinculación definida anteriormente), ambos son listas independientes, pero +comparten la estructura que conforma sus últimos tres elementos. +La lista original también sigue siendo una lista válida de tres elementos. -Write a function `arrayToList` that builds up a list structure like -the one shown when given `[1, 2, 3]` as argument. Also write a -`listToArray` function that produces an array from a list. Then add a -helper function `prepend`, which takes an element and a list and -creates a new list that adds the element to the front of the input -list, and `nth`, which takes a list and a number and returns the -element at the given position in the list (with zero referring to the -first element) or `undefined` when there is no such element. +Escribe una función `arrayALista` que construya una estructura de lista como +el que se muestra arriba cuando se le da `[1, 2, 3]` como argumento. +También escribe una función `listaAArray` que produzca un array de una lista. +Luego agrega una función de utilidad `preceder`, que tome un elemento y +una lista y creé una nueva lista que agrega el elemento al frente de la lista +de entrada, y `posicion`, que toma una lista y un número y retorne el +elemento en la posición dada en la lista (con cero refiriéndose al +primer elemento) o `undefined` cuando no exista tal elemento. {{index recursion}} -If you haven't already, also write a recursive version of `nth`. +Si aún no lo has hecho, también escribe una versión recursiva de `posicion`. {{if interactive ```{test: no} -// Your code here. +// Tu código aquí. -console.log(arrayToList([10, 20])); -// → {value: 10, rest: {value: 20, rest: null}} -console.log(listToArray(arrayToList([10, 20, 30]))); +console.log(arrayALista([10, 20])); +// → {valor: 10, resto: {valor: 20, resto: null}} +console.log(listaAArray(arrayALista([10, 20, 30]))); // → [10, 20, 30] -console.log(prepend(10, prepend(20, null))); -// → {value: 10, rest: {value: 20, rest: null}} -console.log(nth(arrayToList([10, 20, 30]), 1)); +console.log(preceder(10, preceder(20, null))); +// → {valor: 10, resto: {valor: 20, resto: null}} +console.log(posicion(arrayALista([10, 20, 30]), 1)); // → 20 ``` @@ -1550,78 +1567,79 @@ if}} {{index "list (exercise)", "linked list"}} -Building up a list is easier when done back to front. So `arrayToList` -could iterate over the array backwards (see the previous exercise) and, for -each element, add an object to the list. You can use a local binding -to hold the part of the list that was built so far and use an -assignment like `list = {value: X, rest: list}` to add an element. +Crear una lista es más fácil cuando se hace de atrás hacia adelante. Entonces +`arrayALista` podría iterar sobre el array hacia atrás +(ver ejercicio anterior) y, para cada elemento, agregar un objeto a la lista. +Puedes usar una vinculación local para mantener la parte de la lista que se +construyó hasta el momento y usar una asignación como +`lista = {valor: X, resto: lista}` para agregar un elemento. {{index "for loop"}} -To run over a list (in `listToArray` and `nth`), a `for` loop -specification like this can be used: +Para correr a traves de una lista (en `listaAArray` y `posicion`), +una especificación del ciclo `for` como esta se puede utilizar: ``` -for (let node = list; node; node = node.rest) {} +for (let nodo = lista; nodo; nodo = nodo.resto) {} ``` -Can you see how that works? Every iteration of the loop, `node` points -to the current sublist, and the body can read its `value` property to -get the current element. At the end of an iteration, `node` moves to -the next sublist. When that is null, we have reached the end of the -list, and the loop is finished. +Puedes ver cómo eso funciona? En cada iteración del ciclo, `nodo` apunta +a la sublista actual, y el cuerpo puede leer su propiedad `valor` para +obtener el elemento actual. Al final de una iteración, `nodo` se mueve a +la siguiente sublista. Cuando eso es nulo, hemos llegado al final de la +lista y el ciclo termina. {{index recursion}} -The recursive version of `nth` will, similarly, look at an ever -smaller part of the "tail" of the list and at the same time count down -the index until it reaches zero, at which point it can return the -`value` property of the node it is looking at. To get the zeroth -element of a list, you simply take the `value` property of its head -node. To get element _N_ + 1, you take the *N*th element of the list -that's in this list's `rest` property. +La versión recursiva de `posición`, de manera similar, mirará a una +parte más pequeña de la "cola" de la lista y, al mismo tiempo, contara atrás +el índice hasta que llegue a cero, en cuyo punto puede retornar la propiedad +`valor` del nodo que está mirando. Para obtener el elemento cero de una lista, +simplemente toma la propiedad `valor` de su nodo frontal. +Para obtener el elemento _N_ + 1, toma el elemento _N_ de la lista +que este en la propiedad `resto` de esta lista. hint}} {{id exercise_deep_compare}} -### Deep comparison +### Comparación profunda -{{index "deep comparison (exercise)", [comparison, deep], "deep comparison", "== operator"}} +{{index "deep comparison (exercise)", comparison, "deep comparison", "== operator"}} -The `==` operator compares objects by identity. But sometimes you'd -prefer to compare the values of their actual properties. +El operador `==` compara objetos por identidad. Pero a veces +preferirias comparar los valores de sus propiedades reales. -Write a function `deepEqual` that takes two values and returns true -only if they are the same value or are objects with the same -properties, where the values of the properties are equal when compared -with a recursive call to `deepEqual`. +Escribe una función `igualdadProfunda` que toma dos valores y retorne `true` +solo si tienen el mismo valor o son objetos con las mismas +propiedades, donde los valores de las propiedades sean iguales cuando +comparadas con una llamada recursiva a `igualdadProfunda`. {{index null, "=== operator", "typeof operator"}} -To find out whether values should be compared directly (use the `===` -operator for that) or have their properties compared, you can use the -`typeof` operator. If it produces `"object"` for both values, you -should do a deep comparison. But you have to take one silly exception -into account: because of a historical accident, `typeof null` also -produces `"object"`. +Para saber si los valores deben ser comparados directamente (usa el +operador `==` para eso) o si deben tener sus propiedades comparadas, +puedes usar el operador `typeof`. Si produce `"object"` para ambos valores, +deberías hacer una comparación profunda. Pero tienes que tomar una +excepción tonta en cuenta: debido a un accidente histórico, `typeof null` +también produce `"object"`. {{index "Object.keys function"}} -The `Object.keys` function will be useful when you need to go over the -properties of objects to compare them. +La función `Object.keys` será útil para cuando necesites revisar las +propiedades de los objetos para compararlos. {{if interactive ```{test: no} -// Your code here. +// Tu código aquí. -let obj = {here: {is: "an"}, object: 2}; -console.log(deepEqual(obj, obj)); +let objeto = {aqui: {hay: "un"}, objeto: 2}; +console.log(igualdadProfunda(objeto, objeto)); // → true -console.log(deepEqual(obj, {here: 1, object: 2})); +console.log(igualdadProfunda(objeto, {aqui: 1, object: 2})); // → false -console.log(deepEqual(obj, {here: {is: "an"}, object: 2})); +console.log(igualdadProfunda(objeto, {aqui: {hay: "un"}, objeto: 2})); // → true ``` @@ -1629,30 +1647,29 @@ if}} {{hint -{{index "deep comparison (exercise)", [comparison, deep], "typeof operator", "=== operator"}} +{{index "deep comparison (exercise)", "typeof operator", object, "=== operator"}} -Your test for whether you are dealing with a real object will look -something like `typeof x == "object" && x != null`. Be careful to -compare properties only when _both_ arguments are objects. In all -other cases you can just immediately return the result of applying -`===`. +Tu prueba de si estás tratando con un objeto real se verá +algo así como `typeof x == "object" && x != null`. Ten cuidado de +comparar propiedades solo cuando _ambos_ argumentos sean objetos. En todo los +otros casos, puede retornar inmediatamente el resultado de aplicar `===`. {{index "Object.keys function"}} -Use `Object.keys` to go over the properties. You need to test whether -both objects have the same set of property names and whether those -properties have identical values. One way to do that is to ensure that -both objects have the same number of properties (the lengths of the -property lists are the same). And then, when looping over one of the -object's properties to compare them, always first make sure -the other actually has a property by that name. If they have the same -number of properties and all properties in one also exist in the -other, they have the same set of property names. +Usa `Object.keys` para revisar las propiedades. Necesitas probar si +ambos objetos tienen el mismo conjunto de nombres de propiedad y si esos +propiedades tienen valores idénticos. Una forma de hacerlo es garantizar que +ambos objetos tengan el mismo número de propiedades (las longitudes de +las listas de propiedades son las mismas). Y luego, al hacer un ciclo sobre +una de las propiedades del objeto para compararlos, siempre asegúrate primero +de que el otro realmente tenga una propiedad con ese mismo nombre. +Si tienen el mismo número de propiedades, y todas las propiedades en uno +también existen en el otro, tienen el mismo conjunto de nombres de propiedad. {{index "return value"}} -Returning the correct value from the function is best done by -immediately returning false when a mismatch is found and returning -true at the end of the function. +Retornar el valor correcto de la función se realiza mejor al inmediatamente +retornar falso cuando se encuentre una discrepancia y retornar +verdadero al final de la función. hint}} diff --git a/05_higher_order.md b/05_higher_order.md index e8e9e1e49..df3f68a03 100644 --- a/05_higher_order.md +++ b/05_higher_order.md @@ -1,16 +1,16 @@ {{meta {load_files: ["code/scripts.js", "code/chapter/05_higher_order.js", "code/intro.js"], zip: "node/html"}}} -# Higher-Order Functions +# Funciones de Orden Superior {{if interactive {{quote {author: "Master Yuan-Ma", title: "The Book of Programming", chapter: true} -Tzu-li and Tzu-ssu were boasting about the size of their latest -programs. 'Two-hundred thousand lines,' said Tzu-li, 'not counting -comments!' Tzu-ssu responded, 'Pssh, mine is almost a *million* lines -already.' Master Yuan-Ma said, 'My best program has five hundred -lines.' Hearing this, Tzu-li and Tzu-ssu were enlightened. +Tzu-li y Tzu-ssu estaban jactándose del tamaño de sus ultimos +programas. 'Doscientas mil líneas', dijo Tzu-li, 'sin contar los +comentarios!' Tzu-ssu respondió, 'Pssh, el mío tiene casi un *millón* +de líneas ya.' El Maestro Yuan-Ma dijo, 'Mi mejor programa tiene quinientas +líneas.' Al escuchar esto, Tzu-li y Tzu-ssu fueron iluminados. quote}} @@ -20,128 +20,130 @@ if}} {{index "Hoare, C.A.R."}} -There are two ways of constructing a software design: One way is to -make it so simple that there are obviously no deficiencies, and the -other way is to make it so complicated that there are no obvious -deficiencies. +Hay dos formas de construir un diseño de software: Una forma es +hacerlo tan simple de manera que no hayan deficiencias obvias, y +la otra es hacerlo tan complicado de manera que +obviamente no hayan deficiencias. quote}} -{{figure {url: "img/chapter_picture_5.jpg", alt: "Letters from different scripts", chapter: true}}} +{{figure {url: "img/chapter_picture_5.jpg", alt: "Letras de diferentes idiomas", chapter: true}}} {{index "program size"}} -A large program is a costly program, and not just because of the time -it takes to build. Size almost always involves ((complexity)), and -complexity confuses programmers. Confused programmers, in turn, -introduce mistakes (_((bug))s_) into programs. A large program then -provides a lot of space for these bugs to hide, making them hard to -find. +Un programa grande es un programa costoso, y no solo por el tiempo que +se necesita para construirlo. El tamaño casi siempre involucra ((complejidad)), +y la complejidad confunde a los programadores. A su vez, los programadores +confundidos, introducen errores en los programas. Un programa grande entonces +proporciona de mucho espacio para que estos bugs se oculten, haciéndolos +difíciles de encontrar. {{index "summing example"}} -Let's briefly go back to the final two example programs in the -introduction. The first is self-contained and six lines long. +Volvamos rapidamente a los dos últimos programas de ejemplo en la +introducción. El primero es auto-contenido y solo tiene seis líneas de largo: ``` -let total = 0, count = 1; -while (count <= 10) { - total += count; - count += 1; +let total = 0, cuenta = 1; +while (cuenta <= 10) { + total += cuenta; + cuenta += 1; } console.log(total); ``` -The second relies on two external functions and is one line long. +El segundo depende de dos funciones externas y tiene una línea de longitud: ``` -console.log(sum(range(1, 10))); +console.log(suma(rango(1, 10))); ``` -Which one is more likely to contain a bug? +Cuál es más probable que contenga un bug? {{index "program size"}} -If we count the size of the definitions of `sum` and `range`, the -second program is also big—even bigger than the first. But still, I'd -argue that it is more likely to be correct. +Si contamos el tamaño de las definiciones de `suma` y `rango`, +el segundo programa también es grande—incluso puede que sea más grande que +el primero. Pero aún así, argumentaria que es más probable que sea correcto. -{{index [abstraction, "with higher-order functions"], "domain-specific language"}} +{{index abstraction, "domain-specific language"}} -It is more likely to be correct because the solution is expressed in a -((vocabulary)) that corresponds to the problem being solved. Summing a -range of numbers isn't about loops and counters. It is about ranges -and sums. +Es más probable que sea correcto porque la solución se expresa en un +((vocabulario)) que corresponde al problema que se está resolviendo. Sumar un +rango de números no se trata acerca de ciclos y contadores. Se trata acerca +de rangos y sumas. -The definitions of this vocabulary (the functions `sum` and `range`) -will still involve loops, counters, and other incidental details. But -because they are expressing simpler concepts than the program as a -whole, they are easier to get right. +Las definiciones de este vocabulario (las funciones `suma` y `rango`) +seguirán involucrando ciclos, contadores y otros detalles incidentales. Pero +ya que expresan conceptos más simples que el programa como un conjunto, +son más fáciles de realizar correctamente. -## Abstraction +## Abstracción -In the context of programming, these kinds of vocabularies are usually -called _((abstraction))s_. Abstractions hide details and give us the -ability to talk about problems at a higher (or more abstract) level. +En el contexto de la programación, estos tipos de vocabularios suelen ser +llamados _((abstraccione))s_. Las abstracciones esconden detalles y nos dan la +capacidad de hablar acerca de los problemas a un nivel superior +(o más abstracto). {{index "recipe analogy", "pea soup"}} -As an analogy, compare these two recipes for pea soup. The first one -goes like this: +Como una analogía, compara estas dos recetas de sopa de guisantes: {{quote -Put 1 cup of dried peas per person into a container. Add water until -the peas are well covered. Leave the peas in water for at least 12 -hours. Take the peas out of the water and put them in a cooking pan. -Add 4 cups of water per person. Cover the pan and keep the peas -simmering for two hours. Take half an onion per person. Cut it into -pieces with a knife. Add it to the peas. Take a stalk of celery per -person. Cut it into pieces with a knife. Add it to the peas. Take a -carrot per person. Cut it into pieces. With a knife! Add it to the -peas. Cook for 10 more minutes. +Coloque 1 taza de guisantes secos por persona en un recipiente. Agregue agua hasta +que los guisantes esten bien cubiertos. Deje los guisantes en agua durante al menos 12 +horas. Saque los guisantes del agua y pongalos en una cacerola para cocinar. +Agregue 4 tazas de agua por persona. Cubra la sartén y mantenga los guisantes +hirviendo a fuego lento durante dos horas. Tome media cebolla por persona. Cortela en +piezas con un cuchillo. Agréguela a los guisantes. Tome un tallo de apio por +persona. Cortelo en pedazos con un cuchillo. Agréguelo a los guisantes. Tome una +zanahoria por persona. Cortela en pedazos. Con un cuchillo! Agregarla a los +guisantes. Cocine por 10 minutos más. quote}} -And this is the second recipe: +Y la segunda receta: {{quote -Per person: 1 cup dried split peas, half a chopped onion, a stalk of -celery, and a carrot. +Por persona: 1 taza de guisantes secos, media cebolla picada, un tallo de +apio y una zanahoria. -Soak peas for 12 hours. Simmer for 2 hours in 4 cups of water -(per person). Chop and add vegetables. Cook for 10 more minutes. +Remoje los guisantes durante 12 horas. Cocine a fuego lento durante +2 horas en 4 tazas de agua (por persona). Picar y agregar verduras. +Cocine por 10 minutos más. quote}} {{index vocabulary}} -The second is shorter and easier to interpret. But you do need to -understand a few more cooking-related words such as _soak_, _simmer_, _chop_, -and, I guess, _vegetable_. +La segunda es más corta y fácil de interpretar. Pero necesitas +entender algunas palabras más relacionadas a la cocina—_remojar_, +_cocinar a fuego lento_, _picar_, y, supongo, _verduras_. -When programming, we can't rely on all the words we need to be waiting -for us in the dictionary. Thus, we might fall into the pattern of the -first recipe—work out the precise steps the computer has to perform, -one by one, blind to the higher-level concepts that they express. +Cuando programamos, no podemos confiar en que todas las palabras que necesitaremos +estaran esperando por nosotros en el diccionario. Por lo tanto, puedes caer +en el patrón de la primera receta—resolviendo los pasos precisos que debe +realizar la computadora, uno por uno, ciego a los conceptos de orden +superior que estos expresan. {{index abstraction}} -It is a useful skill, in programming, to notice when you are working -at too low a level of abstraction. +En la programación, es una habilidad útil, darse cuenta cuando estás trabajando +en un nivel de abstracción demasiado bajo. -## Abstracting repetition +## Abstrayendo la repetición -{{index [array, iteration]}} +{{index array}} -Plain functions, as we've seen them so far, are a good way to build -abstractions. But sometimes they fall short. +Las funciones simples, como las hemos visto hasta ahora, +son una buena forma de construir abstracciones. Pero a veces se quedan cortas. {{index "for loop"}} -It is common for a program to do something a given number of times. -You can write a `for` ((loop)) for that, like this: +Es común que un programa haga algo una determinada cantidad de veces. +Puedes escribir un ((ciclo)) `for` para eso, de esta manera: ``` for (let i = 0; i < 10; i++) { @@ -149,157 +151,158 @@ for (let i = 0; i < 10; i++) { } ``` -Can we abstract "doing something _N_ times" as a function? Well, it's -easy to write a function that calls `console.log` _N_ times. +Podemos abstraer "hacer algo _N_ veces" como una función? Bueno, es +fácil escribir una función que llame a `console.log` _N_ +cantidad de veces. ``` -function repeatLog(n) { +function repetirLog(n) { for (let i = 0; i < n; i++) { console.log(i); } } ``` -{{index [function, "higher-order"], loop, [function, "as value"]}} +{{index [function, "higher-order"], loop, [array, traversal], [function, "as value"]}} {{indexsee "higher-order function", "function, higher-order"}} -But what if we want to do something other than logging the numbers? -Since "doing something" can be represented as a function and functions -are just values, we can pass our action as a function value. +Pero, y si queremos hacer algo más que loggear los números? +Ya que "hacer algo" se puede representar como una función y que las funciones +solo son valores, podemos pasar nuestra acción como un valor de función. ```{includeCode: "top_lines: 5"} -function repeat(n, action) { +function repetir(n, accion) { for (let i = 0; i < n; i++) { - action(i); + accion(i); } } -repeat(3, console.log); +repetir(3, console.log); // → 0 // → 1 // → 2 ``` -We don't have to pass a predefined function to `repeat`. Often, it -is easier to create a function value on the spot instead. +No es necesario que le pases una función predefinida a `repetir`. A menudo, +desearas crear un valor de función al momento en su lugar. ``` -let labels = []; -repeat(5, i => { - labels.push(`Unit ${i + 1}`); +let etiquetas = []; +repetir(5, i => { + etiquetas.push(`Unidad ${i + 1}`); }); -console.log(labels); -// → ["Unit 1", "Unit 2", "Unit 3", "Unit 4", "Unit 5"] +console.log(etiquetas); +// → ["Unidad 1", "Unidad 2", "Unidad 3", "Unidad 4", "Unidad 5"] ``` -{{index "loop body", [braces, body], [parentheses, arguments]}} +{{index "loop body", "curly braces"}} -This is structured a little like a `for` loop—it first describes the -kind of loop and then provides a body. However, the body is now written -as a function value, which is wrapped in the parentheses of the -call to `repeat`. This is why it has to be closed with the closing -brace _and_ closing parenthesis. In cases like this example, where the -body is a single small expression, you could also omit the -braces and write the loop on a single line. +Esto está estructurado un poco como un ciclo `for`—primero describe el +tipo de ciclo, y luego provee un cuerpo. Sin embargo, el cuerpo ahora está escrito +como un valor de función, que está envuelto en el ((paréntesis)) de la +llamada a `repetir`. Por eso es que tiene que cerrarse con el corchete de cierre +_y_ paréntesis de cierre. En casos como este ejemplo, donde el +cuerpo es una expresión pequeña y única, podrias también omitir las +llaves y escribir el ciclo en una sola línea. -## Higher-order functions +## Funciones de orden superior {{index [function, "higher-order"], [function, "as value"]}} -Functions that operate on other functions, either by taking them as -arguments or by returning them, are called _higher-order functions_. -Since we have already seen that functions are regular values, there is -nothing particularly remarkable about the fact that such functions -exist. The term comes from ((mathematics)), where the distinction -between functions and other values is taken more seriously. +Las funciones que operan en otras funciones, ya sea tomándolas como +argumentos o retornandolas, se denominan _funciones de orden superior_. +Como ya hemos visto que las funciones son valores regulares, no existe +nada particularmente notable sobre el hecho de que tales funciones +existen. El término proviene de las ((matemáticas)), donde la distinción +entre funciones y otros valores se toma más en serio. {{index abstraction}} -Higher-order functions allow us to abstract over _actions_, not just -values. They come in several forms. For example, we can have -functions that create new functions. +Las funciones de orden superior nos permiten abstraer sobre _acciones_, +no solo sobre valores. Estas vienen en varias formas. Por ejemplo, puedes tener +funciones que crean nuevas funciones. ``` -function greaterThan(n) { +function mayorQue(n) { return m => m > n; } -let greaterThan10 = greaterThan(10); -console.log(greaterThan10(11)); +let mayorQue10 = mayorQue(10); +console.log(mayorQue10(11)); // → true ``` -And we can have functions that change other functions. +Y puedes tener funciones que cambien otras funciones. ``` -function noisy(f) { - return (...args) => { - console.log("calling with", args); - let result = f(...args); - console.log("called with", args, ", returned", result); - return result; +function ruidosa(funcion) { + return (...argumentos) => { + console.log("llamando con", argumentos); + let resultado = funcion(...argumentos); + console.log("llamada con", argumentos, ", retorno", resultado); + return resultado; }; } -noisy(Math.min)(3, 2, 1); -// → calling with [3, 2, 1] -// → called with [3, 2, 1] , returned 1 +ruidosa(Math.min)(3, 2, 1); +// → llamando con [3, 2, 1] +// → llamada con [3, 2, 1] , retorno 1 ``` -We can even write functions that provide new types of ((control -flow)). +Incluso puedes escribir funciones que proporcionen nuevos tipos de +((flujo de control)). ``` -function unless(test, then) { - if (!test) then(); +function aMenosQue(prueba, entonces) { + if (!prueba) entonces(); } -repeat(3, n => { - unless(n % 2 == 1, () => { - console.log(n, "is even"); +repetir(3, n => { + aMenosQue(n % 2 == 1, () => { + console.log(n, "es par"); }); }); -// → 0 is even -// → 2 is even +// → 0 es par +// → 2 es par ``` {{index [array, methods], [array, iteration], "forEach method"}} -There is a built-in array method, `forEach`, that provides something -like a `for`/`of` loop as a higher-order function. +Hay un método de array incorporado, `forEach` que proporciona algo +como un ciclo `for`/`of` como una función de orden superior. ``` -["A", "B"].forEach(l => console.log(l)); +["A", "B"].forEach(letra => console.log(letra)); // → A // → B ``` -## Script data set +## Conjunto de datos de códigos -One area where higher-order functions shine is data processing. To process data, we'll need some actual data. This chapter will -use a ((data set)) about scripts—((writing system))s such as Latin, -Cyrillic, or Arabic. +Un área donde brillan las funciones de orden superior es en el +procesamiento de datos. Para procesar datos, necesitaremos algunos datos reales. +Este capítulo usara un ((conjunto de datos)) acerca de +códigos—((sistema de escritura))s como Latin, Cirílico, o Arábico. -Remember ((Unicode)) from [Chapter ?](values#unicode), the system that -assigns a number to each character in written language? Most of these -characters are associated with a specific script. The standard -contains 140 different scripts—81 are still in use today, and 59 -are historic. +Recuerdas ((Unicode)) del [Capítulo 1](valores#unicode), el sistema que +asigna un número a cada carácter en el lenguaje escrito. La mayoría de estos +carácteres están asociados a un código específico. El estandar +contiene 140 codigos diferentes—81 de los cuales todavía están en uso hoy, y 59 +que son históricos. -Though I can fluently read only Latin characters, I appreciate the -fact that people are writing texts in at least 80 other writing -systems, many of which I wouldn't even recognize. For example, here's -a sample of ((Tamil)) handwriting: +Aunque solo puedo leer con fluidez los caracteres en Latin, aprecio el +hecho de que las personas estan escribiendo textos en al menos 80 diferentes +sistemas de escritura, muchos de los cuales ni siquiera reconocería. Por ejemplo, +aquí está una muestra de escritura a mano en ((Tamil)). {{figure {url: "img/tamil.png", alt: "Tamil handwriting"}}} {{index "SCRIPTS data set"}} -The example ((data set)) contains some pieces of information about the -140 scripts defined in Unicode. It is available in the [coding -sandbox](https://eloquentjavascript.net/code#5) for this chapter[ -([_https://eloquentjavascript.net/code#5_](https://eloquentjavascript.net/code#5))]{if -book} as the `SCRIPTS` binding. The binding contains an array of -objects, each of which describes a script. +El ((conjunto de datos)) de ejemplo contiene algunos piezas de información +acerca de los 140 codigos definidos en Unicode. Este esta disponible en la [caja de arena](https://eloquentjavascript.net/code#5) para este capítulo [ +([_eloquentjavascript.net/code#5_](https://eloquentjavascript.net/code#5))]{if +book} como la vinculación `SCRIPTS`. La vinculación contiene un array de +objetos, cada uno de los cuales describe un codigo. ```{lang: "application/json"} @@ -313,130 +316,131 @@ objects, each of which describes a script. } ``` -Such an object tells us the name of the script, the Unicode ranges -assigned to it, the direction in which it is written, the -(approximate) origin time, whether it is still in use, and a link to -more information. The direction may be `"ltr"` for left to right, `"rtl"` -for right to left (the way Arabic and Hebrew text are written), or -`"ttb"` for top to bottom (as with Mongolian writing). +Tal objeto te dice el nombre del codigo, los rangos de Unicode +asignados a él, la dirección en la que está escrito, la +tiempo de origen (aproximado), si todavía está en uso, y un enlace a +más información. La dirección en la que esta escrito puede ser +`"ltr"` (left-to-right) para izquierda a derecha, `"rtl"` (right-to-left) +para derecha a izquierda (la forma en que se escriben los textos en árabe +y en hebreo), o `"ttb"` (top-to-bottom) para de arriba a abajo +(como con la escritura de Mongolia). {{index "slice method"}} -The `ranges` property contains an array of Unicode character -((range))s, each of which is a two-element array containing a lower bound -and an upper bound. Any character codes within these ranges are assigned -to the script. The lower ((bound)) is inclusive (code 994 is a Coptic -character), and the upper bound is non-inclusive (code 1008 isn't). +La propiedad `ranges` contiene un array de ((rango))s de caracteres Unicode, +cada uno de los cuales es un array de dos elementos que contiene límites inferior +y superior. Se asignan los códigos de caracteres dentro de estos rangos +al codigo. El ((limite)) más bajo es inclusivo (el código 994 es un carácter Copto) +y el límite superior es no-inclusivo (el código 1008 no lo es). -## Filtering arrays +## Filtrando arrays {{index [array, methods], [array, filtering], "filter method", [function, "higher-order"], "predicate function"}} -To find the scripts in the data set that are still in use, the -following function might be helpful. It filters out the elements in an -array that don't pass a test. +Para encontrar los codigos en el conjunto de datos que todavía están en uso, +la siguiente función podría ser útil. Filtra hacia afuera los elementos en un +array que no pasen una prueba: ``` -function filter(array, test) { - let passed = []; - for (let element of array) { - if (test(element)) { - passed.push(element); +function filtrar(array, prueba) { + let pasaron = []; + for (let elemento of array) { + if (prueba(elemento)) { + pasaron.push(elemento); } } - return passed; + return pasaron; } -console.log(filter(SCRIPTS, script => script.living)); +console.log(filtrar(SCRIPTS, codigo => codigo.living)); // → [{name: "Adlam", …}, …] ``` {{index [function, "as value"], [function, application]}} -The function uses the argument named `test`, a function value, to fill -a "gap" in the computation—the process of deciding which elements to -collect. +La función usa el argumento llamado `prueba`, un valor de función, para llenar +una "brecha" en el cálculo—el proceso de decidir qué elementos recolectar. {{index "filter method", "pure function", "side effect"}} -Note how the `filter` function, rather than deleting elements from the -existing array, builds up a new array with only the elements that pass -the test. This function is _pure_. It does not modify the array it is -given. +Observa cómo la función `filtrar`, en lugar de eliminar elementos del +array existente, crea un nuevo array solo con los elementos que pasan +la prueba. Esta función es _pura_. No modifica el array que se le es +dado. -Like `forEach`, `filter` is a ((standard)) array method. The example -defined the function only to show what it does internally. -From now on, we'll use it like this instead: +Al igual que `forEach`, `filtrar` es un método de array ((estándar)), este +esta incorporado como `filter`. +El ejemplo definió la función solo para mostrar lo que hace internamente. +A partir de ahora, la usaremos así en su lugar: ``` -console.log(SCRIPTS.filter(s => s.direction == "ttb")); +console.log(SCRIPTS.filter(codigo => codigo.direction == "ttb")); // → [{name: "Mongolian", …}, …] ``` {{id map}} -## Transforming with map +## Transformando con map {{index [array, methods], "map method"}} -Say we have an array of objects representing scripts, produced by -filtering the `SCRIPTS` array somehow. But we want an array of names, -which is easier to inspect. +Digamos que tenemos un array de objetos que representan codigos, producidos al +filtrar el array `SCRIPTS` de alguna manera. Pero queremos un array de nombres, +que es más fácil de inspeccionar {{index [function, "higher-order"]}} -The `map` method transforms an array by applying a function to all of -its elements and building a new array from the returned values. The -new array will have the same length as the input array, but its -content will have been _mapped_ to a new form by the function. +El método `map` ("mapear") transforma un array al aplicar una función a todos +sus elementos y construir un nuevo array a partir de los valores retornados. +El nuevo array tendrá la misma longitud que el array de entrada, pero su +contenido ha sido _mapeado_ a una nueva forma en base a la función. ``` -function map(array, transform) { - let mapped = []; - for (let element of array) { - mapped.push(transform(element)); +function map(array, transformar) { + let mapeados = []; + for (let elemento of array) { + mapeados.push(transformar(elemento)); } - return mapped; + return mapeados; } -let rtlScripts = SCRIPTS.filter(s => s.direction == "rtl"); -console.log(map(rtlScripts, s => s.name)); +let codigosDerechaAIzquierda = SCRIPTS.filter(codigo => codigo.direction == "rtl"); +console.log(map(codigosDerechaAIzquierda, codigo => codigo.name)); // → ["Adlam", "Arabic", "Imperial Aramaic", …] ``` -Like `forEach` and `filter`, `map` is a standard array method. +Al igual que `forEach` y `filter`, `map` es un método de array estándar. -## Summarizing with reduce +## Resumiendo con reduce {{index [array, methods], "summing example", "reduce method"}} -Another common thing to do with arrays is to compute a single value -from them. Our recurring example, summing a collection of numbers, is -an instance of this. Another example is finding the script with -the most characters. +Otra cosa común que hacer con arrays es calcular un valor único a partir +de ellos. Nuestro ejemplo recurrente, sumar una colección de números, es +una instancia de esto. Otro ejemplo sería encontrar el codigo con +la mayor cantidad de caracteres. {{indexsee "fold", "reduce method"}} {{index [function, "higher-order"], "reduce method"}} -The higher-order operation that represents this pattern is called -_reduce_ (sometimes also called _fold_). It builds a value by -repeatedly taking a single element from the array and combining it -with the current value. When summing numbers, you'd start with the -number zero and, for each element, add that to the sum. +La operación de orden superior que representa este patrón se llama +_reduce_ ("reducir")—a veces también llamada _fold_ ("doblar"). +Esta construye un valor al repetidamente tomar un solo elemento del +array y combinándolo con el valor actual. Al sumar números, comenzarías con el +número cero y, para cada elemento, agregas eso a la suma. -The parameters to `reduce` are, apart from the array, a combining -function and a start value. This function is a little less -straightforward than `filter` and `map`, so take a close look at -it: +Los parámetros para `reduce` son, además del array, una función de combinación +y un valor de inicio. Esta función es un poco menos sencilla que `filter` +y `map`, así que mira atentamente: ``` -function reduce(array, combine, start) { - let current = start; - for (let element of array) { - current = combine(current, element); +function reduce(array, combinar, inicio) { + let actual = inicio; + for (let elemento of array) { + actual = combinar(actual, elemento); } - return current; + return actual; } console.log(reduce([1, 2, 3, 4], (a, b) => a + b, 0)); @@ -445,11 +449,11 @@ console.log(reduce([1, 2, 3, 4], (a, b) => a + b, 0)); {{index "reduce method", "SCRIPTS data set"}} -The standard array method `reduce`, which of course corresponds to -this function, has an added convenience. If your array contains at -least one element, you are allowed to leave off the `start` argument. -The method will take the first element of the array as its start value -and start reducing at the second element. +El método de array estándar `reduce`, que por supuesto corresponde a +esta función tiene una mayor comodidad. Si tu array contiene +al menos un elemento, tienes permitido omitir el argumento `inicio`. +El método tomará el primer elemento del array como su valor de inicio +y comienza a reducir a partir del segundo elemento. ``` console.log([1, 2, 3, 4].reduce((a, b) => a + b)); @@ -458,360 +462,359 @@ console.log([1, 2, 3, 4].reduce((a, b) => a + b)); {{index maximum, "characterCount function"}} -To use `reduce` (twice) to find the script with the most characters, -we can write something like this: +Para usar `reduce` (dos veces) para encontrar el codigo con la mayor +cantidad de caracteres, podemos escribir algo como esto: ``` -function characterCount(script) { - return script.ranges.reduce((count, [from, to]) => { - return count + (to - from); +function cuentaDeCaracteres(codigo) { + return codigo.ranges.reduce((cuenta, [desde, hasta]) => { + return cuenta + (hasta - desde); }, 0); } console.log(SCRIPTS.reduce((a, b) => { - return characterCount(a) < characterCount(b) ? b : a; + return cuentaDeCaracteres(a) < cuentaDeCaracteres(b) ? b : a; })); // → {name: "Han", …} ``` -The `characterCount` function reduces the ranges assigned to a script -by summing their sizes. Note the use of destructuring in the parameter -list of the reducer function. The second call to `reduce` then uses -this to find the largest script by repeatedly comparing two scripts -and returning the larger one. +La función `cuentaDeCaracteres` reduce los rangos asignados a un codigo +sumando sus tamaños. Ten en cuenta el uso de la desestructuración en el parámetro +lista de la función reductora. La segunda llamada a `reduce` luego usa +esto para encontrar el codigo más grande al comparar repetidamente dos scripts +y retornando el más grande. -The Han script has more than 89,000 characters assigned to it in the -Unicode standard, making it by far the biggest writing system in the -data set. Han is a script (sometimes) used for Chinese, Japanese, and -Korean text. Those languages share a lot of characters, though they -tend to write them differently. The (U.S.-based) Unicode Consortium -decided to treat them as a single writing system to save -character codes. This is called _Han unification_ and still makes some -people very angry. +El codigo Han tiene más de 89,000 caracteres asignados en el +Estándar Unicode, por lo que es, por mucho, el mayor sistema de escritura en el +conjunto de datos. Han es un codigo (a veces) usado para texto chino, japonés y +coreano. Esos idiomas comparten muchos caracteres, aunque +tienden a escribirlos de manera diferente. El consorcio Unicode (con sede en EE.UU.) +decidió tratarlos como un único sistema de escritura para ahorrar +códigos de caracteres. Esto se llama _unificación Han_ y aún enoja bastante a +algunas personas. -## Composability +## Composabilidad {{index loop, maximum}} -Consider how we would have written the previous example (finding the -biggest script) without higher-order functions. The code is not that -much worse. +Considera cómo habríamos escrito el ejemplo anterior (encontrar el +código más grande) sin funciones de orden superior. El código no es +mucho peor. ```{test: no} -let biggest = null; -for (let script of SCRIPTS) { - if (biggest == null || - characterCount(biggest) < characterCount(script)) { - biggest = script; +let mayor = null; +for (let codigo of SCRIPTS) { + if (mayor == null || + cuentaDeCaracteres(mayor) < cuentaDeCaracteres(codigo)) { + mayor = codigo; } } -console.log(biggest); +console.log(mayor); // → {name: "Han", …} ``` -There are a few more bindings, and the program is four lines -longer. But it is still very readable. +Hay algunos vinculaciones más, y el programa tiene cuatro líneas +más. Pero todavía es bastante legible. {{index "average function", composability, [function, "higher-order"], "filter method", "map method", "reduce method"}} {{id average_function}} -Higher-order functions start to shine when you need to _compose_ -operations. As an example, let's write code that finds the average -year of origin for living and dead scripts in the data set. +Las funciones de orden superior comienzan a brillar cuando necesitas _componer_ +operaciones. Como ejemplo, vamos a escribir código que encuentre el +año de origen promedio para los codigos vivos y muertos en el conjunto de datos. ``` -function average(array) { +function promedio(array) { return array.reduce((a, b) => a + b) / array.length; } -console.log(Math.round(average( - SCRIPTS.filter(s => s.living).map(s => s.year)))); -// → 1165 -console.log(Math.round(average( - SCRIPTS.filter(s => !s.living).map(s => s.year)))); -// → 204 +console.log(Math.round(promedio( + SCRIPTS.filter(codigo => codigo.living).map(codigo => codigo.year)))); +// → 1185 +console.log(Math.round(promedio( + SCRIPTS.filter(codigo => !codigo.living).map(codigo => codigo.year)))); +// → 209 ``` -So the dead scripts in Unicode are, on average, older than the living -ones. This is not a terribly meaningful or surprising statistic. But I -hope you'll agree that the code used to compute it isn't hard to read. -You can see it as a pipeline: we start with all scripts, filter out -the living (or dead) ones, take the years from those, average them, -and round the result. +Entonces, los codigos muertos en Unicode son, en promedio, más antiguos que +los vivos. Esta no es una estadística terriblemente significativa o sorprendente. +Pero espero que aceptes que el código utilizado para calcularlo no es difícil +de leer. Puedes verlo como una tubería: comenzamos con todos los codigos, filtramos +los vivos (o muertos), tomamos los años de aquellos, los promediamos, +y redondeamos el resultado. -You could definitely also write this computation as one big ((loop)). +Definitivamente también podrías haber escribir este codigo como un gran ((ciclo)). ``` -let total = 0, count = 0; -for (let script of SCRIPTS) { - if (script.living) { - total += script.year; - count += 1; +let total = 0, cuenta = 0; +for (let codigo of SCRIPTS) { + if (codigo.living) { + total += codigo.year; + cuenta += 1; } } -console.log(Math.round(total / count)); -// → 1165 +console.log(Math.round(total / cuenta)); +// → 1185 ``` -But it is harder to see what was being computed and how. And because -intermediate results aren't represented as coherent values, it'd be a -lot more work to extract something like `average` into a separate -function. +Pero es más difícil de ver qué se está calculando y cómo. Y ya que +los resultados intermedios no se representan como valores coherentes, sería +mucho más trabajo extraer algo así como `promedio` en una función aparte. -{{index efficiency, [array, creation]}} +{{index efficiency}} -In terms of what the computer is actually doing, these two approaches -are also quite different. The first will build up new arrays when -running `filter` and `map`, whereas the second computes only some -numbers, doing less work. You can usually afford the readable -approach, but if you're processing huge arrays, and doing so many -times, the less abstract style might be worth the extra speed. +En términos de lo que la computadora realmente está haciendo, estos dos enfoques +también son bastante diferentes. El primero creará nuevos ((arrays)) al +ejecutar `filter` y `map`, mientras que el segundo solo computa algunos +números, haciendo menos trabajo. Por lo general, puedes permitirte el +enfoque legible, pero si estás procesando arrays enormes, y haciendolo muchas +veces, el estilo menos abstracto podría ser mejor debido a la velocidad extra. -## Strings and character codes +## Strings y códigos de caracteres {{index "SCRIPTS data set"}} -One use of the data set would be figuring out what script a piece of -text is using. Let's go through a program that does this. +Un uso del conjunto de datos sería averiguar qué código esta usando una +pieza de texto. Veamos un programa que hace esto. -Remember that each script has an array of character code ranges -associated with it. So given a character code, we could use a function -like this to find the corresponding script (if any): +Recuerda que cada codigo tiene un array de rangos para los códigos de caracteres +asociados a el. Entonces, dado un código de carácter, podríamos usar una +función como esta para encontrar el codigo correspondiente (si lo hay): {{index "some method", "predicate function", [array, methods]}} ```{includeCode: strip_log} -function characterScript(code) { - for (let script of SCRIPTS) { - if (script.ranges.some(([from, to]) => { - return code >= from && code < to; +function codigoCaracter(codigo_caracter) { + for (let codigo of SCRIPTS) { + if (codigo.ranges.some(([desde, hasta]) => { + return codigo_caracter >= desde && codigo_caracter < hasta; })) { - return script; + return codigo; } } return null; } -console.log(characterScript(121)); +console.log(codigoCaracter(121)); // → {name: "Latin", …} ``` -The `some` method is another higher-order function. It takes a test -function and tells you whether that function returns true for any of the -elements in the array. +El método `some` ("alguno") es otra función de orden superior. Toma una función +de prueba y te dice si esa función retorna verdadero para cualquiera de los +elementos en el array. {{id code_units}} -But how do we get the character codes in a string? +Pero cómo obtenemos los códigos de los caracteres en un string? -In [Chapter ?](values) I mentioned that JavaScript ((string))s are -encoded as a sequence of 16-bit numbers. These are called _((code -unit))s_. A ((Unicode)) ((character)) code was initially supposed to -fit within such a unit (which gives you a little over 65,000 -characters). When it became clear that wasn't going to be enough, many -people balked at the need to use more memory per character. To address -these concerns, ((UTF-16)), the format used by JavaScript strings, was -invented. It describes most common characters using a single 16-bit -code unit but uses a pair of two such units for others. +En el [Capítulo 1](valores) mencioné que los ((strings)) de JavaScript estan +codificados como una secuencia de números de 16 bits. Estos se llaman +_((unidades de código))_. Inicialmente se suponía que un código de ((carácter)) +((Unicode)) encajara dentro de esa unidad (lo que da un poco más de 65,000 +caracteres). Cuando quedó claro que esto no seria suficiente, muchas +personas se resistieron a la necesidad de usar más memoria por carácter. +Para apaciguar estas preocupaciones, ((UTF-16)), el formato utilizado por los +strings de JavaScript, fue inventado. Este describe la mayoría de los caracteres +mas comunes usando una sola unidad de código de 16 bits, pero usa un par de +dos de esas unidades para otros caracteres. {{index error}} -UTF-16 is generally considered a bad idea today. It seems almost -intentionally designed to invite mistakes. It's easy to write programs -that pretend code units and characters are the same thing. And if your -language doesn't use two-unit characters, that will appear to work -just fine. But as soon as someone tries to use such a program with -some less common ((Chinese characters)), it breaks. Fortunately, with -the advent of ((emoji)), everybody has started using two-unit -characters, and the burden of dealing with such problems is more -fairly distributed. +Al dia de hoy UTF-16 generalmente se considera como una mala idea. Parece casi +intencionalmente diseñado para invitar a errores. Es fácil escribir programas +que pretenden que las unidades de código y caracteres son la misma cosa. Y si tu +lenguaje no usa caracteres de dos unidades, esto parecerá funcionar +simplemente bien. Pero tan pronto como alguien intente usar dicho programa con +algunos menos comunes ((caracteres chinos)), este se rompe. Afortunadamente, con +la llegada del ((emoji)), todo el mundo ha empezado a usar caracteres de +dos unidades, y la carga de lidiar con tales problemas esta +bastante mejor distribuida. {{index [string, length], [string, indexing], "charCodeAt method"}} -Unfortunately, obvious operations on JavaScript strings, such as -getting their length through the `length` property and accessing their -content using square brackets, deal only with code units. +Desafortunadamente, las operaciones obvias con strings de JavaScript, como +obtener su longitud a través de la propiedad `length` y acceder a su +contenido usando corchetes, trata solo con unidades de código. ```{test: no} -// Two emoji characters, horse and shoe -let horseShoe = "🐴👟"; -console.log(horseShoe.length); +// Dos caracteres emoji, caballo y zapato +let caballoZapato = "🐴👟"; +console.log(caballoZapato.length); // → 4 -console.log(horseShoe[0]); -// → (Invalid half-character) -console.log(horseShoe.charCodeAt(0)); -// → 55357 (Code of the half-character) -console.log(horseShoe.codePointAt(0)); -// → 128052 (Actual code for horse emoji) +console.log(caballoZapato[0]); +// → ((Medio-carácter inválido)) +console.log(caballoZapato.charCodeAt(0)); +// → 55357 (Código del medio-carácter) +console.log(caballoZapato.codePointAt(0)); +// → 128052 (Código real para emoji de caballo) ``` {{index "codePointAt method"}} -JavaScript's `charCodeAt` method gives you a code unit, not a full -character code. The `codePointAt` method, added later, does give a -full Unicode character. So we could use that to get characters from a -string. But the argument passed to `codePointAt` is still an index -into the sequence of code units. So to run over all characters in a -string, we'd still need to deal with the question of whether a -character takes up one or two code units. +El método `charCodeAt` de JavaScript te da una unidad de código, no un +código de carácter completo. El método `codePointAt`, añadido despues, si da un +carácter completo de Unicode. Entonces podríamos usarlo para obtener +caracteres de un string. Pero el argumento pasado a `codePointAt` sigue +siendo un índice en la secuencia de unidades de código. Entonces, para hacer +un ciclo a traves de todos los caracteres en un string, todavía tendríamos que +lidiar con la cuestión de si un carácter ocupa una o dos unidades de código. {{index "for/of loop", character}} -In the [previous chapter](data#for_of_loop), I mentioned that a -`for`/`of` loop can also be used on strings. Like `codePointAt`, this -type of loop was introduced at a time where people were acutely aware -of the problems with UTF-16. When you use it to loop over a string, it -gives you real characters, not code units. +En el [capítulo anterior](datos#for_of_loop), mencioné que el ciclo +`for`/`of` también se puede usar en strings. Como `codePointAt`, este +tipo de ciclo se introdujo en un momento en que las personas eran muy +conscientes de los problemas con UTF-16. Cuando lo usas para hacer un ciclo +a traves de un string, te da caracteres reales, no unidades de código. ``` -let roseDragon = "🌹🐉"; -for (let char of roseDragon) { - console.log(char); +let dragonRosa = "🐉🌹"; +for (let caracter of dragonRosa) { + console.log(caracter); } -// → 🌹 // → 🐉 +// → 🌹 ``` -If you have a character (which will be a string of one or two code -units), you can use `codePointAt(0)` to get its code. +Si tienes un caracter (que será un string de unidades de uno o dos códigos), +puedes usar `codePointAt(0)` para obtener su código. -## Recognizing text +## Reconociendo texto -{{index "SCRIPTS data set", "countBy function", [array, counting]}} +{{index "SCRIPTS data set", "countBy function", array}} -We have a `characterScript` function and a way to correctly loop over -characters. The next step is to count the characters that belong -to each script. The following counting abstraction will be useful -there: +Tenemos una función `codigoCaracter` y una forma de correctamente hacer un +ciclo a traves de caracteres. El siguiente paso sería contar los caracteres +que pertenecen a cada codigo. La siguiente abstracción de conteo será útil +para eso: ```{includeCode: strip_log} -function countBy(items, groupName) { - let counts = []; - for (let item of items) { - let name = groupName(item); - let known = counts.findIndex(c => c.name == name); - if (known == -1) { - counts.push({name, count: 1}); +function contarPor(elementos, nombreGrupo) { + let cuentas = []; + for (let elemento of elementos) { + let nombre = nombreGrupo(elemento); + let conocido = cuentas.findIndex(c => c.nombre == nombre); + if (conocido == -1) { + cuentas.push({nombre, cuenta: 1}); } else { - counts[known].count++; + cuentas[conocido].cuenta++; } } - return counts; + return cuentas; } -console.log(countBy([1, 2, 3, 4, 5], n => n > 2)); -// → [{name: false, count: 2}, {name: true, count: 3}] +console.log(contarPor([1, 2, 3, 4, 5], n => n > 2)); +// → [{nombre: false, cuenta: 2}, {nombre: true, cuenta: 3}] ``` -The `countBy` function expects a collection (anything that we can loop -over with `for`/`of`) and a function that computes a group name for a -given element. It returns an array of -objects, each of which names a group and tells you the number of -elements that were found in that group. +La función `contarPor` espera una colección (cualquier cosa con la que podamos +hacer un ciclo `for`/`of`) y una función que calcula un nombre de grupo para un +elemento dado. Retorna un array de objetos, cada uno de los cuales nombra un +grupo y te dice la cantidad de elementos que se encontraron en ese grupo. {{index "findIndex method", "indexOf method"}} -It uses another array method—`findIndex`. This method is somewhat like -`indexOf`, but instead of looking for a specific value, it finds the -first value for which the given function returns true. Like `indexOf`, -it returns -1 when no such element is found. +Utiliza otro método de array—`findIndex` ("encontrar index"). Este método es +algo así como `indexOf`, pero en lugar de buscar un valor específico, este +encuentra el primer valor para el cual la función dada retorna verdadero. +Como `indexOf`, retorna -1 cuando no se encuentra dicho elemento. {{index "textScripts function", "Chinese characters"}} -Using `countBy`, we can write the function that tells us which scripts -are used in a piece of text. +Usando `contarPor`, podemos escribir la función que nos dice qué codigos +se usan en una pieza de texto. ```{includeCode: strip_log, startCode: true} -function textScripts(text) { - let scripts = countBy(text, char => { - let script = characterScript(char.codePointAt(0)); - return script ? script.name : "none"; - }).filter(({name}) => name != "none"); +function codigosTexto(texto) { + let codigos = contarPor(texto, caracter => { + let codigo = codigoCaracter(caracter.codePointAt(0)); + return codigo ? codigo.name : "ninguno"; + }).filter(({name}) => name != "ninguno"); - let total = scripts.reduce((n, {count}) => n + count, 0); - if (total == 0) return "No scripts found"; + let total = codigos.reduce((n, {count}) => n + count, 0); + if (total == 0) return "No se encontraron codigos"; - return scripts.map(({name, count}) => { + return codigos.map(({name, count}) => { return `${Math.round(count * 100 / total)}% ${name}`; }).join(", "); } -console.log(textScripts('英国的狗说"woof", 俄罗斯的狗说"тяв"')); +console.log(codigosTexto('英国的狗说"woof", 俄罗斯的狗说"тяв"')); // → 61% Han, 22% Latin, 17% Cyrillic ``` {{index "characterScript function", "filter method"}} -The function first counts the characters by name, using -`characterScript` to assign them a name and falling back to the -string `"none"` for characters that aren't part of any script. The -`filter` call drops the entry for `"none"` from the resulting array -since we aren't interested in those characters. +La función primero cuenta los caracteres por nombre, usando +`codigoCaracter` para asignarles un nombre, y recurre al +string `"ninguno"` para caracteres que no son parte de ningún codigo. +La llamada `filter` deja afuera las entrada para `"ninguno"` del array resultante, +ya que no estamos interesados ​​en esos caracteres. {{index "reduce method", "map method", "join method", [array, methods]}} -To be able to compute ((percentage))s, we first need the total number -of characters that belong to a script, which we can compute with -`reduce`. If no such characters are found, the function returns a -specific string. Otherwise, it transforms the counting entries into -readable strings with `map` and then combines them with `join`. +Para poder calcular ((porcentaje))s, primero necesitamos la cantidad total +de caracteres que pertenecen a un codigo, lo que podemos calcular con +`reduce`. Si no se encuentran tales caracteres, la función retorna un +string específico. De lo contrario, transforma las entradas de conteo en +strings legibles con `map` y luego las combina con `join`. -## Summary +## Resumen -Being able to pass function values to other functions is a deeply -useful aspect of JavaScript. It allows us to write functions that -model computations with "gaps" in them. The code that calls these -functions can fill in the gaps by providing function values. +Ser capaz de pasar valores de función a otras funciones es un aspecto +profundamente útil de JavaScript. Nos permite escribir funciones que +modelen calculos con "brechas" en ellas. El código que llama a estas +funciones pueden llenar estas brechas al proporcionar valores de función. -Arrays provide a number of useful higher-order methods. You can use -`forEach` to loop over the elements in an array. The `filter` method -returns a new array containing only the elements that pass the -((predicate function)). Transforming an array by putting each element -through a function is done with `map`. You can use `reduce` to combine -all the elements in an array into a single value. The `some` method -tests whether any element matches a given predicate function. And -`findIndex` finds the position of the first element that matches a -predicate. +Los arrays proporcionan varios métodos útiles de orden superior. Puedes usar +`forEach` para recorrer los elementos en un array. El método `filter` +retorna un nuevo array que contiene solo los elementos que pasan una +((función de predicado)). Transformar un array al poner cada elemento +a través de una función se hace con `map`. Puedes usar `reduce` para combinar +todos los elementos en una array a un solo valor. El método `some` +prueba si algun elemento coincide con una función de predicado determinada. Y +`findIndex` encuentra la posición del primer elemento que coincide con un +predicado. -## Exercises +## Ejercicios -### Flattening +### Aplanamiento -{{index "flattening (exercise)", "reduce method", "concat method", [array, flattening]}} +{{index "flattening (exercise)", "reduce method", "concat method", array}} -Use the `reduce` method in combination with the `concat` method to -"flatten" an array of arrays into a single array that has all the -elements of the original arrays. +Use el método `reduce` en combinación con el método `concat` para +"aplanar" un array de arrays en un único array que tenga todos los +elementos de los arrays originales. {{if interactive ```{test: no} let arrays = [[1, 2, 3], [4, 5], [6]]; -// Your code here. +// Tu código aquí. // → [1, 2, 3, 4, 5, 6] ``` if}} -### Your own loop +### Tu propio ciclo {{index "your own loop (example)", "for loop"}} -Write a higher-order function `loop` that provides something like a -`for` loop statement. It takes a value, a test function, an update -function, and a body function. Each iteration, it first runs the test -function on the current loop value and stops if that returns false. -Then it calls the body function, giving it the current value. -Finally, it calls the update function to create a new value and -starts from the beginning. +Escriba una función de orden superior llamada `ciclo` que proporcione algo así +como una declaración de ciclo `for`. Esta toma un valor, una función de prueba, +una función de actualización y un cuerpo de función. En cada iteración, +primero ejecuta la función de prueba en el valor actual del ciclo y se detiene +si esta retorna falso. Luego llama al cuerpo de función, dándole el valor +actual. Y finalmente, llama a la función de actualización para crear un nuevo +valor y comienza desde el principio. -When defining the function, you can use a regular loop to do the -actual looping. +Cuando definas la función, puedes usar un ciclo regular para hacer los +ciclos reales. {{if interactive ```{test: no} -// Your code here. +// Tu código aquí. loop(3, n => n > 0, n => n - 1, console.log); // → 3 @@ -821,31 +824,31 @@ loop(3, n => n > 0, n => n - 1, console.log); if}} -### Everything +### Cada {{index "predicate function", "everything (exercise)", "every method", "some method", [array, methods], "&& operator", "|| operator"}} -Analogous to the `some` method, arrays also have an `every` method. -This one returns true when the given function returns true for _every_ -element in the array. In a way, `some` is a version of the `||` -operator that acts on arrays, and `every` is like the `&&` operator. +De forma análoga al método `some`, los arrays también tienen un método `every` +("cada"). Este retorna true cuando la función dada devuelve verdadero +para _cada_ elemento en el array. En cierto modo, `some` es una versión +del operador `||` que actúa en arrays, y `every` es como el operador `&&`. -Implement `every` as a function that takes an array and a predicate -function as parameters. Write two versions, one using a loop and one -using the `some` method. +Implementa `every` como una función que tome un array y una función predicado +como parámetros. Escribe dos versiones, una usando un ciclo y una +usando el método `some`. {{if interactive ```{test: no} -function every(array, test) { - // Your code here. +function cada(array, test) { + // Tu código aquí. } -console.log(every([1, 3, 5], n => n < 10)); +console.log(cada([1, 3, 5], n => n < 10)); // → true -console.log(every([2, 4, 16], n => n < 10)); +console.log(cada([2, 4, 16], n => n < 10)); // → false -console.log(every([], n => n < 10)); +console.log(cada([], n => n < 10)); // → true ``` @@ -855,47 +858,47 @@ if}} {{index "everything (exercise)", "short-circuit evaluation", "return keyword"}} -Like the `&&` operator, the `every` method can stop evaluating further -elements as soon as it has found one that doesn't match. So the -loop-based version can jump out of the loop—with `break` or -`return`—as soon as it runs into an element for which the predicate -function returns false. If the loop runs to its end without finding -such an element, we know that all elements matched and we should -return true. +Al igual que el operador `&&`, el método `every` puede dejar de evaluar más +elementos tan pronto como haya encontrado uno que no coincida. Entonces +la versión basada en un ciclo puede saltar fuera del ciclo—con `break` o +`return`—tan pronto como se encuentre con un elemento para el cual la función +predicado retorne falso. Si el ciclo corre hasta su final sin encontrar +tal elemento, sabemos que todos los elementos coinciden y debemos +retornar verdadero. -To build `every` on top of `some`, we can apply _((De Morgan's -laws))_, which state that `a && b` equals `!(!a || !b)`. This can be -generalized to arrays, where all elements in the array match if there -is no element in the array that does not match. +Para construir `cada` usando `some`, podemos aplicar las _((leyes De +Morgan))_, que establecen que `a && b` es igual a `!(!a ||! b)`. Esto puede ser +generalizado a arrays, donde todos los elementos del array coinciden si no hay +elemento en el array que no coincida. hint}} -### Dominant writing direction +### Dirección de Escritura Dominante {{index "SCRIPTS data set", "direction (writing)", "groupBy function", "dominant direction (exercise)"}} -Write a function that computes the dominant writing direction in a -string of text. Remember that each script object has a `direction` -property that can be `"ltr"` (left to right), `"rtl"` (right to left), -or `"ttb"` (top to bottom). +Escriba una función que calcule la dirección de escritura dominante en un +string de texto. Recuerde que cada objeto de codigo tiene una propiedad +`direction` que puede ser `"ltr"` (de izquierda a derecha), `"rtl"` +(de derecha a izquierda), o `"ttb"` (arriba a abajo). {{index "characterScript function", "countBy function"}} -The dominant direction is the direction of a majority of the -characters that have a script associated with them. The -`characterScript` and `countBy` functions defined earlier in the -chapter are probably useful here. +La dirección dominante es la dirección de la mayoría de los +caracteres que tienen un código asociado a ellos. Las funciones +`codigoCaracter` y `contarPor` definidas anteriormente en el +capítulo probablemente seran útiles aquí. {{if interactive ```{test: no} -function dominantDirection(text) { - // Your code here. +function direccionDominante(texto) { + // Tu código aquí. } -console.log(dominantDirection("Hello!")); +console.log(direccionDominante("Hola!")); // → ltr -console.log(dominantDirection("Hey, مساء الخير")); +console.log(direccionDominante("Hey, مساء الخير")); // → rtl ``` if}} @@ -904,16 +907,16 @@ if}} {{index "dominant direction (exercise)", "textScripts function", "filter method", "characterScript function"}} -Your solution might look a lot like the first half of the -`textScripts` example. You again have to count characters by a -criterion based on `characterScript` and then filter out the part of -the result that refers to uninteresting (script-less) characters. +Tu solución puede parecerse mucho a la primera mitad del +ejemplo `codigosTexto`. De nuevo debes contar los caracteres por el +criterio basado en `codigoCaracter`, y luego filtrar hacia afuera la parte del +resultado que se refiere a caracteres sin interés (que no tengan codigos). {{index "reduce method"}} -Finding the direction with the highest character count can be done -with `reduce`. If it's not clear how, refer to the example -earlier in the chapter, where `reduce` was used to find the script -with the most characters. +Encontrar la dirección con la mayor cantidad de caracteres se puede hacer +con `reduce`. Si no está claro cómo, refiérate al ejemplo +anterior en el capítulo, donde se usa `reduce` para encontrar el código +con la mayoría de los caracteres. hint}} diff --git a/06_object.md b/06_object.md index be462cb38..3f0c87048 100644 --- a/06_object.md +++ b/06_object.md @@ -1,12 +1,12 @@ {{meta {load_files: ["code/chapter/06_object.js"], zip: "node/html"}}} -# The Secret Life of Objects +# La Vida Secreta de los Objetos {{quote {author: "Barbara Liskov", title: "Programming with Abstract Data Types", chapter: true} -An abstract data type is realized by writing a special kind of program -[…] which defines the type in terms of the operations which can be -performed on it. +Un tipo de datos abstracto se realiza al escribir un tipo especial de programa +[...] que define el tipo en base a las operaciones que puedan ser +realizadas en él. quote}} @@ -14,176 +14,177 @@ quote}} {{figure {url: "img/chapter_picture_6.jpg", alt: "Picture of a rabbit with its proto-rabbit", chapter: framed}}} -[Chapter ?](data) introduced JavaScript's objects. In programming -culture, we have a thing called _((object-oriented programming))_, a -set of techniques that use objects (and related concepts) as the -central principle of program organization. +El [Capítulo 4](datos) introdujo los ((objeto))s en JavaScript. En la cultura +de la programación, tenemos una cosa llamada _((programación orientada a objetos))_, +la cual es un conjunto de técnicas que usan objetos (y conceptos relacionados) +como el principio central de la organización del programa. -Though no one really agrees on its precise definition, object-oriented -programming has shaped the design of many programming languages, -including JavaScript. This chapter will describe the way these ideas -can be applied in JavaScript. +Aunque nadie realmente está de acuerdo con su definición exacta, +la programación orientada a objetos ha contribuido al diseño de muchos +lenguajes de programación, incluyendo JavaScript. Este capítulo describirá la +forma en la que estas ideas pueden ser aplicadas en JavaScript. -## Encapsulation +## Encapsulación {{index encapsulation, isolation, modularity}} -The core idea in object-oriented programming is to divide programs -into smaller pieces and make each piece responsible for managing its -own state. +La idea central en la programación orientada a objetos es dividir a los programas +en piezas más pequeñas y hacer que cada pieza sea responsable de gestionar su +propio estado. -This way, some knowledge about the way a piece of the program works -can be kept _local_ to that piece. Someone working on the rest of the -program does not have to remember or even be aware of that knowledge. -Whenever these local details change, only the code directly around it -needs to be updated. +De esta forma, los conocimientos acerca de como funciona una parte +del programa pueden mantenerse _locales_ a esa pieza. Alguien trabajando en otra +parte del programa no tiene que recordar o ni siquiera tener una idea +de ese conocimiento. Cada vez que los detalles locales cambien, solo +el código directamente a su alrededor debe ser actualizado. {{id interface}} -{{index [interface, object]}} -Different pieces of such a program interact with each other through -_interfaces_, limited sets of functions or bindings that provide -useful functionality at a more abstract level, hiding their precise -implementation. +Las diferentes piezas de un programa como tal, interactúan entre sí a través de +_((interfaces))_, las cuales son conjuntos limitados de funciones y +vinculaciones que proporcionan funcionalidades útiles en un nivel más +abstracto, ocultando asi su implementación interna. -{{index "public properties", "private properties", "access control", [method, interface]}} +{{index "public properties", "private properties", "access control"}} -Such program pieces are modeled using ((object))s. Their interface -consists of a specific set of methods and properties. Properties -that are part of the interface are called _public_. The others, which -outside code should not be touching, are called _private_. +Tales piezas del programa se modelan usando ((objeto))s. Sus interfaces +consisten en un conjunto específico de ((método))s y propiedades. Las propiedades +que son parte de la interfaz se llaman _publicas_. Las otras, las cuales no +deberian ser tocadas por el código externo , se les llama _privadas_. {{index "underscore character"}} -Many languages provide a way to distinguish public and private -properties and prevent outside code from accessing the private -ones altogether. JavaScript, once again taking the minimalist -approach, does not—not yet at least. There is work underway to add -this to the language. +Muchos lenguajes proporcionan una forma de distinguir entre propiedades publicas +y privadas, y ademas evitarán que el código externo pueda acceder a las privadas +por completo. JavaScript, una vez más tomando el enfoque minimalista, +no hace esto. Todavía no, al menos—hay trabajo en camino para agregar +esto al lenguaje. -Even though the language doesn't have this distinction built in, -JavaScript programmers _are_ successfully using this idea. Typically, -the available interface is described in documentation or comments. It -is also common to put an underscore (`_`) character at the start of -property names to indicate that those properties are private. +Aunque el lenguaje no tenga esta distinción incorporada, +los programadores de JavaScript _estan_ usando esta idea con éxito .Típicamente, +la interfaz disponible se describe en la documentación o en los comentarios. +También es común poner un carácter de guión bajo (`_`) al comienzo de los +nombres de las propiedades para indicar que estas propiedades son privadas. -Separating interface from implementation is a great idea. It is -usually called _((encapsulation))_. +Separar la interfaz de la implementación es una gran idea. Esto +usualmente es llamado _((encapsulación))_. {{id obj_methods}} -## Methods +## Métodos -{{index "rabbit example", method, [property, access]}} +{{index "rabbit example", method, property}} -Methods are nothing more than properties that hold function values. -This is a simple method: +Los métodos no son más que propiedades que tienen valores de función. +Este es un método simple: ``` -let rabbit = {}; -rabbit.speak = function(line) { - console.log(`The rabbit says '${line}'`); +let conejo = {}; +conejo.hablar = function(linea) { + console.log(`El conejo dice '${linea}'`); }; -rabbit.speak("I'm alive."); -// → The rabbit says 'I'm alive.' +conejo.hablar("Estoy vivo."); +// → El conejo dice 'Estoy vivo.' ``` -{{index "this binding", "method call"}} +{{index this, "method call"}} -Usually a method needs to do something with the object it was called -on. When a function is called as a method—looked up as a property and -immediately called, as in `object.method()`—the binding called `this` -in its body automatically points at the object that it was called on. +Por lo general, un método debe hacer algo en el objeto con que se llamó. +Cuando una función es llamada como un método—buscada como una propiedad y +llamada inmediatamente, como en `objeto.metodo()`—la vinculación llamada `this` +("este") en su cuerpo apunta automáticamente al objeto en la cual fue llamada. ```{includeCode: "top_lines:6", test: join} -function speak(line) { - console.log(`The ${this.type} rabbit says '${line}'`); +function hablar(linea) { + console.log(`El conejo ${this.tipo} dice '${linea}'`); } -let whiteRabbit = {type: "white", speak}; -let hungryRabbit = {type: "hungry", speak}; - -whiteRabbit.speak("Oh my ears and whiskers, " + - "how late it's getting!"); -// → The white rabbit says 'Oh my ears and whiskers, how -// late it's getting!' -hungryRabbit.speak("I could use a carrot right now."); -// → The hungry rabbit says 'I could use a carrot right now.' +let conejoBlanco = {tipo: "blanco", hablar}; +let conejoHambriento = {tipo: "hambriento", hablar}; + +conejoBlanco.hablar("Oh mis orejas y bigotes, " + + "que tarde se esta haciendo!"); +// → El conejo blanco dice 'Oh mis orejas y bigotes, que +// tarde se esta haciendo!' +conejoHambriento.hablar("Podria comerme una zanahoria ahora mismo."); +// → El conejo hambriento dice 'Podria comerme una zanahoria ahora mismo.' ``` {{id call_method}} {{index "call method"}} -You can think of `this` as an extra ((parameter)) that is passed in a -different way. If you want to pass it explicitly, you can use a -function's `call` method, which takes the `this` value as its first -argument and treats further arguments as normal parameters. +Puedes pensar en `this` como un ((parámetro)) extra que es pasado en +una manera diferente. Si quieres pasarlo explícitamente, puedes usar +el método `call` ("llamar") de una función, que toma el valor de `this` +como primer argumento y trata a los argumentos adicionales como parámetros +normales. ``` -speak.call(hungryRabbit, "Burp!"); -// → The hungry rabbit says 'Burp!' +hablar.call(conejoHambriento, "Burp!"); +// → El conejo hambriento dice 'Burp!' ``` -Since each function has its own `this` binding, whose value depends on -the way it is called, you cannot refer to the `this` of the wrapping -scope in a regular function defined with the `function` keyword. +Como cada función tiene su propia vinculación `this`, cuyo valor depende de +la forma en como esta se llama, no puedes hacer referencia al `this` del +alcance envolvente en una función regular definida con la palabra clave +`function`. -{{index "this binding", "arrow function"}} +{{index this, "arrow function"}} -Arrow functions are different—they do not bind their own `this` but -can see the `this` binding of the scope around them. Thus, you can do -something like the following code, which references `this` from inside -a local function: +Las funciones de flecha son diferentes—no crean su propia vinculación `this`, +pero pueden ver la vinculación`this` del alcance a su alrededor. Por lo tanto, +puedes hacer algo como el siguiente código, que hace referencia a `this` +desde adentro de una función local: ``` -function normalize() { - console.log(this.coords.map(n => n / this.length)); +function normalizar() { + console.log(this.coordinadas.map(n => n / this.length)); } -normalize.call({coords: [0, 2, 3], length: 5}); +normalizar.call({coordinadas: [0, 2, 3], length: 5}); // → [0, 0.4, 0.6] ``` {{index "map method"}} -If I had written the argument to `map` using the `function` keyword, -the code wouldn't work. +Si hubieras escrito el argumento para `map` usando la palabra clave `function`, +el código no funcionaría. {{id prototypes}} -## Prototypes +## Prototipos {{index "toString method"}} -Watch closely. +Observa atentamente. ``` -let empty = {}; -console.log(empty.toString); +let vacio = {}; +console.log(vacio.toString); // → function toString(){…} -console.log(empty.toString()); +console.log(vacio.toString()); // → [object Object] ``` {{index magic}} -I pulled a property out of an empty object. Magic! +Saqué una propiedad de un objeto vacío. Magia! -{{index [property, inheritance], [object, property]}} +{{index property, object}} -Well, not really. I have simply been withholding information about the -way JavaScript objects work. In addition to their set of properties, -most objects also have a _((prototype))_. A prototype is another -object that is used as a fallback source of properties. When an object -gets a request for a property that it does not have, its prototype -will be searched for the property, then the prototype's prototype, and -so on. +Bueno, en realidad no. Simplemente he estado ocultando información acerca de +como funcionan los objetos en JavaScript. En adición a su conjunto de propiedades, +la mayoría de los objetos también tienen un _((prototipo))_. Un prototipo es otro +objeto que se utiliza como una reserva de propiedades alternativa. Cuando un +objeto recibe una solicitud por una propiedad que este no tiene, +se buscará en su prototipo la propiedad, luego en el prototipo del prototipo y +asi sucesivamente. {{index "Object prototype"}} -So who is the ((prototype)) of that empty object? It is the great -ancestral prototype, the entity behind almost all objects, -`Object.prototype`. +Asi que, quién es el ((prototipo)) de ese objeto vacío? Es el gran +prototipo ancestral, la entidad detrás de casi todos los objetos, +`Object.prototype` ("Objeto.prototipo"). ``` console.log(Object.getPrototypeOf({}) == @@ -195,22 +196,23 @@ console.log(Object.getPrototypeOf(Object.prototype)); {{index "getPrototypeOf function"}} -As you guess, `Object.getPrototypeOf` returns the prototype of an -object. +Como puedes adivinar, `Object.getPrototypeOf` ("Objeto.obtenerPrototipoDe") +retorna el prototipo de un objeto. {{index "toString method"}} -The prototype relations of JavaScript objects form a ((tree))-shaped -structure, and at the root of this structure sits `Object.prototype`. -It provides a few methods that show up in all objects, such as -`toString`, which converts an object to a string representation. +Las relaciones prototipo de los objetos en JavaScript forman una estructura +en forma de ((árbol)), y en la raíz de esta estructura se encuentra +`Object.prototype`. Este proporciona algunos métodos que pueden ser accedidos +por todos los objetos, como `toString`, que convierte un objeto en una +representación de tipo string. {{index inheritance, "Function prototype", "Array prototype", "Object prototype"}} -Many objects don't directly have `Object.prototype` as their -((prototype)) but instead have another object that provides a different set of -default properties. Functions derive from `Function.prototype`, and -arrays derive from `Array.prototype`. +Muchos objetos no tienen `Object.prototype` directamente como su ((prototipo)), +pero en su lugar tienen otro objeto que proporciona un conjunto diferente de +propiedades predeterminadas. Las funciones derivan de `Function.prototype`, y +los arrays derivan de `Array.prototype`. ``` console.log(Object.getPrototypeOf(Math.max) == @@ -223,227 +225,223 @@ console.log(Object.getPrototypeOf([]) == {{index "Object prototype"}} -Such a prototype object will itself have a prototype, often -`Object.prototype`, so that it still indirectly provides methods like -`toString`. +Tal prototipo de objeto tendrá en si mismo un prototipo, a menudo `Object.prototype`, +por lo que aún proporciona indirectamente métodos como `toString`. {{index "rabbit example", "Object.create function"}} -You can use `Object.create` to create an object with a specific -((prototype)). +Puede usar `Object.create` para crear un objeto con un ((prototipo)) especifico. ``` -let protoRabbit = { - speak(line) { - console.log(`The ${this.type} rabbit says '${line}'`); +let conejoPrototipo = { + hablar(linea) { + console.log(`El conejo ${this.tipo} dice '${linea}'`); } }; -let killerRabbit = Object.create(protoRabbit); -killerRabbit.type = "killer"; -killerRabbit.speak("SKREEEE!"); -// → The killer rabbit says 'SKREEEE!' +let conejoAsesino = Object.create(conejoPrototipo); +conejoAsesino.tipo = "asesino"; +conejoAsesino.hablar("SKREEEE!"); +// → El conejo asesino dice 'SKREEEE!' ``` {{index "shared property"}} -A property like `speak(line)` in an object expression is a shorthand way -of defining a method. It creates a property called `speak` and gives -it a function as its value. +Una propiedad como `hablar(linea)` en una expresión de objeto es un atajo +para definir un método. Esta crea una propiedad llamada `hablar` y le da +una función como su valor. -The "proto" rabbit acts as a container for the properties that are -shared by all rabbits. An individual rabbit object, like the killer -rabbit, contains properties that apply only to itself—in this case its -type—and derives shared properties from its prototype. +El conejo "prototipo" actúa como un contenedor para las propiedades que son +compartidas por todos los conejos. Un objeto de conejo individual, como el +conejo asesino, contiene propiedades que aplican solo a sí mismo—en este +caso su tipo—y deriva propiedades compartidas desde su prototipo. {{id classes}} -## Classes +## Clases {{index "object-oriented programming"}} -JavaScript's ((prototype)) system can be interpreted as a somewhat -informal take on an object-oriented concept called _((class))es_. A -class defines the shape of a type of object—what methods and -properties it has. Such an object is called an _((instance))_ of the -class. - -{{index [property, inheritance]}} +El sistema de ((prototipos)) en JavaScript se puede interpretar como un +enfoque informal de un concepto orientado a objetos llamado _((clase))es_. +Una clase define la forma de un tipo de objeto—qué métodos y +propiedades tiene este. Tal objeto es llamado una _((instancia))_ de la +clase. -Prototypes are useful for defining properties for which all instances -of a class share the same value, such as ((method))s. Properties that -differ per instance, such as our rabbits' `type` property, need to -be stored directly in the objects themselves. +Los prototipos son útiles para definir propiedades en las cuales todas las +instancias de una clase compartan el mismo valor, como ((método))s. +Las propiedades que difieren por instancia, como la ((propiedad)) `tipo` +en nuestros conejos, necesitan almacenarse directamente en los objetos mismos. {{id constructors}} -So to create an instance of a given class, you have to make -an object that derives from the proper prototype, but you _also_ have -to make sure it, itself, has the properties that instances of this -class are supposed to have. This is what a _((constructor))_ function -does. +Entonces, para crear una instancia de una clase dada, debes crear +un objeto que derive del prototipo adecuado, pero _también_ debes +asegurarte de que, en sí mismo, este objeto tenga las propiedades que +las instancias de esta clase se supone que tengan. +Esto es lo que una función _((constructora))_ hace. ``` -function makeRabbit(type) { - let rabbit = Object.create(protoRabbit); - rabbit.type = type; - return rabbit; +function crearConejo(tipo) { + let conejo = Object.create(conejoPrototipo); + conejo.tipo = tipo; + return conejo; } ``` -{{index "new operator", "this binding", "return keyword", [object, creation]}} +{{index "new operator", this, "return keyword", [object, creation]}} -JavaScript provides a way to make defining this type of function -easier. If you put the keyword `new` in front of a function call, the -function is treated as a constructor. This means that an object with -the right prototype is automatically created, bound to `this` in the -function, and returned at the end of the function. +JavaScript proporciona una manera de hacer que la definición de este tipo de +funciones sea más fácil. Si colocas la palabra clave `new` ("new") delante de +una llamada de función, la función sera tratada como un constructor. Esto +significa que un objeto con el prototipo adecuado es creado automáticamente, +vinculado a `this` en la función, y retornado al final de la función. {{index "prototype property"}} -The prototype object used when constructing objects is found by taking -the `prototype` property of the constructor function. +El objeto prototipo utilizado al construir objetos se encuentra al tomar +la propiedad `prototype` de la función constructora. {{index "rabbit example"}} ``` -function Rabbit(type) { - this.type = type; +function Conejo(tipo) { + this.tipo = tipo; } -Rabbit.prototype.speak = function(line) { - console.log(`The ${this.type} rabbit says '${line}'`); +Conejo.prototype.hablar = function(linea) { + console.log(`El conejo ${this.tipo} dice '${linea}'`); }; -let weirdRabbit = new Rabbit("weird"); +let conejoRaro = new Conejo("raro"); ``` {{index constructor}} -Constructors (all functions, in fact) automatically get a property -named `prototype`, which by default holds a plain, empty object that -derives from `Object.prototype`. You can overwrite it with a new -object if you want. Or you can add properties to the existing object, -as the example does. +Los constructores (todas las funciones, de hecho) automáticamente obtienen +una propiedad llamada `prototype`, que por defecto contiene un objeto simple +y vacío, que deriva de `Object.prototype`. Puedes sobrescribirlo con un nuevo +objeto si asi quieres. O puedes agregar propiedades al objeto ya existente, +como lo hace el ejemplo. {{index capitalization}} -By convention, the names of constructors are capitalized so that they -can easily be distinguished from other functions. +Por convención, los nombres de los constructores tienen la primera letra en +mayúscula para que se puedan distinguir fácilmente de otras funciones. {{index "prototype property", "getPrototypeOf function"}} -It is important to understand the distinction between the way a -prototype is associated with a constructor (through its `prototype` -property) and the way objects _have_ a prototype (which can be found -with `Object.getPrototypeOf`). The actual prototype of a constructor -is `Function.prototype` since constructors are functions. Its -`prototype` _property_ holds the prototype used for instances created -through it. +Es importante entender la distinción entre la forma en que un prototipo +está asociado con un constructor (a través de su propiedad `prototype`) +y la forma en que los objetos _tienen_ un prototipo (que se puede +encontrar con `Object.getPrototypeOf`). El prototipo real de un constructor +es `Function.prototype`, ya que los constructores son funciones. Su +_propiedad_ `prototype` contiene el prototipo utilizado para las +instancias creadas a traves de el. ``` -console.log(Object.getPrototypeOf(Rabbit) == +console.log(Object.getPrototypeOf(Conejo) == Function.prototype); // → true -console.log(Object.getPrototypeOf(weirdRabbit) == - Rabbit.prototype); +console.log(Object.getPrototypeOf(conejoRaro) == + Conejo.prototype); // → true ``` -## Class notation +## Notación de clase -So JavaScript ((class))es are ((constructor)) functions with a -((prototype)) property. That is how they work, and until 2015, that -was how you had to write them. These days, we have a less awkward -notation. +Entonces, las ((clase))es en JavaScript son funciones ((constructoras)) con una +propiedad ((prototipo)). Así es como funcionan, y hasta 2015, esa +era la manera en como tenías que escribirlas. Estos días, tenemos una +notación menos incómoda. ```{includeCode: true} -class Rabbit { - constructor(type) { - this.type = type; +class Conejo { + constructor(tipo) { + this.tipo = tipo; } - speak(line) { - console.log(`The ${this.type} rabbit says '${line}'`); + hablar(linea) { + console.log(`El conejo ${this.tipo} dice '${linea}'`); } } -let killerRabbit = new Rabbit("killer"); -let blackRabbit = new Rabbit("black"); +let conejoAsesino = new Conejo("asesino"); +let conejoNegro = new Conejo("negro"); ``` -{{index "rabbit example", [braces, class]}} +{{index "rabbit example"}} -The `class` keyword starts a ((class declaration)), which allows us to -define a constructor and a set of methods all in a single place. Any -number of methods may be written inside the declaration's braces. -The one named `constructor` is treated specially. It -provides the actual constructor function, which will be bound to the -name `Rabbit`. The others are packaged into that constructor's -prototype. Thus, the earlier class declaration is equivalent to the -constructor definition from the previous section. It just looks nicer. +La palabra clave `class` ("clase") comienza una ((declaración de clase)), +que nos permite definir un constructor y un conjunto de métodos, todo en un +solo lugar. Cualquier número de métodos se pueden escribir dentro de las llaves +de la declaración. El metodo llamado `constructor` es tratado de una manera +especial. Este proporciona la función constructora real, que estará vinculada al +nombre `Conejo`. Los otros metodos estaran empacados en el prototipo de ese +constructor. Por lo tanto, la declaración de clase anterior es equivalente a la +definición de constructor en la sección anterior. Solo que se ve mejor. {{index ["class declaration", properties]}} -Class declarations currently allow only _methods_—properties that hold -functions—to be added to the ((prototype)). This can be somewhat -inconvenient when you want to save a non-function value in there. -The next version of the language will probably improve this. For now, you -can create such properties by directly manipulating the -prototype after you've defined the class. +Actualmente las declaraciones de clase solo permiten que los _metodos_—propiedades +que contengan funciones—puedan ser agregados al ((prototipo)). Esto puede ser +algo inconveniente para cuando quieras guardar un valor no-funcional allí. +La próxima versión del lenguaje probablemente mejore esto. Por ahora, tú +puedes crear tales propiedades al manipular directamente el +prototipo después de haber definido la clase. -Like `function`, `class` can be used both in statements and in -expressions. When used as an expression, it doesn't define a -binding but just produces the constructor as a value. You are allowed -to omit the class name in a class expression. +Al igual que `function`, `class` se puede usar tanto en posiciones de +declaración como de expresión. Cuando se usa como una expresión, no define una +vinculación, pero solo produce el constructor como un valor. Tienes permitido +omitir el nombre de clase en una expresión de clase. ``` -let object = new class { getWord() { return "hello"; } }; -console.log(object.getWord()); -// → hello +let objeto = new class { obtenerPalabra() { return "hola"; } }; +console.log(objeto.obtenerPalabra()); +// → hola ``` -## Overriding derived properties +## Sobreescribiendo propiedades derivadas -{{index "shared property", overriding, [property, inheritance]}} +{{index "shared property", overriding}} -When you add a property to an object, whether it is present in the -prototype or not, the property is added to the object _itself_. -If there was already a property with -the same name in the prototype, this property will no longer affect -the object, as it is now hidden behind the object's own property. +Cuando le agregas una ((propiedad)) a un objeto, ya sea que esté presente en +el prototipo o no, la propiedad es agregada al objeto _en si mismo_. +Si ya había una propiedad con el mismo nombre en el prototipo, esta propiedad +ya no afectará al objeto, ya que ahora está oculta detrás de la propiedad del +propio objeto. ``` -Rabbit.prototype.teeth = "small"; -console.log(killerRabbit.teeth); -// → small -killerRabbit.teeth = "long, sharp, and bloody"; -console.log(killerRabbit.teeth); -// → long, sharp, and bloody -console.log(blackRabbit.teeth); -// → small -console.log(Rabbit.prototype.teeth); -// → small +Rabbit.prototype.dientes = "pequeños"; +console.log(conejoAsesino.dientes); +// → pequeños +conejoAsesino.dientes = "largos, filosos, y sangrientos"; +console.log(conejoAsesino.dientes); +// → largos, filosos, y sangrientos +console.log(conejoNegro.dientes); +// → pequeños +console.log(Rabbit.prototype.dientes); +// → pequeños ``` {{index [prototype, diagram]}} -The following diagram sketches the situation after this code has run. -The `Rabbit` and `Object` ((prototype))s lie behind `killerRabbit` as -a kind of backdrop, where properties that are not found in the object -itself can be looked up. +El siguiente diagrama esboza la situación después de que este código ha sido +ejecutado. Los ((prototipo))s de `Conejo` y `Object` se encuentran detrás de +`conejoAsesino` como una especie de telón de fondo, donde las propiedades +que no se encuentren en el objeto en sí mismo puedan ser buscadas. {{figure {url: "img/rabbits.svg", alt: "Rabbit object prototype schema",width: "8cm"}}} {{index "shared property"}} -Overriding properties that exist in a prototype can be a useful thing -to do. As the rabbit teeth example shows, overriding can be used to express -exceptional properties in instances of a more generic class of -objects, while letting the nonexceptional objects take a -standard value from their prototype. +Sobreescribir propiedades que existen en un prototipo puede ser algo útil +que hacer. Como muestra el ejemplo de los dientes de conejo, esto se +puede usar para expresar propiedades excepcionales en instancias de una clase +más genérica de objetos, dejando que los objetos no-excepcionales tomen un +valor estándar desde su prototipo. {{index "toString method", "Array prototype", "Function prototype"}} -Overriding is also used to give the standard function and array prototypes a -different `toString` method than the basic object prototype. +También puedes sobreescribir para darle a los prototipos estándar de función y +array un método diferente `toString` al del objeto prototipo básico. ``` console.log(Array.prototype.toString == @@ -455,109 +453,107 @@ console.log([1, 2].toString()); {{index "toString method", "join method", "call method"}} -Calling `toString` on an array gives a result similar to calling -`.join(",")` on it—it puts commas between the values in the array. -Directly calling `Object.prototype.toString` with an array produces a -different string. That function doesn't know about arrays, so it -simply puts the word _object_ and the name of the type between square -brackets. +Llamar a `toString` en un array da un resultado similar al de una llamada +`.join(",")` en él—pone comas entre los valores del array. +Llamar directamente a `Object.prototype.toString` con un array produce un +string diferente. Esa función no sabe acerca de los arrays, por lo que +simplemente pone la palabra _object_ y el nombre del tipo entre corchetes. ``` console.log(Object.prototype.toString.call([1, 2])); // → [object Array] ``` -## Maps +## Mapas {{index "map method"}} -We saw the word _map_ used in the [previous chapter](higher_order#map) -for an operation that transforms a data structure by applying a -function to its elements. Confusing as it is, in programming the same -word is also used for a related but rather different thing. +Vimos a la palabra _map_ usada en el [capítulo anterior](orden_superior#map) +para una operación que transforma una estructura de datos al aplicar una +función en sus elementos. -{{index "map (data structure)", "ages example", ["data structure", map]}} +{{index "map (data structure)", "ages example", "data structure"}} -A _map_ (noun) is a data structure that associates values (the keys) -with other values. For example, you might want to map names to ages. -It is possible to use objects for this. +Un _mapa_ (sustantivo) es una estructura de datos que asocia valores (las llaves) +con otros valores. Por ejemplo, es posible que desees mapear nombres a edades. +Es posible usar objetos para esto. ``` -let ages = { +let edades = { Boris: 39, Liang: 22, Júlia: 62 }; -console.log(`Júlia is ${ages["Júlia"]}`); -// → Júlia is 62 -console.log("Is Jack's age known?", "Jack" in ages); -// → Is Jack's age known? false -console.log("Is toString's age known?", "toString" in ages); -// → Is toString's age known? true +console.log(`Júlia tiene ${edades["Júlia"]}`); +// → Júlia tiene 62 +console.log("Se conoce la edad de Jack?", "Jack" in edades); +// → Se conoce la edad de Jack? false +console.log("Se conoce la edad de toString?", "toString" in edades); +// → Se conoce la edad de toString? true ``` {{index "Object.prototype", "toString method"}} -Here, the object's property names are the people's names, and the -property values are their ages. But we certainly didn't list anybody named -toString in our map. Yet, because plain objects derive from -`Object.prototype`, it looks like the property is there. +Aquí, los nombres de las propiedades del objeto son los nombres de las +personas, y los valores de las propiedades sus edades. Pero ciertamente no +incluimos a nadie llamado toString en nuestro mapa. Sin embargo, debido a +que los objetos simples se derivan de `Object.prototype`, parece que +la propiedad está ahí. {{index "Object.create function", prototype}} -As such, using plain objects as maps is dangerous. There are several -possible ways to avoid this problem. First, it is possible to create -objects with _no_ prototype. If you pass `null` to `Object.create`, -the resulting object will not derive from `Object.prototype` and can -safely be used as a map. +Como tal, usar objetos simples como mapas es peligroso. Hay varias +formas posibles de evitar este problema. Primero, es posible crear +objetos sin _ningun_ prototipo. Si pasas `null` a `Object.create`, +el objeto resultante no se derivará de `Object.prototype` y podra ser +usado de forma segura como un mapa. ``` console.log("toString" in Object.create(null)); // → false ``` -{{index [property, naming]}} - -Object property names must be strings. If you need a map whose -keys can't easily be converted to strings—such as objects—you cannot -use an object as your map. +Los nombres de las ((propiedades)) de los objetos deben ser strings. +Si necesitas un mapa cuyas claves no puedan ser convertidas fácilmente a +strings—como objetos—no puedes usar un objeto como tu mapa. {{index "Map class"}} -Fortunately, JavaScript comes with a class called `Map` that is -written for this exact purpose. It stores a mapping and allows any -type of keys. +Afortunadamente, JavaScript viene con una clase llamada `Map` que esta +escrita para este propósito exacto. Esta almacena un mapeo y permite cualquier +tipo de llaves. ``` -let ages = new Map(); -ages.set("Boris", 39); -ages.set("Liang", 22); -ages.set("Júlia", 62); - -console.log(`Júlia is ${ages.get("Júlia")}`); -// → Júlia is 62 -console.log("Is Jack's age known?", ages.has("Jack")); -// → Is Jack's age known? false -console.log(ages.has("toString")); +let edades = new Map(); +edades.set("Boris", 39); +edades.set("Liang", 22); +edades.set("Júlia", 62); + +console.log(`Júlia tiene ${edades.get("Júlia")}`); +// → Júlia tiene 62 +console.log("Se conoce la edad de Jack?", edades.has("Jack")); +// → Se conoce la edad de Jack? false +console.log(edades.has("toString")); // → false ``` -{{index [interface, object], "set method", "get method", "has method", encapsulation}} +{{index interface, "set method", "get method", "has method", encapsulation}} -The methods `set`, `get`, and `has` are part of the interface of the -`Map` object. Writing a data structure that can quickly update and -search a large set of values isn't easy, but we don't have to worry -about that. Someone else did it for us, and we can go through this -simple interface to use their work. +Los métodos `set` ("establecer"),` get` ("obtener"), y `has` ("tiene") +son parte de la interfaz del objeto `Map`. +Escribir una estructura de datos que pueda actualizarse rápidamente y +buscar en un gran conjunto de valores no es fácil, pero no tenemos que +preocuparnos acerca de eso. Alguien más lo hizo por nosotros, y podemos +utilizar esta simple interfaz para usar su trabajo. {{index "hasOwnProperty method", "in operator"}} -If you do have a plain object that you need to treat as a map for some -reason, it is useful to know that `Object.keys` returns only an -object's _own_ keys, not those in the prototype. As an alternative to -the `in` operator, you can use the `hasOwnProperty` method, which -ignores the object's prototype. +Si tienes un objeto simple que necesitas tratar como un mapa por alguna +razón, es útil saber que `Object.keys` solo retorna las llaves propias del +objeto, no las que estan en el prototipo. Como alternativa al operador `in`, +puedes usar el método` hasOwnProperty` ("tienePropiaPropiedad"), el cual +ignora el prototipo del objeto. ``` console.log({x: 1}.hasOwnProperty("x")); @@ -566,209 +562,207 @@ console.log({x: 1}.hasOwnProperty("toString")); // → false ``` -## Polymorphism +## Polimorfismo {{index "toString method", "String function", polymorphism, overriding, "object-oriented programming"}} -When you call the `String` function (which converts a value to a -string) on an object, it will call the `toString` method on that -object to try to create a meaningful string from it. I mentioned that -some of the standard prototypes define their own version of `toString` -so they can create a string that contains more useful information than -`"[object Object]"`. You can also do that yourself. +Cuando llamas a la función `String` (que convierte un valor a un +string) en un objeto, llamará al método `toString` en ese +objeto para tratar de crear un string significativo a partir de el. Mencioné que +algunos de los prototipos estándar definen su propia versión de `toString` +para que puedan crear un string que contenga información más útil que +`"[object Object]"`. También puedes hacer eso tú mismo. ```{includeCode: "top_lines: 3"} -Rabbit.prototype.toString = function() { - return `a ${this.type} rabbit`; +Conejo.prototype.toString = function() { + return `un conejo ${this.tipo}`; }; -console.log(String(blackRabbit)); -// → a black rabbit +console.log(String(conejoNegro)); +// → un conejo negro ``` -{{index "object-oriented programming", [interface, object]}} +{{index "object-oriented programming"}} -This is a simple instance of a powerful idea. When a piece of code is -written to work with objects that have a certain interface—in this -case, a `toString` method—any kind of object that happens to support -this interface can be plugged into the code, and it will just work. +Esta es una instancia simple de una idea poderosa. Cuando un pedazo de código es +escrito para funcionar con objetos que tienen una cierta ((interfaz))—en este +caso, un método `toString`—cualquier tipo de objeto que soporte +esta interfaz se puede conectar al código, y simplemente funcionará. -This technique is called _polymorphism_. Polymorphic code can work -with values of different shapes, as long as they support the interface -it expects. +Esta técnica se llama _polimorfismo_. El código polimórfico puede funcionar +con valores de diferentes formas, siempre y cuando soporten la interfaz +que este espera. {{index "for/of loop", "iterator interface"}} -I mentioned in [Chapter ?](data#for_of_loop) that a `for`/`of` loop -can loop over several kinds of data structures. This is another case -of polymorphism—such loops expect the data structure to expose a -specific interface, which arrays and strings do. And we can also add -this interface to your own objects! But before we can do that, we need -to know what symbols are. +Mencioné en el [Capítulo 4](datos#for_of_loop) que un ciclo `for`/`of` +puede recorrer varios tipos de estructuras de datos. Este es otro caso +de polimorfismo—tales ciclos esperan que la estructura de datos exponga una +interfaz específica, lo que hacen los arrays y strings. Y también puedes agregar +esta interfaz a tus propios objetos! Pero antes de que podamos hacer eso, +necesitamos saber qué son los símbolos. -## Symbols +## Símbolos -It is possible for multiple interfaces to use the same property name -for different things. For example, I could define an interface in which -the `toString` method is supposed to convert the object into a piece -of yarn. It would not be possible for an object to conform to both -that interface and the standard use of `toString`. +Es posible que múltiples interfaces usen el mismo nombre de propiedad +para diferentes cosas. Por ejemplo, podría definir una interfaz en la que +se suponga que el método `toString` convierte el objeto a una pieza +de hilo. No sería posible para un objeto ajustarse a +esa interfaz y al uso estándar de `toString`. -That would be a bad idea, and this problem isn't that common. Most -JavaScript programmers simply don't think about it. But the language -designers, whose _job_ it is to think about this stuff, have provided -us with a solution anyway. +Esa sería una mala idea, y este problema no es muy común. La mayoria de +los programadores de JavaScript simplemente no piensan en eso. +Pero los diseñadores del lenguaje, cuyo _trabajo_ es pensar acerca de estas +cosas, nos han proporcionado una solución de todos modos. -{{index "Symbol function", [property, naming]}} +{{index "Symbol function", property}} -When I claimed that property names are strings, that wasn't entirely -accurate. They usually are, but they can also be _((symbol))s_. -Symbols are values created with the `Symbol` function. Unlike strings, -newly created symbols are unique—you cannot create the same symbol -twice. +Cuando afirmé que los nombres de propiedad son strings, eso no fue del todo +preciso. Usualmente lo son, pero también pueden ser _((símbolo))s_. +Los símbolos son valores creados con la función `Symbol`. A diferencia de los +strings, los símbolos recién creados son únicos—no puedes crear el mismo símbolo +dos veces. ``` -let sym = Symbol("name"); -console.log(sym == Symbol("name")); +let simbolo = Symbol("nombre"); +console.log(simbolo == Symbol("nombre")); // → false -Rabbit.prototype[sym] = 55; -console.log(blackRabbit[sym]); +Conejo.prototype[simbolo] = 55; +console.log(conejoNegro[simbolo]); // → 55 ``` -The string you pass to `Symbol` is included when you convert it to a -string and can make it easier to recognize a symbol when, for -example, showing it in the console. But it has no meaning beyond -that—multiple symbols may have the same name. +El string que pases a `Symbol` es incluido cuando lo conviertas a +string, y puede hacer que sea más fácil reconocer un símbolo cuando, por +ejemplo, lo muestres en la consola. Pero no tiene sentido más allá de +eso—múltiples símbolos pueden tener el mismo nombre. -Being both unique and usable as property names makes symbols suitable -for defining interfaces that can peacefully live alongside other -properties, no matter what their names are. +Al ser únicos y utilizables como nombres de propiedad, los símbolos son adecuados +para definir interfaces que pueden vivir pacíficamente junto a otras +propiedades, sin importar cuáles sean sus nombres. ```{includeCode: "top_lines: 1"} -const toStringSymbol = Symbol("toString"); -Array.prototype[toStringSymbol] = function() { - return `${this.length} cm of blue yarn`; +const simboloToString = Symbol("toString"); +Array.prototype[simboloToString] = function() { + return `${this.length} cm de hilo azul`; }; console.log([1, 2].toString()); // → 1,2 -console.log([1, 2][toStringSymbol]()); -// → 2 cm of blue yarn +console.log([1, 2][simboloToString]()); +// → 2 cm de hilo azul ``` -{{index [property, naming]}} - -It is possible to include symbol properties in object expressions and -classes by using ((square bracket))s around the property name. -That causes the property name to be evaluated, much like the square -bracket property access notation, which allows us to refer to a -binding that holds the symbol. +Es posible incluir propiedades de símbolos en expresiones de objetos y +clases usando ((corchete))s alrededor del nombre de la ((propiedad)). +Eso hace que se evalúe el nombre de la propiedad, al igual que la +notación de corchetes para acceder propiedades, lo cual +nos permite hacer referencia a una vinculación que contiene el símbolo. ``` -let stringObject = { - [toStringSymbol]() { return "a jute rope"; } +let objetoString = { + [simboloToString]() { return "una cuerda de cañamo"; } }; -console.log(stringObject[toStringSymbol]()); -// → a jute rope +console.log(objetoString[simboloToString]()); +// → una cuerda de cañamo ``` -## The iterator interface +## La interfaz de iterador {{index "iterable interface", "Symbol.iterator symbol", "for/of loop"}} -The object given to a `for`/`of` loop is expected to be _iterable_. -This means it has a method named with the `Symbol.iterator` -symbol (a symbol value defined by the language, stored as a property -of the `Symbol` function). +Se espera que el objeto dado a un ciclo `for`/`of` sea _iterable_. +Esto significa que tenga un método llamado con el símbolo `Symbol.iterator` +(un valor de símbolo definido por el idioma, almacenado como una propiedad +de la función `Symbol`). {{index "iterator interface", "next method"}} -When called, that method should return an object that provides a -second interface, _iterator_. This is the actual thing that iterates. -It has a `next` method that returns the next result. That result -should be an object with a `value` property that provides the next value, -if there is one, and a `done` property, which should be true when there -are no more results and false otherwise. +Cuando sea llamado, ese método debe retornar un objeto que proporcione una +segunda interfaz, _iteradora_. Esta es la cosa real que realiza la iteración. +Tiene un método `next` ("siguiente") que retorna el siguiente resultado. +Ese resultado debería ser un objeto con una propiedad `value` ("valor"), +que proporciona el siguiente valor, si hay uno, y una propiedad `done` ("listo") +que debería ser cierta cuando no haya más resultados y falso de lo contrario. -Note that the `next`, `value`, and `done` property names are plain -strings, not symbols. Only `Symbol.iterator`, which is likely to be -added to a _lot_ of different objects, is an actual symbol. +Ten en cuenta que los nombres de las propiedades `next`, `value` y `done` son +simples strings, no símbolos. Solo `Symbol.iterator`, que probablemente sea +agregado a un _monton_ de objetos diferentes, es un símbolo real. -We can directly use this interface ourselves. +Podemos usar directamente esta interfaz nosotros mismos. ``` -let okIterator = "OK"[Symbol.iterator](); -console.log(okIterator.next()); +let iteradorOK = "OK"[Symbol.iterator](); +console.log(iteradorOK.next()); // → {value: "O", done: false} -console.log(okIterator.next()); +console.log(iteradorOK.next()); // → {value: "K", done: false} -console.log(okIterator.next()); +console.log(iteradorOK.next()); // → {value: undefined, done: true} ``` -{{index "matrix example", "Matrix class", [array, "as matrix"]}} +{{index "matrix example", "Matrix class", array}} {{id matrix}} -Let's implement an iterable data structure. We'll build a _matrix_ -class, acting as a two-dimensional array. +Implementemos una estructura de datos iterable. Construiremos una clase +_matriz_, que actuara como un array bidimensional. ```{includeCode: true} -class Matrix { - constructor(width, height, element = (x, y) => undefined) { - this.width = width; - this.height = height; - this.content = []; - - for (let y = 0; y < height; y++) { - for (let x = 0; x < width; x++) { - this.content[y * width + x] = element(x, y); +class Matriz { + constructor(ancho, altura, elemento = (x, y) => undefined) { + this.ancho = ancho; + this.altura = altura; + this.contenido = []; + + for (let y = 0; y < altura; y++) { + for (let x = 0; x < ancho; x++) { + this.contenido[y * ancho + x] = elemento(x, y); } } } - get(x, y) { - return this.content[y * this.width + x]; + obtener(x, y) { + return this.contenido[y * this.ancho + x]; } - set(x, y, value) { - this.content[y * this.width + x] = value; + establecer(x, y, valor) { + this.contenido[y * this.ancho + x] = valor; } } ``` -The class stores its content in a single array of _width_ × _height_ -elements. The elements are stored row by row, so, for example, the third -element in the fifth row is (using zero-based indexing) stored at -position 4 × _width_ + 2. +La clase almacena su contenido en un único array de elementos _altura_ × _ancho_. +Los elementos se almacenan fila por fila, por lo que, por ejemplo, el tercer +elemento en la quinta fila es (utilizando indexación basada en cero) almacenado +en la posición 4 × _ancho_ + 2. -The constructor function takes a width, a height, and an optional -`element` function that will be used to fill in the initial values. -There are `get` and `set` methods to retrieve and update elements in -the matrix. +La función constructora toma un ancho, una altura y una función opcional +de contenido que se usará para llenar los valores iniciales. +Hay métodos `obtener` y `establecer` para recuperar y actualizar elementos en +la matriz. -When looping over a matrix, you are usually interested in the position -of the elements as well as the elements themselves, so we'll have our -iterator produce objects with `x`, `y`, and `value` properties. +Al hacer un ciclo sobre una matriz, generalmente estás interesado en la posición +tanto de los elementos como de los elementos en sí mismos, así que haremos que +nuestro iterador produzca objetos con propiedades `x`, `y`, y `value` ("valor"). {{index "MatrixIterator class"}} ```{includeCode: true} -class MatrixIterator { - constructor(matrix) { +class IteradorMatriz { + constructor(matriz) { this.x = 0; this.y = 0; - this.matrix = matrix; + this.matriz = matriz; } next() { - if (this.y == this.matrix.height) return {done: true}; + if (this.y == this.matriz.altura) return {done: true}; let value = {x: this.x, y: this.y, - value: this.matrix.get(this.x, this.y)}; + value: this.matriz.obtener(this.x, this.y)}; this.x++; - if (this.x == this.matrix.width) { + if (this.x == this.matriz.ancho) { this.x = 0; this.y++; } @@ -777,92 +771,93 @@ class MatrixIterator { } ``` -The class tracks the progress of iterating over a matrix in its `x` -and `y` properties. The `next` method starts by checking whether the -bottom of the matrix has been reached. If it hasn't, it _first_ -creates the object holding the current value and _then_ updates its -position, moving to the next row if necessary. +La clase hace un seguimiento del progreso de iterar sobre una matriz en sus +propiedades `x` y `y`. El método `next` ("siguiente") comienza comprobando si +la parte inferior de la matriz ha sido alcanzada. Si no es así, _primero_ +crea el objeto que contiene el valor actual y _luego_ actualiza su +posición, moviéndose a la siguiente fila si es necesario. -Let's set up the `Matrix` class to be iterable. Throughout this book, -I'll occasionally use after-the-fact prototype manipulation to add -methods to classes so that the individual pieces of code remain small -and self-contained. In a regular program, where there is no need to -split the code into small pieces, you'd declare these methods directly -in the class instead. +Configuremos la clase `Matriz` para que sea iterable. A lo largo de este libro, +Ocasionalmente usaré la manipulación del prototipo después de los hechos para +agregar métodos a clases, para que las piezas individuales de código +permanezcan pequeñas y autónomas. En un programa regular, donde no hay necesidad +de dividir el código en pedazos pequeños, declararias estos métodos directamente +en la clase. ```{includeCode: true} -Matrix.prototype[Symbol.iterator] = function() { - return new MatrixIterator(this); +Matriz.prototype[Symbol.iterator] = function() { + return new IteradorMatriz(this); }; ``` {{index "for/of loop"}} -We can now loop over a matrix with `for`/`of`. +Ahora podemos recorrer una matriz con `for`/`of`. ``` -let matrix = new Matrix(2, 2, (x, y) => `value ${x},${y}`); -for (let {x, y, value} of matrix) { +let matriz = new Matriz(2, 2, (x, y) => `valor ${x},${y}`); +for (let {x, y, value} of matriz) { console.log(x, y, value); } -// → 0 0 value 0,0 -// → 1 0 value 1,0 -// → 0 1 value 0,1 -// → 1 1 value 1,1 +// → 0 0 valor 0,0 +// → 1 0 valor 1,0 +// → 0 1 valor 0,1 +// → 1 1 valor 1,1 ``` -## Getters, setters, and statics +## Getters, setters y estáticos -{{index [interface, object], [property, definition], "Map class"}} +{{index interface, property, "Map class"}} -Interfaces often consist mostly of methods, but it is also okay to -include properties that hold non-function values. For example, `Map` -objects have a `size` property that tells you how many keys are stored -in them. +A menudo, las interfaces consisten principalmente de métodos, pero también +está bien incluir propiedades que contengan valores que no sean de función. +Por ejemplo, los objetos `Map` tienen una propiedad `size` ("tamaño") +que te dice cuántas claves hay almacenanadas en ellos. -It is not even necessary for such an object to compute and store such -a property directly in the instance. Even properties that are accessed -directly may hide a method call. Such methods are called -_((getter))s_, and they are defined by writing `get` in front of the -method name in an object expression or class declaration. +Ni siquiera es necesario que dicho objeto calcule y almacene tales +propiedades directamente en la instancia. Incluso las propiedades que +pueden ser accedidas directamente pueden ocultar una llamada a un método. +Tales métodos se llaman _((getter))s_, y se definen escribiendo `get` ("obtener") +delante del nombre del método en una expresión de objeto o declaración +de clase. ```{test: no} -let varyingSize = { - get size() { +let tamañoCambiante = { + get tamaño() { return Math.floor(Math.random() * 100); } }; -console.log(varyingSize.size); +console.log(tamañoCambiante.tamaño); // → 73 -console.log(varyingSize.size); +console.log(tamañoCambiante.tamaño); // → 49 ``` {{index "temperature example"}} -Whenever someone reads from this object's `size` property, the -associated method is called. You can do a similar thing when a -property is written to, using a _((setter))_. +Cuando alguien lee desde la propiedad `tamaño` de este objeto, el +método asociado es llamado. Puedes hacer algo similar cuando se escribe +en una propiedad, usando un _((setter))_. ```{test: no, startCode: true} -class Temperature { +class Temperatura { constructor(celsius) { this.celsius = celsius; } get fahrenheit() { return this.celsius * 1.8 + 32; } - set fahrenheit(value) { - this.celsius = (value - 32) / 1.8; + set fahrenheit(valor) { + this.celsius = (valor - 32) / 1.8; } - static fromFahrenheit(value) { - return new Temperature((value - 32) / 1.8); + static desdeFahrenheit(valor) { + return new Temperatura((valor - 32) / 1.8); } } -let temp = new Temperature(22); +let temp = new Temperatura(22); console.log(temp.fahrenheit); // → 71.6 temp.fahrenheit = 86; @@ -870,123 +865,123 @@ console.log(temp.celsius); // → 30 ``` -The `Temperature` class allows you to read and write the temperature -in either degrees ((Celsius)) or degrees ((Fahrenheit)), but -internally it stores only Celsius and automatically converts to -and from Celsius in the `fahrenheit` getter and setter. +La clase `Temperatura` te permite leer y escribir la temperatura ya sea +en grados ((Celsius)) o grados ((Fahrenheit)), pero +internamente solo almacena Celsius y convierte automáticamente a Celsius +en el getter y setter `fahrenheit`. {{index "static method"}} -Sometimes you want to attach some properties directly to your -constructor function, rather than to the prototype. Such methods won't -have access to a class instance but can, for example, be used to -provide additional ways to create instances. +Algunas veces quieres adjuntar algunas propiedades directamente a tu +función constructora, en lugar de al prototipo. Tales métodos no +tienen acceso a una instancia de clase, pero pueden, por ejemplo, ser +utilizados para proporcionar formas adicionales de crear instancias. -Inside a class declaration, methods that have `static` written before -their name are stored on the constructor. So the `Temperature` class -allows you to write `Temperature.fromFahrenheit(100)` to create a -temperature using degrees Fahrenheit. +Dentro de una declaración de clase, métodos que tienen `static` ("estatico") +escrito antes su nombre son almacenados en el constructor. Entonces, la clase +`Temperatura` te permite escribir `Temperature.desdeFahrenheit(100)` para +crear una temperatura usando grados Fahrenheit. -## Inheritance +## Herencia {{index inheritance, "matrix example", "object-oriented programming", "SymmetricMatrix class"}} -Some matrices are known to be _symmetric_. If you mirror a symmetric -matrix around its top-left-to-bottom-right diagonal, it stays the -same. In other words, the value stored at _x_,_y_ is always the same -as that at _y_,_x_. +Algunas matrices son conocidas por ser _simétricas_. Si duplicas una matriz +simétrico alrededor de su diagonal de arriba-izquierda a derecha-abajo, esta +se mantiene igual. En otras palabras, el valor almacenado en _x_,_y_ es +siempre el mismo al de _y_,_x_. -Imagine we need a data structure like `Matrix` but one that enforces -the fact that the matrix is and remains symmetrical. We could write it -from scratch, but that would involve repeating some code very similar -to what we already wrote. +Imagina que necesitamos una estructura de datos como `Matriz` pero que haga +cumplir el hecho de que la matriz es y siga siendo simétrica. Podríamos +escribirla desde cero, pero eso implicaría repetir algo de código muy similar +al que ya hemos escrito. {{index overriding, prototype}} -JavaScript's prototype system makes it possible to create a _new_ -class, much like the old class, but with new definitions for some of -its properties. The prototype for the new class derives from the old -prototype but adds a new definition for, say, the `set` method. +El sistema de prototipos en JavaScript hace posible crear una _nueva_ +clase, parecida a la clase anterior, pero con nuevas definiciones para +algunas de sus propiedades. El prototipo de la nueva clase deriva del antiguo +prototipo, pero agrega una nueva definición para, por ejemplo, el método `set`. -In object-oriented programming terms, this is called -_((inheritance))_. The new class inherits properties and behavior from -the old class. +En términos de programación orientada a objetos, esto se llama +_((herencia))_. La nueva clase hereda propiedades y comportamientos de +la vieja clase. ```{includeCode: "top_lines: 17"} -class SymmetricMatrix extends Matrix { - constructor(size, element = (x, y) => undefined) { - super(size, size, (x, y) => { - if (x < y) return element(y, x); - else return element(x, y); +class MatrizSimetrica extends Matriz { + constructor(tamaño, elemento = (x, y) => undefined) { + super(tamaño, tamaño, (x, y) => { + if (x < y) return elemento(y, x); + else return elemento(x, y); }); } - set(x, y, value) { - super.set(x, y, value); + set(x, y, valor) { + super.set(x, y, valor); if (x != y) { - super.set(y, x, value); + super.set(y, x, valor); } } } -let matrix = new SymmetricMatrix(5, (x, y) => `${x},${y}`); -console.log(matrix.get(2, 3)); +let matriz = new MatrizSimetrica(5, (x, y) => `${x},${y}`); +console.log(matriz.get(2, 3)); // → 3,2 ``` -The use of the word `extends` indicates that this class shouldn't be -directly based on the default `Object` prototype but on some other class. This -is called the _((superclass))_. The derived class is the -_((subclass))_. - -To initialize a `SymmetricMatrix` instance, the constructor calls its -superclass's constructor through the `super` keyword. This is necessary -because if this new object is to behave (roughly) like a `Matrix`, it -is going to need the instance properties that matrices have. -To ensure the matrix is symmetrical, the constructor wraps the -`element` function to swap the coordinates for values below the +El uso de la palabra `extends` indica que esta clase no debe estar +basada directamente en el prototipo de `Objeto` predeterminado, pero de +alguna otra clase. Esta se llama la _((superclase))_. La clase derivada es la +_((subclase))_. + +Para inicializar una instancia de `MatrizSimetrica`, el constructor llama a su +constructor de superclase a través de la palabra clave `super`. Esto es necesario +porque si este nuevo objeto se comporta (más o menos) como una `Matriz`, +va a necesitar las propiedades de instancia que tienen las matrices. En orden +para asegurar que la matriz sea simétrica, el constructor ajusta el +método `contenido` para intercambiar las coordenadas de los valores por debajo del diagonal. -The `set` method again uses `super` but this time not to call the -constructor but to call a specific method from the superclass's set of -methods. We are redefining `set` but do want to use the original -behavior. Because `this.set` refers to the _new_ `set` method, calling -that wouldn't work. Inside class methods, `super` provides a way to -call methods as they were defined in the superclass. +El método `set` nuevamente usa `super`, pero esta vez no para llamar al +constructor, pero para llamar a un método específico del conjunto de metodos +de la superclase. Estamos redefiniendo `set` pero queremos usar el comportamiento +original. Ya que `this.set` se refiere al _nuevo_ método` set`, llamarlo +no funcionaria. Dentro de los métodos de clase, `super` proporciona una forma de +llamar a los métodos tal y como se definieron en la superclase. -Inheritance allows us to build slightly different data types from -existing data types with relatively little work. It is a fundamental -part of the object-oriented tradition, alongside encapsulation and -polymorphism. But while the latter two are now generally regarded as -wonderful ideas, inheritance is more controversial. +La herencia nos permite construir tipos de datos ligeramente diferentes a partir +de tipos de datos existentes con relativamente poco trabajo. Es una +parte fundamental de la tradición orientada a objetos, junto con la encapsulación y +el polimorfismo. Pero mientras que los últimos dos son considerados como ideas +maravillosas en la actualidad, la herencia es más controversial. {{index complexity, reuse, "class hierarchy"}} -Whereas ((encapsulation)) and polymorphism can be used to _separate_ -pieces of code from each other, reducing the tangledness of the -overall program, ((inheritance)) fundamentally ties classes together, -creating _more_ tangle. When inheriting from a class, you usually have -to know more about how it works than when simply using it. Inheritance -can be a useful tool, and I use it now and then in my own programs, -but it shouldn't be the first tool you reach for, and you probably -shouldn't actively go looking for opportunities to construct class -hierarchies (family trees of classes). +Mientras que la ((encapsulación)) y el polimorfismo se pueden usar para +_separar_ piezas de código entre sí, reduciendo el enredo del programa +en general, la ((herencia)) fundamentalmente vincula las clases, +creando _mas_ enredo. Al heredar de una clase, generalmente tienes +que saber más sobre cómo funciona que cuando simplemente la usas. La herencia +puede ser una herramienta útil, y la uso de vez en cuando en mis +propios programas, pero no debería ser la primera herramienta que busques, +y probablemente no deberías estar buscando oportunidades para construir +jerarquías (árboles genealógicos de clases) de clases en una manera activa. -## The instanceof operator +## El operador instanceof {{index type, "instanceof operator", constructor, object}} -It is occasionally useful to know whether an object was derived from a -specific class. For this, JavaScript provides a binary operator called -`instanceof`. +Ocasionalmente es útil saber si un objeto fue derivado de una +clase específica. Para esto, JavaScript proporciona un operador binario llamado +`instanceof` ("instancia de"). ``` console.log( - new SymmetricMatrix(2) instanceof SymmetricMatrix); + new MatrizSimetrica(2) instanceof MatrizSimetrica); // → true -console.log(new SymmetricMatrix(2) instanceof Matrix); +console.log(new MatrizSimetrica(2) instanceof Matriz); // → true -console.log(new Matrix(2, 2) instanceof SymmetricMatrix); +console.log(new Matriz(2, 2) instanceof MatrizSimetrica); // → false console.log([1] instanceof Array); // → true @@ -994,80 +989,80 @@ console.log([1] instanceof Array); {{index inheritance}} -The operator will see through inherited types, so a `SymmetricMatrix` -is an instance of `Matrix`. The operator can also be applied to -standard constructors like `Array`. Almost every object is an instance -of `Object`. +El operador verá a través de los tipos heredados, por lo que una `MatrizSimetrica` +es una instancia de `Matriz`. El operador también se puede aplicar a +constructores estándar como `Array`. Casi todos los objetos son una instancia +de `Object`. -## Summary +## Resumen -So objects do more than just hold their own properties. They have -prototypes, which are other objects. They'll act as if they have -properties they don't have as long as their prototype has that -property. Simple objects have `Object.prototype` as their prototype. +Entonces los objetos hacen más que solo tener sus propias propiedades. Ellos +tienen prototipos, que son otros objetos. Estos actuarán como si tuvieran +propiedades que no tienen mientras su prototipo tenga esa +propiedad. Los objetos simples tienen `Object.prototype` como su prototipo. -Constructors, which are functions whose names usually start with a -capital letter, can be used with the `new` operator to create new -objects. The new object's prototype will be the object found in the -`prototype` property of the constructor. You can make good use of this -by putting the properties that all values of a given type share into -their prototype. There's a `class` notation that provides a clear way -to define a constructor and its prototype. +Los constructores, que son funciones cuyos nombres generalmente comienzan con +una mayúscula, se pueden usar con el operador `new` para crear nuevos +objetos. El prototipo del nuevo objeto será el objeto encontrado en la +propiedad `prototype` del constructor. Puedes hacer un buen uso de esto +al poner las propiedades que todos los valores de un tipo dado comparten en +su prototipo. Hay una notación de `class` que proporciona una manera clara +de definir un constructor y su prototipo. -You can define getters and setters to secretly call methods every time -an object's property is accessed. Static methods are methods stored in -a class's constructor, rather than its prototype. +Puedes definir getters y setters para secretamente llamar a métodos +cada vez que se acceda a la propiedad de un objeto. Los métodos estáticos +son métodos almacenados en el constructor de clase, en lugar de su prototipo. -The `instanceof` operator can, given an object and a constructor, tell -you whether that object is an instance of that constructor. +El operador `instanceof` puede, dado un objeto y un constructor, decir +si ese objeto es una instancia de ese constructor. -One useful thing to do with objects is to specify an interface for -them and tell everybody that they are supposed to talk to your object -only through that interface. The rest of the details that make up your -object are now _encapsulated_, hidden behind the interface. +Una cosa útil que hacer con los objetos es especificar una interfaz para +ellos y decirle a todos que se supone que deben hablar con ese objeto +solo a través de esa interfaz. El resto de los detalles que componen tu +objeto ahora estan _encapsulados_, escondidos detrás de la interfaz. -More than one type may implement the same interface. Code written to -use an interface automatically knows how to work with any number of -different objects that provide the interface. This is called -_polymorphism_. +Más de un tipo puede implementar la misma interfaz. El código escrito para +utilizar una interfaz automáticamente sabe cómo trabajar con cualquier +cantidad de objetos diferentes que proporcionen la interfaz. Esto se llama +_polimorfismo_. -When implementing multiple classes that differ in only some details, -it can be helpful to write the new classes as _subclasses_ of an -existing class, _inheriting_ part of its behavior. +Al implementar múltiples clases que difieran solo en algunos detalles, +puede ser útil escribir las nuevas clases como _subclases_ de una +clase existente, _heredando_ parte de su comportamiento. -## Exercises +## Ejercicios {{id exercise_vector}} -### A vector type +### Un tipo vector {{index dimensions, "Vec class", coordinates, "vector (exercise)"}} -Write a ((class)) `Vec` that represents a vector in two-dimensional -space. It takes `x` and `y` parameters (numbers), which it should save -to properties of the same name. +Escribe una ((clase)) `Vec` que represente un vector en un espacio +de dos dimensiones. Toma los parámetros (numericos) `x` y `y`, que debería +guardar como propiedades del mismo nombre. {{index addition, subtraction}} -Give the `Vec` prototype two methods, `plus` and `minus`, that take -another vector as a parameter and return a new vector that has the sum -or difference of the two vectors' (`this` and the parameter) _x_ and -_y_ values. +Dale al prototipo de `Vector` dos métodos, `mas` y `menos`, los cuales toman +otro vector como parámetro y retornan un nuevo vector que tiene la suma +o diferencia de los valores _x_ y _y_ de los dos vectores (`this` y el +parámetro). -Add a ((getter)) property `length` to the prototype that computes the -length of the vector—that is, the distance of the point (_x_, _y_) from -the origin (0, 0). +Agrega una propiedad ((getter)) llamada `longitud` al prototipo que calcule la +longitud del vector—es decir, la distancia del punto (_x_, _y_) desde +el origen (0, 0). {{if interactive ```{test: no} // Your code here. -console.log(new Vec(1, 2).plus(new Vec(2, 3))); -// → Vec{x: 3, y: 5} -console.log(new Vec(1, 2).minus(new Vec(2, 3))); -// → Vec{x: -1, y: -1} -console.log(new Vec(3, 4).length); +console.log(new Vector(1, 2).mas(new Vector(2, 3))); +// → Vector{x: 3, y: 5} +console.log(new Vector(1, 2).menos(new Vector(2, 3))); +// → Vector{x: -1, y: -1} +console.log(new Vector(3, 4).longitud); // → 5 ``` if}} @@ -1076,69 +1071,70 @@ if}} {{index "vector (exercise)"}} -Look back to the `Rabbit` class example if you're unsure how `class` -declarations look. +Mira de nuevo al ejemplo de la clase `Conejo` si no recuerdas muy bien +como se ven las declaraciones de clases. {{index Pythagoras, "defineProperty function", "square root", "Math.sqrt function"}} -Adding a getter property to the constructor can be done by putting the -word `get` before the method name. To compute the distance from (0, 0) -to (x, y), you can use the Pythagorean theorem, which says that the -square of the distance we are looking for is equal to the square of -the x-coordinate plus the square of the y-coordinate. Thus, [√(x^2^ + -y^2^)]{if html}[[$\sqrt{x^2 + y^2}$]{latex}]{if tex} is the number you -want, and `Math.sqrt` is the way you compute a square root in -JavaScript. +Agregar una propiedad getter al constructor se puede hacer al poner la +palabra `get` antes del nombre del método. Para calcular la distancia desde +(0, 0) a (x, y), puedes usar el teorema de Pitágoras, que dice que el +cuadrado de la distancia que estamos buscando es igual al cuadrado de +la coordenada x más el cuadrado de la coordenada y. Por lo tanto, [√(x^2^ + +y^2^)]{if html}[[$\sqrt{x^2 + y^2}$]{latex}]{if tex} +es el número que quieres, y `Math.sqrt` es la forma en que calculas una +raíz cuadrada en JavaScript. hint}} -### Groups +### Conjuntos {{index "groups (exercise)", "Set class", "Group class", "set (data structure)"}} {{id groups}} -The standard JavaScript environment provides another data structure -called `Set`. Like an instance of `Map`, a set holds a collection of -values. Unlike `Map`, it does not associate other values with those—it -just tracks which values are part of the set. A value can be part -of a set only once—adding it again doesn't have any effect. +El entorno de JavaScript estándar proporciona otra estructura de datos +llamada `Set` ("Conjunto"). Al igual que una instancia de `Map`, +un conjunto contiene una colección de valores. Pero a diferencia de `Map`, +este no asocia valores con otros—este solo rastrea qué valores son parte del +conjunto. Un valor solo puede ser parte de un conjunto una vez—agregarlo de +nuevo no tiene ningún efecto. {{index "add method", "delete method", "has method"}} -Write a class called `Group` (since `Set` is already taken). Like -`Set`, it has `add`, `delete`, and `has` methods. Its constructor -creates an empty group, `add` adds a value to the group (but only if -it isn't already a member), `delete` removes its argument from the -group (if it was a member), and `has` returns a Boolean value -indicating whether its argument is a member of the group. +Escribe una clase llamada `Conjunto`. Como `Set`, debe tener los métodos +`add` ("añadir"), `delete` ("eliminar"), y `has` ("tiene"). Su constructor +crea un conjunto vacío, `añadir` agrega un valor al conjunto (pero solo si no +es ya un miembro), `eliminar` elimina su argumento del +conjunto (si era un miembro) y `tiene` retorna un valor booleano +que indica si su argumento es un miembro del conjunto. {{index "=== operator", "indexOf method"}} -Use the `===` operator, or something equivalent such as `indexOf`, to -determine whether two values are the same. +Usa el operador `===`, o algo equivalente como `indexOf`, para +determinar si dos valores son iguales. {{index "static method"}} -Give the class a static `from` method that takes an iterable object -as argument and creates a group that contains all the values produced -by iterating over it. +Proporcionale a la clase un método estático `desde` que tome un objeto iterable +como argumento y cree un grupo que contenga todos los valores producidos +al iterar sobre el. {{if interactive ```{test: no} -class Group { - // Your code here. +class Conjunto { + // Tu código aquí. } -let group = Group.from([10, 20]); -console.log(group.has(10)); +let conjunto = Conjunto.desde([10, 20]); +console.log(conjunto.tiene(10)); // → true -console.log(group.has(30)); +console.log(conjunto.tiene(30)); // → false -group.add(10); -group.delete(10); -console.log(group.has(10)); +conjunto.añadir(10); +conjunto.eliminar(10); +console.log(conjunto.tiene(10)); // → false ``` @@ -1148,55 +1144,55 @@ if}} {{index "groups (exercise)", "Group class", "indexOf method", "includes method"}} -The easiest way to do this is to store an array of group members -in an instance property. The `includes` or `indexOf` methods can be -used to check whether a given value is in the array. +La forma más fácil de hacer esto es almacenar un ((array)) con los miembros del +conjunto en una propiedad de instancia. Los métodos `includes` o `indexOf` +pueden ser usados para verificar si un valor dado está en el array. {{index "push method"}} -Your class's ((constructor)) can set the member collection to an empty -array. When `add` is called, it must check whether the given value is -in the array or add it, for example with `push`, otherwise. +El ((constructor)) de clase puede establecer la colección de miembros como +un array vacio. Cuando se llama a `añadir`, debes verificar si el valor dado +esta en el conjunto y agregarlo, por ejemplo con `push`, de lo contrario. {{index "filter method"}} -Deleting an element from an array, in `delete`, is less -straightforward, but you can use `filter` to create a new array -without the value. Don't forget to overwrite the property holding the -members with the newly filtered version of the array. +Eliminar un elemento de un array, en `eliminar`, es menos +sencillo, pero puedes usar `filter` para crear un nuevo array +sin el valor. No te olvides de sobrescribir la propiedad que +sostiene los miembros del conjunto con la versión recién filtrada del array. {{index "for/of loop", "iterable interface"}} -The `from` method can use a `for`/`of` loop to get the values out of -the iterable object and call `add` to put them into a newly created -group. +El método `desde` puede usar un bucle `for`/`of` para obtener los valores de +el objeto iterable y llamar a `añadir` para ponerlos en un conjunto recien +creado. hint}} -### Iterable groups +### Conjuntos Iterables -{{index "groups (exercise)", [interface, object], "iterator interface", "Group class"}} +{{index "groups (exercise)", interface, "iterator interface", "Group class"}} {{id group_iterator}} -Make the `Group` class from the previous exercise iterable. Refer -to the section about the iterator interface earlier in the chapter if -you aren't clear on the exact form of the interface anymore. +Haz iterable la clase `Conjunto` del ejercicio anterior. Puedes remitirte +a la sección acerca de la interfaz del iterador anteriormente en el capítulo si +ya no recuerdas muy bien la forma exacta de la interfaz. -If you used an array to represent the group's members, don't just -return the iterator created by calling the `Symbol.iterator` method on -the array. That would work, but it defeats the purpose of this exercise. +Si usaste un array para representar a los miembros del conjunto, no solo +retornes el iterador creado llamando al método `Symbol.iterator` en +el array. Eso funcionaría, pero frustra el propósito de este ejercicio. -It is okay if your iterator behaves strangely when the group is -modified during iteration. +Está bien si tu iterador se comporta de manera extraña cuando el conjunto es +modificado durante la iteración. {{if interactive ```{test: no} -// Your code here (and the code from the previous exercise) +// Tu código aquí (y el codigo del ejercicio anterior) -for (let value of Group.from(["a", "b", "c"])) { - console.log(value); +for (let valor of Conjunto.desde(["a", "b", "c"])) { + console.log(valor); } // → a // → b @@ -1209,36 +1205,36 @@ if}} {{index "groups (exercise)", "Group class", "next method"}} -It is probably worthwhile to define a new class `GroupIterator`. -Iterator instances should have a property that tracks the current -position in the group. Every time `next` is called, it checks whether -it is done and, if not, moves past the current value and returns it. +Probablemente valga la pena definir una nueva clase `IteradorConjunto`. +Las instancias de Iterador deberian tener una propiedad que rastree la +posición actual en el conjunto. Cada vez que se invoque a `next`, este +comprueba si está hecho, y si no, se mueve más allá del valor actual y +lo retorna. -The `Group` class itself gets a method named by `Symbol.iterator` -that, when called, returns a new instance of the iterator class for -that group. +La clase `Conjunto` recibe un método llamado por `Symbol.iterator` +que, cuando se llama, retorna una nueva instancia de la clase de iterador para +ese grupo. hint}} -### Borrowing a method +### Tomando un método prestado -Earlier in the chapter I mentioned that an object's `hasOwnProperty` -can be used as a more robust alternative to the `in` operator when you -want to ignore the prototype's properties. But what if your map needs -to include the word `"hasOwnProperty"`? You won't be able to call that -method anymore because the object's own property hides the method -value. +Anteriormente en el capítulo mencioné que el metodo `hasOwnProperty` de un +objeto puede usarse como una alternativa más robusta al operador `in` cuando +quieras ignorar las propiedades del prototipo. Pero, ¿y si tu mapa necesita +incluir la palabra `"hasOwnProperty"`? Ya no podrás llamar a ese +método ya que la propiedad del objeto oculta el valor del método. -Can you think of a way to call `hasOwnProperty` on an object that has -its own property by that name? +¿Puedes pensar en una forma de llamar `hasOwnProperty` en un objeto que tiene +una propia propiedad con ese nombre? {{if interactive ```{test: no} -let map = {one: true, two: true, hasOwnProperty: true}; +let mapa = {uno: true, dos: true, hasOwnProperty: true}; -// Fix this call -console.log(map.hasOwnProperty("one")); +// Arregla esta llamada +console.log(mapa.hasOwnProperty("uno")); // → true ``` @@ -1246,10 +1242,10 @@ if}} {{hint -Remember that methods that exist on plain objects come from +Recuerda que los métodos que existen en objetos simples provienen de `Object.prototype`. -Also remember that you can call a function with a specific `this` -binding by using its `call` method. +Y que puedes llamar a una función con una vinculación `this` específica al +usar su método `call`. hint}} diff --git a/07_robot.md b/07_robot.md index 61f29d295..d3a707ab9 100644 --- a/07_robot.md +++ b/07_robot.md @@ -1,11 +1,11 @@ {{meta {load_files: ["code/chapter/07_robot.js", "code/animatevillage.js"], zip: html}}} -# Project: A Robot +# Proyecto: Un Robot {{quote {author: "Edsger Dijkstra", title: "The Threats to Computing Science", chapter: true} -[...] the question of whether Machines Can Think [...] is about as -relevant as the question of whether Submarines Can Swim. +[...] la pregunta de si las Maquinas Pueden Pensar [...] es tan +relevante como la pregunta de si los Submarinos Pueden Nadar. quote}} @@ -15,556 +15,557 @@ quote}} {{index "project chapter", "reading code", "writing code"}} -In "project" chapters, I'll stop pummeling you with new theory for a -brief moment, and instead we'll work through a program together. Theory -is necessary to learn to program, but reading and understanding actual -programs is just as important. +En los capítulos de "proyectos", dejaré de golpearte con teoría nueva +por un breve momento y en su lugar vamos a trabajar juntos en un programa. +La teoría es necesaria para aprender a programar, pero leer y entender +programas reales es igual de importante. -Our project in this chapter is to build an ((automaton)), a little -program that performs a task in a ((virtual world)). Our automaton -will be a mail-delivery ((robot)) picking up and dropping off parcels. +Nuestro proyecto en este capítulo es construir un ((autómata)), un pequeño +programa que realiza una tarea en un ((mundo virtual)). Nuestro autómata +será un ((robot)) de entregas por correo que recoge y deja paquetes. -## Meadowfield +## VillaPradera {{index "roads array"}} -The village of ((Meadowfield)) isn't very big. It consists of 11 -places with 14 roads between them. It can be described with this -array of roads: +El pueblo de ((VillaPradera)) no es muy grande. Este consiste de 11 +lugares con 14 caminos entre ellos. Puede ser describido con este +array de caminos: + ```{includeCode: true} -const roads = [ - "Alice's House-Bob's House", "Alice's House-Cabin", - "Alice's House-Post Office", "Bob's House-Town Hall", - "Daria's House-Ernie's House", "Daria's House-Town Hall", - "Ernie's House-Grete's House", "Grete's House-Farm", - "Grete's House-Shop", "Marketplace-Farm", - "Marketplace-Post Office", "Marketplace-Shop", - "Marketplace-Town Hall", "Shop-Town Hall" +const caminos = [ + "Casa de Alicia-Casa de Bob", "Casa de Alicia-Cabaña", + "Casa de Alicia-Oficina de Correos", "Casa de Bob-Ayuntamiento", + "Casa de Daria-Casa de Ernie", "Casa de Daria-Ayuntamiento", + "Casa de Ernie-Casa de Grete", "Casa de Grete-Granja", + "Casa de Grete-Tienda", "Mercado-Granja", + "Mercado-Oficina de Correos", "Mercado-Tienda", + "Mercado-Ayuntamiento", "Tienda-Ayuntamiento" ]; ``` {{figure {url: "img/village2x.png", alt: "The village of Meadowfield"}}} -The network of roads in the village forms a _((graph))_. A graph is a -collection of points (places in the village) with lines between them -(roads). This graph will be the world that our robot moves through. +La red de caminos en el pueblo forma un _((grafo))_. Un grafo es una +colección de puntos (lugares en el pueblo) con líneas entre ellos +(caminos). Este grafo será el mundo por el que nuestro robot se movera. {{index "roadGraph object"}} -The array of strings isn't very easy to work with. What we're -interested in is the destinations that we can reach from a given -place. Let's convert the list of roads to a data structure that, for -each place, tells us what can be reached from there. +El array de strings no es muy fácil de trabajar. En lo que estamos +interesados es en los destinos a los que podemos llegar desde un lugar +determinado. Vamos a convertir la lista de caminos en una estructura de datos +que, para cada lugar, nos diga a donde se pueda llegar desde allí. ```{includeCode: true} -function buildGraph(edges) { - let graph = Object.create(null); - function addEdge(from, to) { - if (graph[from] == null) { - graph[from] = [to]; +function construirGrafo(bordes) { + let grafo = Object.create(null); + function añadirBorde(desde, hasta) { + if (grafo[desde] == null) { + grafo[desde] = [hasta]; } else { - graph[from].push(to); + grafo[desde].push(hasta); } } - for (let [from, to] of edges.map(r => r.split("-"))) { - addEdge(from, to); - addEdge(to, from); + for (let [desde, hasta] of bordes.map(c => c.split("-"))) { + añadirBorde(desde, hasta); + añadirBorde(hasta, desde); } - return graph; + return grafo; } -const roadGraph = buildGraph(roads); +const grafoCamino = construirGrafo(roads); ``` -Given an array of edges, `buildGraph` creates a map object that, for -each node, stores an array of connected nodes. +Dado un conjunto de bordes, `construirGrafo` crea un objeto de mapa que, para +cada nodo, almacena un array de nodos conectados. {{index "split method"}} -It uses the `split` method to go from the road strings, which have the -form `"Start-End"`, to two-element arrays containing the start and end -as separate strings. +Utiliza el método `split` para ir de los strings de caminos, que tienen la +forma `"Comienzo-Final"`, a arrays de dos elementos que contienen el inicio y +el final como strings separados. -## The task +## La tarea -Our ((robot)) will be moving around the village. There are parcels -in various places, each addressed to some other place. The robot picks -up parcels when it comes to them and delivers them when it arrives at -their destinations. +Nuestro ((robot)) se moverá por el pueblo. Hay paquetes +en varios lugares, cada uno dirigido a otro lugar. El robot tomara +paquetes cuando los encuentre y los entregara cuando llegue a +sus destinos. -The automaton must decide, at each point, where to go next. It has -finished its task when all parcels have been delivered. +El autómata debe decidir, en cada punto, a dónde ir después. Ha +finalizado su tarea cuando se han entregado todos los paquetes. {{index simulation, "virtual world"}} -To be able to simulate this process, we must define a virtual world -that can describe it. This model tells us where the robot is and where -the parcels are. When the robot has decided to move somewhere, we need -to update the model to reflect the new situation. - -{{index [state, in objects]}} - -If you're thinking in terms of ((object-oriented programming)), your -first impulse might be to start defining objects for the various -elements in the world: a ((class)) for the robot, one for a parcel, -maybe one for places. These could then hold properties that describe -their current ((state)), such as the pile of parcels at a location, -which we could change when updating the world. - -This is wrong. - -At least, it usually is. The fact that something sounds like an object -does not automatically mean that it should be an object in your -program. Reflexively writing classes for every concept in your -application tends to leave you with a collection of interconnected -objects that each have their own internal, changing state. Such -programs are often hard to understand and thus easy to break. - -{{index [state, in objects]}} - -Instead, let's condense the village's state down to the minimal -set of values that define it. There's the robot's current location and -the collection of undelivered parcels, each of which has a current -location and a destination address. That's it. +Para poder simular este proceso, debemos definir un mundo virtual que +pueda describirlo. Este modelo nos dice dónde está el robot y dónde estan +los paquetes. Cuando el robot ha decidido moverse a alguna parte, necesitamos +actualizar el modelo para reflejar la nueva situación. + +Si estás pensando en términos de ((programación orientada a objetos)), tu +primer impulso podría ser comenzar a definir objetos para los diversos +elementos en el mundo. Una ((clase)) para el robot, una para un paquete, +tal vez una para los lugares. Estas podrían tener propiedades que describen +su ((estado)) actual, como la pila de paquetes en un lugar, +que podríamos cambiar al actualizar el mundo. + +Esto está mal. + +Al menos, usualmente lo esta. El hecho de que algo suena como un objeto +no significa automáticamente que debe ser un objeto en tu +programa. Escribir por reflejo las clases para cada concepto en tu +aplicación tiende a dejarte con una colección de objetos interconectados +donde cada uno tiene su propio estado interno y cambiante. Tales +programas a menudo son difíciles de entender y, por lo tanto, fáciles +de romper. + +En lugar de eso, condensemos el ((estado)) del pueblo hasta el mínimo +conjunto de valores que lo definan. Está la ubicación actual del robot y +la colección de paquetes no entregados, cada uno de los cuales tiene una +ubicación actual y una dirección de destino. Eso es todo. {{index "VillageState class", "persistent data structure"}} -And while we're at it, let's make it so that we don't _change_ this -state when the robot moves but rather compute a _new_ state for the -situation after the move. +Y mientras estamos en ello, hagámoslo de manera que no _cambiemos_ este +estado cuándo se mueva el robot, sino calcular un _nuevo_ estado para la +situación después del movimiento. ```{includeCode: true} -class VillageState { - constructor(place, parcels) { - this.place = place; - this.parcels = parcels; +class EstadoPueblo { + constructor(lugar, paquetes) { + this.lugar = lugar; + this.paquetes = paquetes; } - move(destination) { - if (!roadGraph[this.place].includes(destination)) { + mover(destino) { + if (!grafoCamino[this.lugar].includes(destino)) { return this; } else { - let parcels = this.parcels.map(p => { - if (p.place != this.place) return p; - return {place: destination, address: p.address}; - }).filter(p => p.place != p.address); - return new VillageState(destination, parcels); + let paquetes = this.paquetes.map(p => { + if (p.lugar != this.lugar) return p; + return {lugar: destino, direccion: p.direccion}; + }).filter(p => p.lugar != p.direccion); + return new EstadoPueblo(destino, paquetes); } } } ``` -The `move` method is where the action happens. It first checks whether -there is a road going from the current place to the destination, and -if not, it returns the old state since this is not a valid move. +En el método `mover` es donde ocurre la acción. Este primero verifica si +hay un camino que va del lugar actual al destino, y +si no, retorna el estado anterior, ya que este no es un movimiento válido. {{index "map method", "filter method"}} -Then it creates a new state with the destination as the robot's new -place. But it also needs to create a new set of parcels—parcels that -the robot is carrying (that are at the robot's current place) need to -be moved along to the new place. And parcels that are addressed to the -new place need to be delivered—that is, they need to be removed from -the set of undelivered parcels. The call to `map` takes care of the -moving, and the call to `filter` does the delivering. +Luego crea un nuevo estado con el destino como el nuevo lugar del robot. +Pero también necesita crear un nuevo conjunto de paquetes—los paquetes que +el robot esta llevando (que están en el lugar actual del robot) necesitan de +moverse también al nuevo lugar. Y paquetes que están dirigidos al nuevo +lugar donde deben de ser entregados—es decir, deben de eliminarse del +conjunto de paquetes no entregados. La llamada a `map` se encarga de mover +los paquetes, y la llamada a `filter` hace la entrega. -Parcel objects aren't changed when they are moved but re-created. The -`move` method gives us a new village state but leaves the old one -entirely intact. +Los objetos de paquete no se modifican cuando se mueven, sino que se vuelven +a crear. El método `movee` nos da un nuevo estado de aldea, pero deja el +viejo completamente intacto ``` -let first = new VillageState( - "Post Office", - [{place: "Post Office", address: "Alice's House"}] +let primero = new EstadoPueblo( + "Oficina de Correos", + [{lugar: "Oficina de Correos", direccion: "Casa de Alicia"}] ); -let next = first.move("Alice's House"); +let siguiente = primero.mover("Casa de Alicia"); -console.log(next.place); -// → Alice's House -console.log(next.parcels); +console.log(siguiente.lugar); +// → Casa de Alicia +console.log(siguiente.parcels); // → [] -console.log(first.place); -// → Post Office +console.log(primero.lugar); +// → Oficina de Correos ``` -The move causes the parcel to be delivered, and this is reflected in -the next state. But the initial state still describes the situation -where the robot is at the post office and the parcel is undelivered. +Mover hace que se entregue el paquete, y esto se refleja en +el próximo estado. Pero el estado inicial todavía describe la situación +donde el robot está en la oficina de correos y el paquete aun no ha +sido entregado. -## Persistent data +## Datos persistentes -{{index "persistent data structure", mutability, ["data structure", immutable]}} +{{index "persistent data structure", mutability, "data structure"}} -Data structures that don't change are called _((immutable))_ or -_persistent_. They behave a lot like strings and numbers in that they -are who they are and stay that way, rather than containing different -things at different times. +Las estructuras de datos que no cambian se llaman _((inmutables))_ o +_persistentes_. Se comportan de manera muy similar a los strings y números +en que son quienes son, y se quedan así, en lugar de contener diferentes +cosas en diferentes momentos. -In JavaScript, just about everything _can_ be changed, so working with -values that are supposed to be persistent requires some restraint. -There is a function called `Object.freeze` that changes an object so -that writing to its properties is ignored. You could use that to make -sure your objects aren't changed, if you want to be careful. Freezing -does require the computer to do some extra work, and having updates -ignored is just about as likely to confuse someone as having them do -the wrong thing. So I usually prefer to just tell people that a given -object shouldn't be messed with and hope they remember it. +En JavaScript, casi todo _puede_ ser cambiado, así que trabajar con +valores que se supone que sean persistentes requieren cierta restricción. +Hay una función llamada `Object.freeze` ("Objeto.congelar") que cambia un objeto +de manera que escribir en sus propiedades sea ignorado. Podrías usar eso para +asegurarte de que tus objetos no cambien, si quieres ser cuidadoso. La congelación +requiere que la computadora haga un trabajo extra e ignorar actualizaciones +es probable que confunda a alguien tanto como para que hagan +lo incorrecto. Por lo general, prefiero simplemente decirle a la gente que un +determinado objeto no debe ser molestado, y espero que lo recuerden. ``` -let object = Object.freeze({value: 5}); -object.value = 10; -console.log(object.value); +let objeto = Object.freeze({valor: 5}); +objeto.valor = 10; +console.log(objeto.valor); // → 5 ``` -Why am I going out of my way to not change objects when the language -is obviously expecting me to? +Por qué me salgo de mi camino para no cambiar objetos cuando el lenguaje +obviamente está esperando que lo haga? -Because it helps me understand my programs. This is about complexity -management again. When the objects in my system are fixed, stable -things, I can consider operations on them in isolation—moving to -Alice's house from a given start state always produces the same new -state. When objects change over time, that adds a whole new dimension -of complexity to this kind of reasoning. +Porque me ayuda a entender mis programas. Esto es acerca de manejar la +complejidad nuevamente. Cuando los objetos en mi sistema son cosas fijas y +estables, puedo considerar las operaciones en ellos de forma aislada—moverse +a la casa de Alicia desde un estado de inicio siempre produce el mismo nuevo +estado. Cuando los objetos cambian con el tiempo, eso agrega una dimensión +completamente nueva de complejidad a este tipo de razonamiento. -For a small system like the one we are building in this chapter, we -could handle that bit of extra complexity. But the most important -limit on what kind of systems we can build is how much we can -understand. Anything that makes your code easier to understand makes -it possible to build a more ambitious system. +Para un sistema pequeño como el que estamos construyendo en este capítulo, +podriamos manejar ese poco de complejidad adicional. Pero el límite +más importante sobre qué tipo de sistemas podemos construir es cuánto podemos +entender. Cualquier cosa que haga que tu código sea más fácil de entender hace +que sea posible construir un sistema más ambicioso. -Unfortunately, although understanding a system built on persistent data -structures is easier, _designing_ one, especially when your -programming language isn't helping, can be a little harder. We'll -look for opportunities to use persistent data structures in this -book, but we'll also be using changeable ones. +Lamentablemente, aunque entender un sistema basado en estructuras de datos +persistentes es más fácil, _diseñar_ uno, especialmente cuando tu +lenguaje de programación no ayuda, puede ser un poco más difícil. +Buscaremos oportunidades para usar estructuras de datos persistentes en este +libro, pero también utilizaremos las modificables. -## Simulation +## Simulación {{index simulation, "virtual world"}} -A delivery ((robot)) looks at the world and decides in which -direction it wants to move. As such, we could say that a robot is a -function that takes a `VillageState` object and returns the name of a -nearby place. +Un ((robot)) de entregas mira al mundo y decide en qué +dirección que quiere moverse. Como tal, podríamos decir que un robot es una +función que toma un objeto `EstadoPueblo` y retorna el nombre de un +lugar cercano. {{index "runRobot function"}} -Because we want robots to be able to remember things, so that they can -make and execute plans, we also pass them their memory and allow them -to return a new memory. Thus, the thing a robot returns is an object -containing both the direction it wants to move in and a memory value -that will be given back to it the next time it is called. +Ya que queremos que los robots sean capaces de recordar cosas, para que puedan +hacer y ejecutar planes, también les pasamos su memoria y les permitimos +retornar una nueva memoria. Por lo tanto, lo que retorna un robot es un objeto +que contiene tanto la dirección en la que quiere moverse como un +valor de memoria que se le sera regresado la próxima vez que se llame. ```{includeCode: true} -function runRobot(state, robot, memory) { - for (let turn = 0;; turn++) { - if (state.parcels.length == 0) { - console.log(`Done in ${turn} turns`); +function correrRobot(estado, robot, memoria) { + for (let turno = 0;; turno++) { + if (estado.paquetes.length == 0) { + console.log(`Listo en ${turno} turnos`); break; } - let action = robot(state, memory); - state = state.move(action.direction); - memory = action.memory; - console.log(`Moved to ${action.direction}`); + let accion = robot(estado, memoria); + estado = estado.mover(accion.direccion); + memoria = accion.memoria; + console.log(`Moverse a ${accion.direccion}`); } } ``` -Consider what a robot has to do to "solve" a given state. It must pick -up all parcels by visiting every location that has a parcel and -deliver them by visiting every location that a parcel is addressed to, -but only after picking up the parcel. +Considera lo que un robot tiene que hacer para "resolver" un estado dado. Debe +recoger todos los paquetes visitando cada ubicación que tenga un paquete, y +entregarlos visitando cada lugar al que se dirige un paquete, +pero solo después de recoger el paquete. -What is the dumbest strategy that could possibly work? The robot could -just walk in a random direction every turn. That means, with -great likelihood, it will eventually run into all parcels and then -also at some point reach the place where they should be delivered. +Cuál es la estrategia más estúpida que podría funcionar? El robot podría +simplemente caminar hacia una dirección aleatoria en cada vuelta. Eso significa, con +gran probabilidad, que eventualmente se encontrara con todos los paquetes, y luego +también en algún momento llegara a todos los lugares donde estos deben +ser entregados. {{index "randomPick function", "randomRobot function"}} -Here's what that could look like: +Aqui esta como se podria ver eso: ```{includeCode: true} -function randomPick(array) { - let choice = Math.floor(Math.random() * array.length); - return array[choice]; +function eleccionAleatoria(array) { + let eleccion = Math.floor(Math.random() * array.length); + return array[eleccion]; } -function randomRobot(state) { - return {direction: randomPick(roadGraph[state.place])}; +function robotAleatorio(estado) { + return {direccion: eleccionAleatoria(grafoCamino[estado.lugar])}; } ``` -{{index "Math.random function", "Math.floor function", [array, "random element"]}} +{{index "Math.random function", "Math.floor function", array}} -Remember that `Math.random()` returns a number between zero and -one—but always below one. Multiplying such a number by the length of an -array and then applying `Math.floor` to it gives us a random index for -the array. +Recuerda que `Math.random ()` retorna un número entre cero y uno, +pero siempre debajo de uno. Multiplicar dicho número por la longitud de un +array y luego aplicarle `Math.floor` nos da un índice aleatorio para +el array. -Since this robot does not need to remember anything, it ignores its -second argument (remember that JavaScript functions can be called with -extra arguments without ill effects) and omits the `memory` property -in its returned object. +Como este robot no necesita recordar nada, ignora su +segundo argumento (recuerda que puedes llamar a las funciones en JavaScript con +argumentos adicionales sin efectos negativos) y omite la propiedad `memoria` +en su objeto retornado. -To put this sophisticated robot to work, we'll first need a way to -create a new state with some parcels. A static method (written here by -directly adding a property to the constructor) is a good place to put -that functionality. +Para poner en funcionamiento este sofisticado robot, primero necesitaremos una +forma de crear un nuevo estado con algunos paquetes. Un método estático +(escrito aquí al agregar directamente una propiedad al constructor) es un +buen lugar para poner esa funcionalidad. ```{includeCode: true} -VillageState.random = function(parcelCount = 5) { - let parcels = []; - for (let i = 0; i < parcelCount; i++) { - let address = randomPick(Object.keys(roadGraph)); - let place; +EstadoPueblo.aleatorio = function(numeroDePaquetes = 5) { + let paquetes = []; + for (let i = 0; i < numeroDePaquetes; i++) { + let direccion = eleccionAleatoria(Object.keys(grafoCamino)); + let lugar; do { - place = randomPick(Object.keys(roadGraph)); - } while (place == address); - parcels.push({place, address}); + lugar = eleccionAleatoria(Object.keys(grafoCamino)); + } while (lugar == direccion); + paquetes.push({lugar, direccion}); } - return new VillageState("Post Office", parcels); + return new EstadoPueblo("Oficina de Correos", paquetes); }; ``` {{index "do loop"}} -We don't want any parcels that are sent from the same place that they -are addressed to. For this reason, the `do` loop keeps picking new -places when it gets one that's equal to the address. +No queremos paquetes que sean enviados desde el mismo lugar al que están +dirigidos. Por esta razón, el bucle `do` sigue seleccionando nuevos +lugares cuando obtenga uno que sea igual a la dirección. -Let's start up a virtual world. +Comencemos un mundo virtual. ```{test: no} -runRobot(VillageState.random(), randomRobot); -// → Moved to Marketplace -// → Moved to Town Hall +correrRobot(EstadoPueblo.aleatorio(), robotAleatorio); +// → Moverse a Mercado +// → Moverse a Ayuntamiento // → … -// → Done in 63 turns +// → Listo en 63 turnos ``` -It takes the robot a lot of turns to deliver the parcels because it -isn't planning ahead very well. We'll address that soon. +Le toma muchas vueltas al robot para entregar los paquetes, porque este +no está planeando muy bien. Nos ocuparemos de eso pronto. {{if interactive -For a more pleasant perspective on the simulation, you can use the -`runRobotAnimation` function that's available in [this chapter's -programming environment](https://eloquentjavascript.net/code/#7). -This runs the simulation, but instead of outputting text, it shows -you the robot moving around the village map. +Para una perspectiva más agradable de la simulación, puedes usar la +función `runRobotAnimation` que está disponible en el entorno de programación +de este capítulo. Esta ejecuta la simulación, pero en lugar de +mostrar texto, muestra al robot moviéndose por el mapa del pueblo. ```{test: no} -runRobotAnimation(VillageState.random(), randomRobot); +runRobotAnimation(EstadoPueblo.aleatorio(), robotAleatorio); ``` -The way `runRobotAnimation` is implemented will remain a mystery for -now, but after you've read the [later chapters](dom) of this book, -which discuss JavaScript integration in web browsers, you'll be able -to guess how it works. +La forma en la que `runRobotAnimation` esta implementada seguirá siendo un +misterio por ahora, pero después de que hayas leído [capítulos mas avanzados](dom) +de este libro, que discuten la integración de JavaScript en los navegadores web, +podrás adivinar cómo esta funciona. if}} -## The mail truck's route +## La ruta del camión de correos {{index "mailRoute array"}} -We should be able to do a lot better than the random ((robot)). An -easy improvement would be to take a hint from the way real-world mail -delivery works. If we find a route that passes all places in the -village, the robot could run that route twice, at which point it is -guaranteed to be done. Here is one such route (starting from the post -office): +Deberíamos poder hacer algo mucho mejor que el ((robot)) aleatorio. Una +mejora fácil sería tomar una pista de la forma en que como funciona la entrega +de correos en el mundo real. Si encontramos una ruta que pasa por todos los +lugares en el pueblo, el robot podría ejecutar esa ruta dos veces, +y en ese punto esta garantizado que ha entregado todos los paquetes. +Aquí hay una de esas rutas (comenzando desde la Oficina de Correos). ```{includeCode: true} -const mailRoute = [ - "Alice's House", "Cabin", "Alice's House", "Bob's House", - "Town Hall", "Daria's House", "Ernie's House", - "Grete's House", "Shop", "Grete's House", "Farm", - "Marketplace", "Post Office" +const rutaCorreo = [ + "Casa de Alicia", "Cabaña", "Casa de Alicia", "Casa de Bob", + "Ayuntamiento", "Casa de Daria", "Casa de Ernie", + "GCasa de Grete", "Tienda", "Casa de Grete", "Granja", + "Mercado", "Oficina de Correos" ]; ``` {{index "routeRobot function"}} -To implement the route-following robot, we'll need to make use of -robot memory. The robot keeps the rest of its route in its memory and -drops the first element every turn. +Para implementar el robot que siga la ruta, necesitaremos hacer uso de la +memoria del robot. El robot mantiene el resto de su ruta en su memoria y +deja caer el primer elemento en cada vuelta. ```{includeCode: true} -function routeRobot(state, memory) { - if (memory.length == 0) { - memory = mailRoute; +function robotRuta(estado, memoria) { + if (memoria.length == 0) { + memoria = rutaCorreo; } - return {direction: memory[0], memory: memory.slice(1)}; + return {direction: memoria[0], memoria: memoria.slice(1)}; } ``` -This robot is a lot faster already. It'll take a maximum of 26 turns -(twice the 13-step route) but usually less. +Este robot ya es mucho más rápido. Tomará un máximo de 26 turnos +(dos veces la ruta de 13 pasos), pero generalmente seran menos. {{if interactive ```{test: no} -runRobotAnimation(VillageState.random(), routeRobot, []); +runRobotAnimation(EstadoPueblo.aleatorio(), robotRuta, []); ``` if}} -## Pathfinding +## Búsqueda de rutas -Still, I wouldn't really call blindly following a fixed route -intelligent behavior. The ((robot)) could work more efficiently if it -adjusted its behavior to the actual work that needs to be done. +Aún así, realmente no llamaría seguir ciegamente una ruta fija +comportamiento inteligente. El ((robot)) podría funcionar más eficientemente si +ajustara su comportamiento al trabajo real que necesita hacerse. {{index pathfinding}} -To do that, it has to be able to deliberately move toward a given -parcel or toward the location where a parcel has to be delivered. -Doing that, even when the goal is more than one move away, will -require some kind of route-finding function. - -The problem of finding a route through a ((graph)) is a typical -_((search problem))_. We can tell whether a given solution (a route) -is a valid solution, but we can't directly compute the solution the -way we could for 2 + 2. Instead, we have to keep creating potential -solutions until we find one that works. - -The number of possible routes through a graph is infinite. But -when searching for a route from _A_ to _B_, we are interested only in -the ones that start at _A_. We also don't care about routes that visit -the same place twice—those are definitely not the most efficient route -anywhere. So that cuts down on the number of routes that the route -finder has to consider. - -In fact, we are mostly interested in the _shortest_ route. So we want -to make sure we look at short routes before we look at longer ones. A -good approach would be to "grow" routes from the starting point, -exploring every reachable place that hasn't been visited yet, until a -route reaches the goal. That way, we'll only explore routes that are -potentially interesting, and we'll find the shortest route (or one of the -shortest routes, if there are more than one) to the goal. +Para hacer eso, tiene que ser capaz de avanzar deliberadamente hacia un +determinado paquete, o hacia la ubicación donde se debe entregar un paquete. +Al hacer eso, incluso cuando el objetivo esté a más de un movimiento de +distancia, requiere algún tipo de función de búsqueda de ruta. + +El problema de encontrar una ruta a través de un ((grafo)) es un típico +_((problema de búsqueda))_. Podemos decir si una solución dada (una ruta) +es una solución válida, pero no podemos calcular directamente la solución de +la misma manera que podríamos para 2 + 2. En cambio, tenemos que seguir +creando soluciones potenciales hasta que encontremos una que funcione. + +El número de rutas posibles a través de un grafo es infinito. Pero +cuando buscamos una ruta de _A_ a _B_, solo estamos interesados ​​en aquellas +que comienzan en _A_. Tampoco nos importan las rutas que visitan +el mismo lugar dos veces, definitivamente esa no es la ruta más eficiente +en cualquier sitio. Entonces eso reduce la cantidad de rutas que el +buscador de rutas tiene que considerar. + +De hecho, estamos más interesados ​​en la ruta _mas corta_. Entonces queremos +asegurarnos de mirar las rutas cortas antes de mirar las más largas. Un +buen enfoque sería "crecer" las rutas desde el punto de partida, +explorando cada lugar accesible que aún no ha sido visitado, hasta que +una ruta alcanze la meta. De esa forma, solo exploraremos las rutas que son +potencialmente interesantes, y encontremos la ruta más corta (o una de las +rutas más cortas, si hay más de una) a la meta. {{index "findRoute function"}} {{id findRoute}} -Here is a function that does this: +Aquí hay una función que hace esto: ```{includeCode: true} -function findRoute(graph, from, to) { - let work = [{at: from, route: []}]; - for (let i = 0; i < work.length; i++) { - let {at, route} = work[i]; - for (let place of graph[at]) { - if (place == to) return route.concat(place); - if (!work.some(w => w.at == place)) { - work.push({at: place, route: route.concat(place)}); +function encontrarRuta(grafo, desde, hasta) { + let trabajo = [{donde: desde, ruta: []}]; + for (let i = 0; i < trabajo.length; i++) { + let {donde, ruta} = trabajo[i]; + for (let lugar of grafo[donde]) { + if (lugar == hasta) return ruta.concat(lugar); + if (!trabajo.some(w => w.donde == lugar)) { + trabajo.push({donde: lugar, ruta: ruta.concat(lugar)}); } } } } ``` -The exploring has to be done in the right order—the places that were -reached first have to be explored first. We can't immediately explore -a place as soon as we reach it because that would mean places reached -_from there_ would also be explored immediately, and so on, even -though there may be other, shorter paths that haven't yet been -explored. - -Therefore, the function keeps a _((work list))_. This is an array of -places that should be explored next, along with the route that got us -there. It starts with just the start position and an empty route. - -The search then operates by taking the next item in the list and -exploring that, which means all roads going from that place are -looked at. If one of them is the goal, a finished route can be -returned. Otherwise, if we haven't looked at this place before, a new -item is added to the list. If we have looked at it before, since we -are looking at short routes first, we've found either a longer route -to that place or one precisely as long as the existing one, and we -don't need to explore it. - -You can visually imagine this as a web of known routes crawling out -from the start location, growing evenly on all sides (but never -tangling back into itself). As soon as the first thread reaches the -goal location, that thread is traced back to the start, giving us our -route. +La exploración tiene que hacerse en el orden correcto—los lugares que fueron +alcanzados primero deben ser explorados primero. No podemos explorar de inmediato +un lugar apenas lo alcanzamos, porque eso significaría que los lugares alcanzados +_desde allí_ también se explorarían de inmediato, y así sucesivamente, incluso +aunque puedan haber otros caminos más cortos que aún no han sido +explorados. + +Por lo tanto, la función mantiene una _((lista de trabajo))_. Esta es un array de +lugares que deberían explorarse a continuación, junto con la ruta que nos llevó +ahí. Esta comienza solo con la posición de inicio y una ruta vacía. + +La búsqueda luego opera tomando el siguiente elemento en la lista y +explorando eso, lo que significa que todos los caminos que van desde ese lugar +son mirados. Si uno de ellos es el objetivo, una ruta final puede ser +retornada. De lo contrario, si no hemos visto este lugar antes, un nuevo +elemento se agrega a la lista. Si lo hemos visto antes, ya que +estamos buscando primero rutas cortas, hemos encontrado una ruta más larga +a ese lugar o una precisamente tan larga como la existente, y +no necesitamos explorarla. + +Puedes imaginar esto visualmente como una red de rutas conocidas que se arrastran +desde el lugar de inicio, creciendo uniformemente hacia todos los lados (pero nunca +enredándose de vuelta a si misma). Tan pronto como el primer hilo llegue a +la ubicación objetivo, ese hilo se remonta al comienzo, dándonos asi nuestra +ruta. {{index "connected graph"}} -Our code doesn't handle the situation where there are no more work -items on the work list because we know that our graph is _connected_, -meaning that every location can be reached from all other locations. -We'll always be able to find a route between two points, and the -search can't fail. +Nuestro código no maneja la situación donde no hay más elementos de trabajo +en la lista de trabajo, porque sabemos que nuestro gráfico está +_conectado_, lo que significa que se puede llegar a todos los lugares +desde todos los otros lugares. Siempre podremos encontrar una ruta entre +dos puntos, y la búsqueda no puede fallar. ```{includeCode: true} -function goalOrientedRobot({place, parcels}, route) { - if (route.length == 0) { - let parcel = parcels[0]; - if (parcel.place != place) { - route = findRoute(roadGraph, place, parcel.place); +function robotOrientadoAMetas({lugar, paquetes}, ruta) { + if (ruta.length == 0) { + let paquete = paquetes[0]; + if (paquete.lugar != lugar) { + ruta = encontrarRuta(grafoCamino, lugar, paquete.lugar); } else { - route = findRoute(roadGraph, place, parcel.address); + ruta = encontrarRuta(grafoCamino, lugar, paquete.direccion); } } - return {direction: route[0], memory: route.slice(1)}; + return {direccion: ruta[0], memoria: ruta.slice(1)}; } ``` {{index "goalOrientedRobot function"}} -This robot uses its memory value as a list of directions to move in, -just like the route-following robot. Whenever that list is empty, it -has to figure out what to do next. It takes the first undelivered -parcel in the set and, if that parcel hasn't been picked up yet, plots a -route toward it. If the parcel _has_ been picked up, it still needs to be -delivered, so the robot creates a route toward the delivery address instead. +Este robot usa su valor de memoria como una lista de instrucciones para moverse, +como el robot que sigue la ruta. Siempre que esa lista esté vacía, este +tiene que descubrir qué hacer a continuación. Toma el primer paquete no entregado +en el conjunto y, si ese paquete no se ha recogido aún, traza una +ruta hacia el. Si el paquete _ha_ sido recogido, todavía debe ser +entregado, por lo que el robot crea una ruta hacia la dirección de entrega +en su lugar. {{if interactive -Let's see how it does. +Veamos cómo le va. ```{test: no, startCode: true} -runRobotAnimation(VillageState.random(), - goalOrientedRobot, []); +runRobotAnimation(EstadoPueblo.aleatorio(), + robotOrientadoAMetas, []); ``` if}} -This robot usually finishes the task of delivering 5 parcels in about -16 turns. That's slightly better than `routeRobot` but still definitely not optimal. +Este robot generalmente termina la tarea de entregar 5 paquetes en +16 turnos aproximadamente. Un poco mejor que `robotRuta`, +pero definitivamente no es óptimo. -## Exercises +## Ejercicios -### Measuring a robot +### Midiendo un robot {{index "measuring a robot (exercise)", testing, automation, "compareRobots function"}} -It's hard to objectively compare ((robot))s by just letting them solve -a few scenarios. Maybe one robot just happened to get easier tasks or -the kind of tasks that it is good at, whereas the other didn't. +Es difícil comparar objetivamente ((robot))s simplemente dejándolos resolver +algunos escenarios. Tal vez un robot acaba de conseguir tareas más fáciles, o +el tipo de tareas en las que es bueno, mientras que el otro no. -Write a function `compareRobots` that takes two robots (and their -starting memory). It should generate 100 tasks and let each of -the robots solve each of these tasks. When done, it should output the -average number of steps each robot took per task. +Escribe una función `compararRobots` que toma dos robots (y su +memoria de inicio). Debe generar 100 tareas y dejar que cada uno de +los robots resuelvan cada una de estas tareas. Cuando terminen, +debería generar el promedio de pasos que cada robot tomó por tarea. -For the sake of fairness, make sure you give each task to both -robots, rather than generating different tasks per robot. +En favor de lo que es justo, asegúrate de la misma tarea a ambos +robots, en lugar de generar diferentes tareas por robot. {{if interactive ```{test: no} -function compareRobots(robot1, memory1, robot2, memory2) { - // Your code here +function compararRobots(robot1, memoria1, robot2, memoria2) { + // Tu código aqui } -compareRobots(routeRobot, [], goalOrientedRobot, []); +compararRobots(robotRuta, [], robotOrientadoAMetas, []); ``` if}} @@ -572,35 +573,35 @@ if}} {{index "measuring a robot (exercise)", "runRobot function"}} -You'll have to write a variant of the `runRobot` function that, -instead of logging the events to the console, returns the number of -steps the robot took to complete the task. +Tendrás que escribir una variante de la función `correrRobot` que, +en lugar de registrar los eventos en la consola, retorne el número de +pasos que le tomó al robot completar la tarea. -Your measurement function can then, in a loop, generate new states and -count the steps each of the robots takes. When it has generated enough -measurements, it can use `console.log` to output the average for each -robot, which is the total number of steps taken divided by the number -of measurements. +Tu función de medición puede, en un ciclo, generar nuevos estados y +contar los pasos que lleva cada uno de los robots. Cuando has generado +suficientes mediciones, puedes usar `console.log` para mostrar el +promedio de cada robot, que es la cantidad total de pasos tomados dividido por +el número de mediciones hint}} -### Robot efficiency +### Eficiencia del robot {{index "robot efficiency (exercise)"}} -Can you write a robot that finishes the delivery task faster than -`goalOrientedRobot`? If you observe that robot's behavior, what -obviously stupid things does it do? How could those be improved? +Puedes escribir un robot que termine la tarea de entrega más rápido que +`robotOrientadoAMetas`? Si observas el comportamiento de ese robot, qué +obviamente cosas estúpidas este hace? Cómo podrían mejorarse? -If you solved the previous exercise, you might want to use your -`compareRobots` function to verify whether you improved the robot. +Si resolviste el ejercicio anterior, es posible que desees utilizar tu +función `compararRobots` para verificar si has mejorado al robot. {{if interactive ```{test: no} -// Your code here +// Tu código aqui -runRobotAnimation(VillageState.random(), yourRobot, memory); +runRobotAnimation(EstadoPueblo.aleatorio(), tuRobot, memoria); ``` if}} @@ -609,68 +610,65 @@ if}} {{index "robot efficiency (exercise)"}} -The main limitation of `goalOrientedRobot` is that it considers only -one parcel at a time. It will often walk back and forth across the -village because the parcel it happens to be looking at happens to be -at the other side of the map, even if there are others much closer. +La principal limitación de `robotOrientadoAMetas` es que solo considera +un paquete a la vez. A menudo caminará de ida y vuelta por el +pueblo porque el paquete que resulta estar mirando sucede que esta +en el otro lado del mapa, incluso si hay otros mucho más cerca. -One possible solution would be to compute routes for all packages and -then take the shortest one. Even better results can be obtained, if -there are multiple shortest routes, by preferring the ones that go to -pick up a package instead of delivering a package. +Una posible solución sería calcular rutas para todos los paquetes, y +luego tomar la más corta. Se pueden obtener incluso mejores resultados, si +hay múltiples rutas más cortas, al ir prefiriendo las que van a +recoger un paquete en lugar de entregar un paquete. hint}} -### Persistent group +### Conjunto persistente {{index "persistent group (exercise)", "persistent data structure", "Set class", "set (data structure)", "Group class", "PGroup class"}} -Most data structures provided in a standard JavaScript environment -aren't very well suited for persistent use. Arrays have `slice` and -`concat` methods, which allow us to easily create new arrays without -damaging the old one. But `Set`, for example, has no methods for -creating a new set with an item added or removed. - -Write a new class `PGroup`, similar to the `Group` class from [Chapter -?](object#groups), which stores a set of values. Like `Group`, it has -`add`, `delete`, and `has` methods. +La mayoría de las estructuras de datos proporcionadas en un entorno de +JavaScript estándar no son muy adecuadas para usos persistentes. Los arrays +tienen los métodos `slice` y `concat`, que nos permiten fácilmente crear nuevos +arrays sin dañar al anterior. Pero `Set`, por ejemplo, no tiene métodos para +crear un nuevo conjunto con un elemento agregado o eliminado. -Its `add` method, however, should return a _new_ `PGroup` instance -with the given member added and leave the old one unchanged. -Similarly, `delete` creates a new instance without a given member. +Escribe una nueva clase `ConjuntoP`, similar a la clase `Conjunto` del [Capitulo 6](objeto#grupos), que almacena un conjunto de valores. Como `Grupo`, tiene +métodos `añadir`, `eliminar`, y `tiene`. -The class should work for values of any type, not just strings. It -does _not_ have to be efficient when used with large amounts of -values. +Su método `añadir`, sin embargo, debería retornar una _nueva_ instancia de +`ConjuntoP` con el miembro dado agregado, y dejar la instancia anterior sin cambios. +Del mismo modo, `eliminar` crea una nueva instancia sin un miembro dado. -{{index [interface, object]}} +La clase debería funcionar para valores de cualquier tipo, no solo strings. Esta +_no_ tiene que ser eficiente cuando se usa con grandes cantidades de +valores. -The ((constructor)) shouldn't be part of the class's interface -(though you'll definitely want to use it internally). Instead, there -is an empty instance, `PGroup.empty`, that can be used as a starting -value. +El ((constructor)) no deberia ser parte de la interfaz de la clase +(aunque definitivamente querrás usarlo internamente). En cambio, allí +hay una instancia vacía, `ConjuntoP.vacio`, que se puede usar como un valor +de inicio. {{index singleton}} -Why do you need only one `PGroup.empty` value, rather than having a -function that creates a new, empty map every time? +Por qué solo necesitas un valor `ConjuntoP.vacio`, en lugar de tener una +función que crea un nuevo mapa vacío cada vez? {{if interactive ```{test: no} -class PGroup { - // Your code here +class ConjuntoP { + // Tu código aqui } -let a = PGroup.empty.add("a"); -let ab = a.add("b"); -let b = ab.delete("a"); +let a = ConjuntoP.vacio.añadir("a"); +let ab = a.añadir("b"); +let b = ab.eliminar("a"); -console.log(b.has("b")); +console.log(b.tiene("b")); // → true -console.log(a.has("b")); +console.log(a.tiene("b")); // → false -console.log(b.has("a")); +console.log(b.tiene("a")); // → false ``` @@ -678,29 +676,29 @@ if}} {{hint -{{index "persistent map (exercise)", "Set class", [array, creation], "PGroup class"}} +{{index "persistent map (exercise)", "Set class", array, "PGroup class"}} -The most convenient way to represent the set of member values -is still as an array since arrays are easy to copy. +La forma más conveniente de representar el conjunto de valores de miembro +sigue siendo un array, ya que son fáciles de copiar. {{index "concat method", "filter method"}} -When a value is added to the group, you can create a new group with a -copy of the original array that has the value added (for example, using -`concat`). When a value is deleted, you filter it from the array. +Cuando se agrega un valor al grupo, puedes crear un nuevo grupo con una +copia del array original que tiene el valor agregado (por ejemplo, usando +`concat`). Cuando se borra un valor, lo filtra afuera del array. -The class's ((constructor)) can take such an array as argument and -store it as the instance's (only) property. This array is never -updated. +El ((constructor)) de la clase puede tomar un array como argumento, y +almacenarlo como la (única) propiedad de la instancia. Este array nunca es +actualizado. {{index "static method"}} -To add a property (`empty`) to a constructor that is not a method, you -have to add it to the constructor after the class definition, as a -regular property. +Para agregar una propiedad (`vacio`) a un constructor que no sea un método, +tienes que agregarlo al constructor después de la definición de la clase, como +una propiedad regular. -You need only one `empty` instance because all empty groups are the -same and instances of the class don't change. You can create many -different groups from that single empty group without affecting it. +Solo necesita una instancia `vacio` porque todos los conjuntos vacíos son +iguales y las instancias de la clase no cambian. Puedes crear muchos +conjuntos diferentes de ese único conjunto vacío sin afectarlo. hint}} diff --git a/08_error.md b/08_error.md index 6a591ca9f..05211520f 100644 --- a/08_error.md +++ b/08_error.md @@ -1,12 +1,12 @@ {{meta {load_files: ["code/chapter/08_error.js"]}}} -# Bugs and Errors +# Bugs y Errores {{quote {author: "Brian Kernighan and P.J. Plauger", title: "The Elements of Programming Style", chapter: true} -Debugging is twice as hard as writing the code in the first place. -Therefore, if you write the code as cleverly as possible, you are, by -definition, not smart enough to debug it. +Arreglar errores es dos veces mas difícil que escribir el código en primer lugar. +Por lo tanto, si escribes código de la manera más inteligente posible, eres, +por definición, no lo suficientemente inteligente como para depurarlo. quote}} @@ -14,311 +14,309 @@ quote}} {{index "Kernighan, Brian", "Plauger, P.J.", debugging, "error handling"}} -Flaws in computer programs are usually called _((bug))s_. It makes -programmers feel good to imagine them as little things that just -happen to crawl into our work. In reality, of course, we put them -there ourselves. +Los defectos en los programas de computadora usualmente se llaman _((bug))s_ +(o "insectos"). Este nombre hace que los programadores se sientan bien al +imaginarlos como pequeñas cosas que solo sucede se arrastran hacia nuestro trabajo. +En la realidad, por supuesto, nosotros mismos los ponemos allí. -If a program is crystallized thought, you can roughly categorize bugs -into those caused by the thoughts being confused and those caused by -mistakes introduced while converting a thought to code. The former -type is generally harder to diagnose and fix than the latter. +Si un programa es un pensamiento cristalizado, puedes categorizar en grandes +rasgos a los bugs en aquellos causados ​​al confundir los pensamientos, y +los causados ​​por cometer errores al convertir un pensamiento en código. +El primer tipo es generalmente más difícil de diagnosticar y corregir que +el último. -## Language +## Lenguaje {{index parsing, analysis}} -Many mistakes could be pointed out to us automatically by the -computer, if it knew enough about what we're trying to do. But here -JavaScript's looseness is a hindrance. Its concept of bindings and -properties is vague enough that it will rarely catch ((typo))s before -actually running the program. And even then, it allows you to do some -clearly nonsensical things without complaint, such as computing -`true * "monkey"`. +Muchos errores podrían ser señalados automáticamente por la +computadora, si esta supiera lo suficiente sobre lo que estamos tratando de hacer. +Pero aquí la soltura de JavaScript es un obstáculo. Su concepto de vinculaciones y +propiedades es lo suficientemente vago que rara vez atrapará errores +ortograficos antes de ejecutar el programa. E incluso entonces, te permite +hacer algunas cosas claramente sin sentido, como calcular +`true * "mono"`. -{{index [syntax, error], [property, access]}} +{{index syntax}} -There are some things that JavaScript does complain about. Writing a -program that does not follow the language's ((grammar)) will -immediately make the computer complain. Other things, such as calling -something that's not a function or looking up a property on an -((undefined)) value, will cause an error to be reported when the -program tries to perform the action. +Hay algunas cosas de las que JavaScript se queja. Escribir un +programa que no siga la ((gramática)) del lenguaje +inmediatamente hara que la computadora se queje. Otras cosas, como llamar a +algo que no sea una función o buscar una ((propiedad)) en un +valor ((indefinido)), causará un error que sera reportado cuando el +programa intente realizar la acción. {{index NaN, error}} -But often, your nonsense computation will merely produce `NaN` (not a -number) or an undefined value, while the program happily continues, -convinced that it's doing something meaningful. The mistake will -manifest itself only later, after the bogus value has traveled through -several functions. It might not trigger an error at all but silently -cause the program's output to be wrong. Finding the source of such -problems can be difficult. +Pero a menudo, tu cálculo sin sentido simplemente producirá `NaN` (no es un +número) o un valor indefinido. Y el programa continuara felizmente, +convencido de que está haciendo algo significativo. El error solo se +manifestara más tarde, después de que el valor falso haya viajado a traves de +varias funciones. Puede no desencadenar un error en absoluto, pero en silencio +causara que la salida del programa sea incorrecta. Encontrar la fuente de tales +problemas puede ser algo difícil. -The process of finding mistakes—bugs—in programs is called -_((debugging))_. +El proceso de encontrar errores—bugs—en los programas se llama +_((depuración))_. -## Strict mode +## Modo estricto -{{index "strict mode", [syntax, error], function}} +{{index "strict mode", syntax, function}} {{indexsee "use strict", "strict mode"}} -JavaScript can be made a _little_ stricter by enabling _strict -mode_. This is done by putting the string `"use strict"` at the top of -a file or a function body. Here's an example: +JavaScript se puede hacer un _poco_ más estricto al habilitar el _modo estricto_. +Esto se hace al poner el string `"use strict"` ("usar estricto") +en la parte superior de un archivo o cuerpo de función. Aquí hay un ejemplo: -```{test: "error \"ReferenceError: counter is not defined\""} -function canYouSpotTheProblem() { +```{test: "error \"ReferenceError: contador is not defined\""} +function puedesDetectarElProblema() { "use strict"; - for (counter = 0; counter < 10; counter++) { - console.log("Happy happy"); + for (contador = 0; contador < 10; contador++) { + console.log("Feliz feliz"); } } -canYouSpotTheProblem(); -// → ReferenceError: counter is not defined +puedesDetectarElProblema(); +// → ReferenceError: contador is not defined ``` {{index "let keyword", [binding, global]}} -Normally, when you forget to put `let` in front of your binding, as -with `counter` in the example, JavaScript quietly creates a global -binding and uses that. In strict mode, an ((error)) is reported -instead. This is very helpful. It should be noted, though, that this -doesn't work when the binding in question already exists as a global -binding. In that case, the loop will still quietly overwrite the value -of the binding. - -{{index "this binding", "global object", undefined, "strict mode"}} - -Another change in strict mode is that the `this` binding holds the -value `undefined` in functions that are not called as ((method))s. -When making such a call outside of strict mode, `this` refers to the -global scope object, which is an object whose properties are the -global bindings. So if you accidentally call a method or constructor -incorrectly in strict mode, JavaScript will produce an error as soon -as it tries to read something from `this`, rather than happily writing -to the global scope. - -For example, consider the following code, which calls a -((constructor)) function without the `new` keyword so that its `this` -will _not_ refer to a newly constructed object: +Normalmente, cuando te olvidas de poner `let` delante de tu vinculación, como +con `contador` en el ejemplo, JavaScript silenciosamente crea una +vinculación global y utiliza eso. En el modo estricto, se reportara un ((error)) +en su lugar. Esto es muy útil. Sin embargo, debe tenerse en cuenta que esto +no funciona cuando la vinculación en cuestión ya existe como una +vinculación global. En ese caso, el ciclo aún sobrescribirá silenciosamente +el valor de la vinculación. + +{{index this, "global object", undefined, "strict mode"}} + +Otro cambio en el modo estricto es que la vinculación `this` contiene el +valor `undefined` en funciones que no se llamen como ((método))s. +Cuando se hace una llamada fuera del modo estricto, `this` se refiere al +objeto del alcance global, que es un objeto cuyas propiedades son +vinculaciones globales. Entonces, si llamas accidentalmente a un método o +constructor incorrectamente en el modo estricto, JavaScript producirá un error +tan pronto trate de leer algo de `this`, en lugar de escribirlo felizmente +al alcance global. + +Por ejemplo, considera el siguiente código, que llama una función +((constructora)) sin la palabra clave `new` de modo que su `this` +_no_ hara referencia a un objeto recién construido: ``` -function Person(name) { this.name = name; } -let ferdinand = Person("Ferdinand"); // oops -console.log(name); +function Persona(nombre) { this.nombre = nombre; } +let ferdinand = Persona("Ferdinand"); // oops +console.log(nombre); // → Ferdinand ``` {{index error}} -So the bogus call to `Person` succeeded but returned an undefined -value and created the global binding `name`. In strict mode, the -result is different. +Así que la llamada fraudulenta a `Persona` tuvo éxito pero retorno un +valor indefinido y creó la vinculación `nombre` global. En el modo estricto, +el resultado es diferente. -```{test: "error \"TypeError: Cannot set property 'name' of undefined\""} +```{test: "error \"TypeError: Cannot set property 'nombre' of undefined\""} "use strict"; -function Person(name) { this.name = name; } -let ferdinand = Person("Ferdinand"); // forgot new -// → TypeError: Cannot set property 'name' of undefined +function Persona(nombre) { this.nombre = nombre; } +let ferdinand = Persona("Ferdinand"); // olvide new +// → TypeError: Cannot set property 'nombre' of undefined ``` -We are immediately told that something is wrong. This is helpful. +Se nos dice inmediatamente que algo está mal. Esto es útil. -Fortunately, constructors created with the `class` notation will -always complain if they are called without `new`, making this less of -a problem even in non-strict mode. +Afortunadamente, los constructores creados con la notación `class` +siempre se quejan si se llaman sin `new`, lo que hace que esto sea menos +un problema incluso en el modo no-estricto. {{index parameter, [binding, naming], "with statement"}} -Strict mode does a few more things. It disallows giving a function -multiple parameters with the same name and removes certain problematic -language features entirely (such as the `with` statement, which is so -wrong it is not further discussed in this book). +El modo estricto hace algunas cosas más. No permite darle a una función +múltiples parámetros con el mismo nombre y elimina ciertas características +problemáticas del lenguaje por completo (como la declaración `with` ("con"), +la cual esta tan mal, que no se discute en este libro). {{index debugging}} -In short, putting `"use strict"` at the top of your program rarely -hurts and might help you spot a problem. +En resumen, poner `"use strict"` en la parte superior de tu programa rara vez +duele y puede ayudarte a detectar un problema. -## Types +## Tipos -Some languages want to know the types of all your bindings and -expressions before even running a program. They will tell you right -away when a type is used in an inconsistent way. JavaScript considers -types only when actually running the program, and even there often -tries to implicitly convert values to the type it expects, so it's not -much help. +Algunos lenguajes quieren saber los tipos de todas tus vinculaciones y +expresiones incluso antes de ejecutar un programa. Estos te dirán de una vez +cuando uses un tipo de una manera inconsistente. JavaScript solo considera a los +tipos cuando ejecuta el programa, e incluso a menudo intentara convertir +implícitamente los valores al tipo que espera, por lo que no es de +mucha ayuda -Still, types provide a useful framework for talking about programs. A -lot of mistakes come from being confused about the kind of value that -goes into or comes out of a function. If you have that information -written down, you're less likely to get confused. +Aún así, los tipos proporcionan un marco útil para hablar acerca de los +programas. Muchos errores provienen de estar confundido acerca del tipo de +valor que entra o sale de una función. Si tienes esa información +anotada, es menos probable que te confundas. -You could add a comment like the following before the `goalOrientedRobot` -function from the previous chapter to describe its type: +Podrías agregar un comentario como arriba de la función `robotOrientadoAMetas` +del último capítulo, para describir su tipo. ``` -// (VillageState, Array) → {direction: string, memory: Array} -function goalOrientedRobot(state, memory) { +// (EstadoMundo, Array) → {direccion: string, memoria: Array} +function robotOrientadoAMetas(estado, memoria) { // ... } ``` -There are a number of different conventions for annotating JavaScript -programs with types. +Hay varias convenciones diferentes para anotar programas de JavaScript con tipos. -One thing about types is that they need to introduce their own -complexity to be able to describe enough code to be useful. What do -you think would be the type of the `randomPick` function that returns -a random element from an array? You'd need to introduce a _((type -variable))_, _T_, which can stand in for any type, so that you can -give `randomPick` a type like `([T]) → T` (function from an array of -*T*s to a *T*). +Una cosa acerca de los tipos es que necesitan introducir su propia +complejidad para poder describir suficiente código como para poder ser útil. +Cual crees que sería el tipo de la función `eleccionAleatoria` que retorna +un elemento aleatorio de un array? Deberías introducir un _((tipo +variable))_, _T_, que puede representar cualquier tipo, para que puedas +darle a `eleccionAleatoria` un tipo como `([T]) → T` (función de un array de +*T*s a a *T*). {{index "type checking", TypeScript}} {{id typing}} -When the types of a program are known, it is possible for the computer -to _check_ them for you, pointing out mistakes before the program is -run. There are several JavaScript dialects that add types to the -language and check them. The most popular one is called -[TypeScript](https://www.typescriptlang.org/). If you are interested -in adding more rigor to your programs, I recommend you give it a try. +Cuando se conocen los tipos de un programa, es posible que la computadora +haga un _chequeo_ por ti, señalando los errores antes de que el programa sea +ejecutado. Hay varios dialectos de JavaScript que agregan tipos al +lenguaje y y los verifica. El más popular se llama +[TypeScript](https://www.typescriptlang.org/). Si estás interesado +en agregarle más rigor a tus programas, te recomiendo que lo pruebes. -In this book, we'll continue using raw, dangerous, untyped JavaScript -code. +En este libro, continuaremos usando código en JavaScript crudo, peligroso y +sin tipos. -## Testing +## Probando {{index "test suite", "run-time error", automation, testing}} -If the language is not going to do much to help us find mistakes, -we'll have to find them the hard way: by running the program and -seeing whether it does the right thing. +Si el lenguaje no va a hacer mucho para ayudarnos a encontrar errores, +tendremos que encontrarlos de la manera difícil: ejecutando el programa y +viendo si hace lo correcto. -Doing this by hand, again and again, is a really bad idea. Not only is -it annoying, it also tends to be ineffective since it takes too much -time to exhaustively test everything every time you make a change. +Hacer esto a mano, una y otra vez, es una muy mala idea. No solo es +es molesto, también tiende a ser ineficaz, ya que lleva demasiado +tiempo probar exhaustivamente todo cada vez que haces un cambio en tu programa. -Computers are good at repetitive tasks, and testing is the ideal -repetitive task. Automated testing is the process of writing a program -that tests another program. Writing tests is a bit more work than -testing manually, but once you've done it, you gain a kind of -superpower: it takes you only a few seconds to verify that your -program still behaves properly in all the situations you wrote tests -for. When you break something, you'll immediately notice, rather than -randomly running into it at some later time. +Las computadoras son buenas para las tareas repetitivas, y las pruebas son las +tareas repetitivas ideales. Las pruebas automatizadas es el proceso de escribir un +programa que prueba otro programa. Escribir pruebas consiste en algo más de +trabajo que probar manualmente, pero una vez que lo haz hecho, ganas un tipo de +superpoder: solo te tomara unos segundos verificar que tu programa todavía +se comporta correctamente en todas las situaciones para las que +escribiste tu prueba. Cuando rompas algo, lo notarás inmediatamente, en +lugar aleatoriomente encontrarte con el problema en algún momento posterior. {{index "toUpperCase method"}} -Tests usually take the form of little labeled programs that verify -some aspect of your code. For example, a set of tests for the -(standard, probably already tested by someone else) `toUpperCase` -method might look like this: +Las pruebas usualmente toman la forma de pequeños programas etiquetados que +verifican algún aspecto de tu código. Por ejemplo, un conjunto de pruebas +para el método (estándar, probablemente ya probado por otra persona) +`toUpperCase` podría verse así: ``` -function test(label, body) { - if (!body()) console.log(`Failed: ${label}`); +function probar(etiqueta, cuerpo) { + if (!cuerpo()) console.log(`Fallo: ${etiqueta}`); } -test("convert Latin text to uppercase", () => { - return "hello".toUpperCase() == "HELLO"; +probar("convertir texto Latino a mayúscula", () => { + return "hola".toUpperCase() == "HOLA"; }); -test("convert Greek text to uppercase", () => { +probar("convertir texto Griego a mayúsculas", () => { return "Χαίρετε".toUpperCase() == "ΧΑΊΡΕΤΕ"; }); -test("don't convert case-less characters", () => { +probar("no convierte caracteres sin mayúsculas", () => { return "مرحبا".toUpperCase() == "مرحبا"; }); ``` {{index "domain-specific language"}} -Writing tests like this tends to produce rather repetitive, awkward -code. Fortunately, there exist pieces of software that help you build -and run collections of tests (_((test suites))_) by providing a -language (in the form of functions and methods) suited to expressing -tests and by outputting informative information when a test fails. -These are usually called _((test runners))_. +Escribir pruebas de esta manera tiende a producir código bastante repetitivo e +incómodo. Afortunadamente, existen piezas de software que te ayudan a construir +y ejecutar colecciones de pruebas (_((suites de prueba))_) al proporcionar un +lenguaje (en forma de funciones y métodos) adecuado para expresar +pruebas y obtener información informativa cuando una prueba falla. +Estos generalmente se llaman _((corredores de pruebas))_. {{index "persistent data structure"}} -Some code is easier to test than other code. Generally, the more -external objects that the code interacts with, the harder it is to set -up the context in which to test it. The style of programming shown in -the [previous chapter](robot), which uses self-contained persistent -values rather than changing objects, tends to be easy to test. +Algunos programas son más fáciles de probar que otros programas. Por lo general, +con cuantos más objetos externos interactúe el código, más difícil es establecer +el contexto en el cual probarlo. El estilo de programación mostrado en +el [capítulo anterior](robot), que usa valores persistentes auto-contenidos +en lugar de cambiar objetos, tiende a ser fácil de probar. -## Debugging +## Depuración {{index debugging}} -Once you notice there is something wrong with your program -because it misbehaves or produces errors, the next step is to figure -out _what_ the problem is. +Una vez que notes que hay algo mal con tu programa porque se comporta mal o +produce errores, el siguiente paso es descubir _cual_ es el problema. -Sometimes it is obvious. The ((error)) message will point at a -specific line of your program, and if you look at the error -description and that line of code, you can often see the problem. +A veces es obvio. El mensaje de ((error)) apuntará a una +línea específica de tu programa, y ​​si miras la descripción del error +y esa línea de código, a menudo puedes ver el problema. {{index "run-time error"}} -But not always. Sometimes the line that triggered the problem is -simply the first place where a flaky value produced elsewhere gets -used in an invalid way. If you have been solving the ((exercises)) in -earlier chapters, you will probably have already experienced such -situations. +Pero no siempre. A veces, la línea que provocó el problema es simplemente +el primer lugar en donde un valor extraño producido en otro lugar es +usado de una manera inválida. Si has estado resolviendo los ((ejercicios)) en +capítulos anteriores, probablemente ya habrás experimentado tales +situaciones. {{index "decimal number", "binary number"}} -The following example program tries to convert a whole number to a -string in a given base (decimal, binary, and so on) by repeatedly -picking out the last ((digit)) and then dividing the number to get rid -of this digit. But the strange output that it currently produces -suggests that it has a ((bug)). +El siguiente programa de ejemplo intenta convertir un número entero a un +string en una base dada (decimal, binario, etc.) al repetidamente +seleccionar el último ((dígito)) y luego dividiendo el número para deshacerse +de este dígito. Pero la extraña salida que produce sugiere que tiene un ((error)). ``` -function numberToString(n, base = 10) { - let result = "", sign = ""; +function numeroAString(n, base = 10) { + let resultado = "", signo = ""; if (n < 0) { - sign = "-"; + signo = "-"; n = -n; } do { - result = String(n % base) + result; + resultado = String(n % base) + resultado; n /= base; } while (n > 0); - return sign + result; + return signo + resultado; } -console.log(numberToString(13, 10)); +console.log(numeroAString(13, 10)); // → 1.5e-3231.3e-3221.3e-3211.3e-3201.3e-3191.3e-3181.3… ``` {{index analysis}} -Even if you see the problem already, pretend for a moment that you -don't. We know that our program is malfunctioning, and we want to find -out why. +Incluso si ya ves el problema, finge por un momento que no lo has hecho. +Sabemos que nuestro programa no funciona bien, y queremos encontrar +por qué. {{index "trial and error"}} -This is where you must resist the urge to start making random changes -to the code to see whether that makes it better. Instead, _think_. Analyze -what is happening and come up with a ((theory)) of why it might be -happening. Then, make additional observations to test this theory—or, -if you don't yet have a theory, make additional observations to help -you come up with one. +Aquí es donde debes resistir el impulso de comenzar a hacer cambios aleatorios +en el código para ver si eso lo mejora. En cambio, _piensa_. Analiza +lo que está sucediendo y piensa en una ((teoría)) de por qué podría ser +sucediendo. Luego, haz observaciones adicionales para probar esta teoría—o +si aún no tienes una teoría, haz observaciones adicionales para ayudarte +a que se te ocurra una. {{index "console.log", output, debugging, logging}} -Putting a few strategic `console.log` calls into the program is a good -way to get additional information about what the program is doing. In -this case, we want `n` to take the values `13`, `1`, and then `0`. -Let's write out its value at the start of the loop. +Poner algunas llamadas estratégicas a `console.log` en el programa es una buena +forma de obtener información adicional sobre lo que está haciendo el programa. +En en este caso, queremos que `n` tome los valores `13`, `1` y luego `0`. +Vamos a escribir su valor al comienzo del ciclo. ```{lang: null} 13 @@ -331,422 +329,428 @@ Let's write out its value at the start of the loop. {{index rounding}} -_Right_. Dividing 13 by 10 does not produce a whole number. Instead of -`n /= base`, what we actually want is `n = Math.floor(n / base)` so -that the number is properly "shifted" to the right. +_Exacto_. Dividir 13 entre 10 no produce un número entero. En lugar de +`n /= base`, lo que realmente queremos es `n = Math.floor(n / base)` para +que el número sea correctamente "desplazado" hacia la derecha. {{index "JavaScript console", "debugger statement"}} -An alternative to using `console.log` to peek into the program's -behavior is to use the _debugger_ capabilities of your browser. -Browsers come with the ability to set a _((breakpoint))_ on a specific -line of your code. When the execution of the program reaches a line -with a breakpoint, it is paused, and you can inspect the values of -bindings at that point. I won't go into details, as debuggers differ -from browser to browser, but look in your browser's ((developer -tools)) or search the Web for more information. +Una alternativa al uso de `console.log` para echarle un vistazo al comportamiento +del programa es usar las capacidades del _depurador_ de tu navegador. +Los navegadores vienen con la capacidad de establecer un _((punto de interrupción))_ +en una línea específico de tu código. Cuando la ejecución del programa alcanza +una línea con un punto de interrupción, este entra en pausa, y puedes +inspeccionar los valores de las vinculaciones en ese punto. +No entraré en detalles, ya que los depuradores difieren +de navegador en navegador, pero mira las ((herramientas de +desarrollador)) en tu navegador o busca en la Web para obtener más información. -Another way to set a breakpoint is to include a `debugger` statement -(consisting of simply that keyword) in your program. If the -((developer tools)) of your browser are active, the program will pause -whenever it reaches such a statement. +Otra forma de establecer un punto de interrupción es incluir una declaración +`debugger` (que consiste simplemente de esa palabra clave) en tu programa. Si las +((herramientas de desarrollador)) en tu navegador están activas, el programa pausará +cada vez que llegue a tal declaración. -## Error propagation +## Propagación de errores {{index input, output, "run-time error", error, validation}} -Not all problems can be prevented by the programmer, unfortunately. If -your program communicates with the outside world in any way, it is -possible to get malformed input, to become overloaded with work, or to -have the network fail. +Desafortunadamente, no todos los problemas pueden ser prevenidos por el +programador. Si tu programa se comunica con el mundo exterior de alguna manera, +es posible obtener una entrada malformada, sobrecargarse con el trabajo, o +la red falle en la ejecución. {{index "error recovery"}} -If you're programming only for yourself, you can afford to just ignore -such problems until they occur. But if you build something that is -going to be used by anybody else, you usually want the program to do -better than just crash. Sometimes the right thing to do is take the -bad input in stride and continue running. In other cases, it is better -to report to the user what went wrong and then give up. But in either -situation, the program has to actively do something in response to the -problem. +Si solo estás programando para ti mismo, puedes permitirte ignorar tales +problemas hasta que estos ocurran. Pero si construyes algo que +va a ser utilizado por cualquier otra persona, generalmente quieres que +el programa haga algo mejor que solo estrellarse. A veces lo correcto es tomar la +mala entrada en zancada y continuar corriendo. En otros casos, es mejor +informar al usuario lo que salió mal y luego darse por vencido. Pero en +cualquier situación, el programa tiene que hacer algo activamente en +respuesta al problema. -{{index "promptNumber function", validation}} +{{index "promptInteger function", validation}} -Say you have a function `promptNumber` that asks the user for a number -and returns it. What should it return if the user inputs "orange"? +Supongamos que tienes una función `pedirEntero` que le pide al usuario un +número entero y lo retorna. Qué deberías retornar si la entrada por +parte del usuario es "naranja"? {{index null, undefined, "return value", "special return value"}} -One option is to make it return a special value. Common choices for -such values are `null`, `undefined`, or -1. +Una opción es hacer que retorne un valor especial. Opciones comunes para +tales valores son `null`, `undefined`, o -1. ```{test: no} -function promptNumber(question) { - let result = Number(prompt(question)); - if (Number.isNaN(result)) return null; - else return result; +function pedirEntero(pregunta) { + let resultado = Number(prompt(pregunta)); + if (Number.isNaN(resultado)) return null; + else return resultado; } -console.log(promptNumber("How many trees do you see?")); +console.log(pedirEntero("Cuantos arboles ves?")); ``` -Now any code that calls `promptNumber` must check whether an actual -number was read and, failing that, must somehow recover—maybe by -asking again or by filling in a default value. Or it could again -return a special value to _its_ caller to indicate that it failed to -do what it was asked. +Ahora cualquier código que llame a `pedirEntero` debe verificar si un número real +fue leído y, si eso falla, de alguna manera debe recuperarse—tal vez +preguntando nuevamente o usando un valor predeterminado. O podría de nuevo +retornar un valor especial a _su_ llamada para indicar que no pudo +hacer lo que se pidió. {{index "error handling"}} -In many situations, mostly when ((error))s are common and the caller -should be explicitly taking them into account, returning a special -value is a good way to indicate an error. It does, however, have its -downsides. First, what if the function can already return every -possible kind of value? In such a function, you'll have to do -something like wrap the result in an object to be able to distinguish -success from failure. +En muchas situaciones, principalmente cuando los ((error))es son comunes +y la persona que llama debe tenerlos explícitamente en cuenta, retornar un +valor especial es una buena forma de indicar un error. Sin embargo, esto tiene +sus desventajas. Primero, qué pasa si la función puede retornar cada +tipo de valor posible? En tal función, tendrás que hacer +algo como envolver el resultado en un objeto para poder distinguir el +éxito del fracaso. ``` -function lastElement(array) { +function ultimoElemento(array) { if (array.length == 0) { - return {failed: true}; + return {fallo: true}; } else { - return {element: array[array.length - 1]}; + return {elemento: array[array.length - 1]}; } } ``` {{index "special return value", readability}} -The second issue with returning special values is that it can lead to -awkward code. If a piece of code calls `promptNumber` 10 times, -it has to check 10 times whether `null` was returned. And if its -response to finding `null` is to simply return `null` itself, callers -of the function will in turn have to check for it, and so on. +El segundo problema con retornar valores especiales es que puede conducir a +código muy incómodo. Si un fragmento de código llama a `pedirEntero` 10 veces, +tiene que comprobar 10 veces si `null` fue retornado. Y si su +respuesta a encontrar `null` es simplemente retornar `null` en sí mismo, +los llamadores de esa función a su vez tendrán que verificarlo, y +así sucesivamente. -## Exceptions +## Excepciones {{index "error handling"}} -When a function cannot proceed normally, what we would _like_ to do is -just stop what we are doing and immediately jump to a place that knows -how to handle the problem. This is what _((exception handling))_ does. +Cuando una función no puede continuar normalmente, lo que nos _gustaría_ hacer es +simplemente detener lo que estamos haciendo e inmediatamente saltar a un +lugar que sepa cómo manejar el problema. Esto es lo que el +_((manejo de excepciones))_ hace. -{{index ["control flow", exceptions], "raising (exception)", "throw keyword", "call stack"}} +{{index "control flow", "raising (exception)", "throw keyword", "call stack"}} -Exceptions are a mechanism that makes it possible for code that runs -into a problem to _raise_ (or _throw_) an exception. An exception can -be any value. Raising one somewhat resembles a super-charged return -from a function: it jumps out of not just the current function but -also its callers, all the way down to the first call that -started the current execution. This is called _((unwinding the -stack))_. You may remember the stack of function calls that was -mentioned in [Chapter ?](functions#stack). An exception zooms down -this stack, throwing away all the call contexts it encounters. +Las excepciones son un mecanismo que hace posible que el código que se encuentre +con un problema _produzca_ (o _lance_) una excepción. Una excepción puede +ser cualquier valor. Producir una se asemeja a un retorno súper-cargado +de una función: salta no solo de la función actual sino +también fuera de sus llamadores, todo el camino hasta la primera llamada que +comenzó la ejecución actual. Esto se llama _((desenrollando la +pila))_. Puede que recuerdes que la pila de llamadas de función fue +mencionada en el [Capítulo 3](funciones#pila). Una excepción se aleja de +esta pila, descartando todos los contextos de llamadas que encuentra. -{{index "error handling", [syntax, statement], "catch keyword"}} +{{index "error handling", syntax, "catch keyword"}} -If exceptions always zoomed right down to the bottom of the stack, -they would not be of much use. They'd just provide a novel way to blow -up your program. Their power lies in the fact that you can set -"obstacles" along the stack to _catch_ the exception as it is zooming -down. Once you've caught an exception, you can do something with it to -address the problem and then continue to run the program. +Si las excepciones siempre se acercaran al final de la pila, estas +no serían de mucha utilidad. Simplemente proporcionarían una nueva forma de +explotar tu programa. Su poder reside en el hecho de que puedes establecer +"obstáculos" a lo largo de la pila para _capturar_ la excepción, cuando esta +esta se dirige hacia abajo. Una vez que hayas capturado una excepción, +puedes hacer algo con ella para abordar el problema y +luego continuar ejecutando el programa. -Here's an example: +Aquí hay un ejemplo: {{id look}} ``` -function promptDirection(question) { - let result = prompt(question); - if (result.toLowerCase() == "left") return "L"; - if (result.toLowerCase() == "right") return "R"; - throw new Error("Invalid direction: " + result); +function pedirDireccion(pregunta) { + let resultado = prompt(pregunta); + if (resultado.toLowerCase() == "izquierda") return "I"; + if (resultado.toLowerCase() == "derecha") return "D"; + throw new Error("Dirección invalida: " + resultado); } -function look() { - if (promptDirection("Which way?") == "L") { - return "a house"; +function mirar() { + if (pedirDireccion("Hacia que dirección quieres ir?") == "I") { + return "una casa"; } else { - return "two angry bears"; + return "dos osos furiosos"; } } try { - console.log("You see", look()); + console.log("Tu ves", mirar()); } catch (error) { - console.log("Something went wrong: " + error); + console.log("Algo incorrecto sucedio: " + error); } ``` {{index "exception handling", block, "throw keyword", "try keyword", "catch keyword"}} -The `throw` keyword is used to raise an exception. Catching one is -done by wrapping a piece of code in a `try` block, followed by the -keyword `catch`. When the code in the `try` block causes an exception -to be raised, the `catch` block is evaluated, with the name in -parentheses bound to the exception value. After the `catch` block -finishes—or if the `try` block finishes without problems—the program -proceeds beneath the entire `try/catch` statement. +La palabra clave `throw` ("producir") se usa para generar una excepción. +La captura de una se hace al envolver un fragmento de código en un bloque +`try` ("intentar"), seguido de la palabra clave `catch` ("atrapar"). +Cuando el código en el bloque `try` cause una excepción +para ser producida, se evalúa el bloque `catch`, con el nombre en +paréntesis vinculado al valor de la excepción. Después de que el bloque `catch` +finaliza, o si el bloque `try` finaliza sin problemas, el programa +procede debajo de toda la declaración `try/catch`. {{index debugging, "call stack", "Error type"}} -In this case, we used the `Error` ((constructor)) to create our -exception value. This is a ((standard)) JavaScript constructor that -creates an object with a `message` property. In most JavaScript -environments, instances of this constructor also gather information -about the call stack that existed when the exception was created, a -so-called _((stack trace))_. This information is stored in the `stack` -property and can be helpful when trying to debug a problem: it tells -us the function where the problem occurred and which functions made -the failing call. +En este caso, usamos el ((constructor)) `Error` para crear nuestro +valor de excepción. Este es un constructor (estándar) de JavaScript que +crea un objeto con una propiedad `message` ("mensaje"). En la mayoría de los +entornos de JavaScript, las instancias de este constructor también recopilan información +sobre la pila de llamadas que existía cuando se creó la excepción, algo +llamado _((seguimiento de la pila))_. Esta información se almacena en la +propiedad `stack` ("pila") y puede ser útil al intentar depurar un problema: +esta nos dice la función donde ocurrió el problema y qué funciones realizaron +la llamada fallida. {{index "exception handling"}} -Note that the `look` function completely ignores the possibility that -`promptDirection` might go wrong. This is the big advantage of -exceptions: error-handling code is necessary only at the point where -the error occurs and at the point where it is handled. The functions -in between can forget all about it. +Ten en cuenta que la función `mirar` ignora por completo la posibilidad de que +`pedirDireccion` podría salir mal. Esta es la gran ventaja de las +excepciones: el código de manejo de errores es necesario solamente +en el punto donde el error ocurre y en el punto donde se maneja. Las funciones +en el medio puede olvidarse de todo. -Well, almost... +Bueno, casi... -## Cleaning up after exceptions +## Limpiando después de excepciones -{{index "exception handling", "cleaning up", ["control flow", exceptions]}} +{{index "exception handling", "cleaning up"}} -The effect of an exception is another kind of control flow. Every -action that might cause an exception, which is pretty much every -function call and property access, might cause control to suddenly -leave your code. +El efecto de una excepción es otro tipo de ((flujo de control)). Cada +acción que podría causar una excepción, que es prácticamente cualquier +llamada de función y acceso a propiedades, puede causar al control +dejar tu codigo repentinamente. -This means when code has several side effects, even if its -"regular" control flow looks like they'll always all happen, an -exception might prevent some of them from taking place. +Eso significa que cuando el código tiene varios efectos secundarios, +incluso si parece que el flujo de control "regular" siempre sucederá, una +excepción puede evitar que algunos de ellos sucedan. {{index "banking example"}} -Here is some really bad banking code. +Aquí hay un código bancario realmente malo. ```{includeCode: true} -const accounts = { +const cuentas = { a: 100, b: 0, c: 20 }; -function getAccount() { - let accountName = prompt("Enter an account name"); - if (!accounts.hasOwnProperty(accountName)) { - throw new Error(`No such account: ${accountName}`); +function obtenerCuenta() { + let nombreCuenta = prompt("Ingrese el nombre de la cuenta"); + if (!cuentas.hasOwnProperty(nombreCuenta)) { + throw new Error(`La cuenta "${nombreCuenta}" no existe`); } - return accountName; + return nombreCuenta; } -function transfer(from, amount) { - if (accounts[from] < amount) return; - accounts[from] -= amount; - accounts[getAccount()] += amount; +function transferir(desde, cantidad) { + if (cuentas[desde] < cantidad) return; + cuentas[desde] -= cantidad; + cuentas[obtenerCuenta()] += cantidad; } ``` -The `transfer` function transfers a sum of money from a given account -to another, asking for the name of the other account in the process. -If given an invalid account name, `getAccount` throws an exception. +La función `transferir` transfiere una suma de dinero desde una determinada +cuenta a otra, pidiendo el nombre de la otra cuenta en el proceso. +Si se le da un nombre de cuenta no válido, `obtenerCuenta` arroja una excepción. -But `transfer` _first_ removes the money from the account and _then_ -calls `getAccount` before it adds it to another account. If it is -broken off by an exception at that point, it'll just make the money -disappear. +Pero `transferir` _primero_ remueve el dinero de la cuenta, y _luego_ +llama a `obtenerCuenta` antes de añadirlo a la otra cuenta. Si esto es +interrumpido por una excepción en ese momento, solo hará que el dinero +desaparezca. -That code could have been written a little more intelligently, for -example by calling `getAccount` before it starts moving money around. -But often problems like this occur in more subtle ways. Even functions -that don't look like they will throw an exception might do so in -exceptional circumstances or when they contain a programmer mistake. +Ese código podría haber sido escrito de una manera un poco más inteligente, por +ejemplo al llamar `obtenerCuenta` antes de que se comience a mover el dinero. +Pero a menudo problemas como este ocurren de maneras más sutiles. Incluso +funciones que no parece que lanzarán una excepción podría hacerlo en +circunstancias excepcionales o cuando contienen un error de programador. -One way to address this is to use fewer side effects. Again, a -programming style that computes new values instead of changing -existing data helps. If a piece of code stops running in the middle of -creating a new value, no one ever sees the half-finished value, and -there is no problem. +Una forma de abordar esto es usar menos efectos secundarios. De nuevo, un +estilo de programación que calcula nuevos valores en lugar de cambiar +los datos existentes ayuda. Si un fragmento de código deja de ejecutarse en +el medio de crear un nuevo valor, nadie ve el valor a medio terminar, y +no hay ningún problema. {{index block, "try keyword", "finally keyword"}} -But that isn't always practical. So there is another feature that -`try` statements have. They may be followed by a `finally` block -either instead of or in addition to a `catch` block. A `finally` block -says "no matter _what_ happens, run this code after trying to run the -code in the `try` block." +Pero eso no siempre es práctico. Entonces, hay otra característica que las +declaraciones `try` tienen. Estas pueden ser seguidas por un bloque `finally` ("finalmente") +en lugar de o además de un bloque `catch`. Un bloque `finally` +dice "no importa lo que pase, ejecuta este código después de intentar +ejecutar el código en el bloque `try`." ```{includeCode: true} -function transfer(from, amount) { - if (accounts[from] < amount) return; - let progress = 0; +function transferir(desde, cantidad) { + if (cuentas[desde] < cantidad) return; + let progreso = 0; try { - accounts[from] -= amount; - progress = 1; - accounts[getAccount()] += amount; - progress = 2; + cuentas[desde] -= cantidad; + progreso = 1; + cuentas[obtenerCuenta()] += cantidad; + progreso = 2; } finally { - if (progress == 1) { - accounts[from] += amount; + if (progreso == 1) { + cuentas[desde] += cantidad; } } } ``` -This version of the function tracks its progress, and if, when -leaving, it notices that it was aborted at a point where it had -created an inconsistent program state, it repairs the damage it did. +Esta versión de la función rastrea su progreso, y si, cuando este +terminando, se da cuenta de que fue abortada en un punto donde habia +creado un estado de programa inconsistente, repara el daño que hizo. -Note that even though the `finally` code is run when an exception -is thrown in the `try` block, it does not interfere with the exception. -After the `finally` block runs, the stack continues unwinding. +Ten en cuenta que, aunque el código `finally` se ejecuta cuando una excepción +deja el bloque `try`, no interfiere con la excepción. +Después de que se ejecuta el bloque `finally`, la pila continúa desenrollandose. {{index "exception safety"}} -Writing programs that operate reliably even when exceptions pop up in -unexpected places is hard. Many people simply don't bother, and -because exceptions are typically reserved for exceptional -circumstances, the problem may occur so rarely that it is never even -noticed. Whether that is a good thing or a really bad thing depends on -how much damage the software will do when it fails. +Escribir programas que funcionan de manera confiable incluso cuando aparecen +excepciones en lugares inesperados es muy difícil. Muchas personas simplemente +no se molestan, y porque las excepciones suelen reservarse para circunstancias +excepcionales, el problema puede ocurrir tan raramente que nunca siquiera es +notado. Si eso es algo bueno o algo realmente malo depende de +cuánto daño hará el software cuando falle. -## Selective catching +## Captura selectiva {{index "uncaught exception", "exception handling", "JavaScript console", "developer tools", "call stack", error}} -When an exception makes it all the way to the bottom of the stack -without being caught, it gets handled by the environment. What this -means differs between environments. In browsers, a description of the -error typically gets written to the JavaScript console (reachable -through the browser's Tools or Developer menu). Node.js, the -browserless JavaScript environment we will discuss in [Chapter -?](node), is more careful about data corruption. It aborts the whole -process when an unhandled exception occurs. +Cuando una excepción llega hasta el final de la pila +sin ser capturada, esta es manejada por el entorno. Lo que esto +significa difiere entre los entornos. En los navegadores, una descripción del +error generalmente sera escrita en la consola de JavaScript (accesible +a través de las herramientas de desarrollador del navegador). Node.js, el +entorno de JavaScript sin navegador que discutiremos en el [Capítulo 20](node), +es más cuidadoso con la corrupción de datos. Aborta todo el +proceso cuando ocurre una excepción no manejada. {{index crash, "error handling"}} -For programmer mistakes, just letting the error go through is often -the best you can do. An unhandled exception is a reasonable way to -signal a broken program, and the JavaScript console will, on modern -browsers, provide you with some information about which function calls -were on the stack when the problem occurred. +Para los errores de programador, solo dejar pasar al error es a menudo +lo mejor que puedes hacer. Una excepción no manejada es una forma razonable de +señalizar un programa roto, y la consola de JavaScript, en los +navegadores moderno, te proporcionan cierta información acerca de qué +llamdas de función estaban en la pila cuando ocurrió el problema. {{index "user interface"}} -For problems that are _expected_ to happen during routine use, -crashing with an unhandled exception is a terrible strategy. +Para problemas que se _espera_ que sucedan durante el uso rutinario, +estrellarse con una excepción no manejada es una estrategia terrible. -{{index [function, application], "exception handling", "Error type", [binding, undefined]}} +{{index syntax, [function, application], "exception handling", "Error type"}} -Invalid uses of the language, such as referencing a nonexistent -binding, looking up a property on `null`, or calling something -that's not a function, will also result in exceptions being raised. -Such exceptions can also be caught. +Usos inválidos del lenguaje, como hacer referencia a ((vinculaciones)) +inexistentes, buscar una propiedad en `null`, o llamar a algo +que no sea una función, también dará como resultado que se levanten excepciones. +Tales excepciones también pueden ser atrapadas. {{index "catch keyword"}} -When a `catch` body is entered, all we know is that _something_ in our -`try` body caused an exception. But we don't know _what_ did or _which_ -exception it caused. +Cuando se ingresa en un cuerpo `catch`, todo lo que sabemos es que _algo_ en +nuestro cuerpo `try` provocó una excepción. Pero no sabemos _que_, o _cual_ +excepción este causó. {{index "exception handling"}} -JavaScript (in a rather glaring omission) doesn't provide direct -support for selectively catching exceptions: either you catch them all -or you don't catch any. This makes it tempting to _assume_ that the -exception you get is the one you were thinking about when you wrote -the `catch` block. +JavaScript (en una omisión bastante evidente) no proporciona soporte directo +para la captura selectiva de excepciones: o las atrapas todas +o no atrapas nada. Esto hace que sea tentador _asumir_ que la +excepción que obtienes es en la que estabas pensando cuando escribiste +el bloque `catch`. {{index "promptDirection function"}} -But it might not be. Some other ((assumption)) might be violated, or -you might have introduced a bug that is causing an exception. Here is -an example that _attempts_ to keep on calling `promptDirection` -until it gets a valid answer: +Pero puede que no. Alguna otra ((suposición)) podría ser violada, o +es posible que hayas introducido un error que está causando una excepción. +Aquí está un ejemplo que _intenta_ seguir llamando `pedirDireccion` +hasta que obtenga una respuesta válida: ```{test: no} for (;;) { try { - let dir = promtDirection("Where?"); // ← typo! - console.log("You chose ", dir); + let direccion = peirDirrecion("Donde?"); // ← error tipografico! + console.log("Tu elegiste ", direccion); break; } catch (e) { - console.log("Not a valid direction. Try again."); + console.log ("No es una dirección válida. Inténtalo de nuevo"); } } ``` {{index "infinite loop", "for loop", "catch keyword", debugging}} -The `for (;;)` construct is a way to intentionally create a loop that -doesn't terminate on its own. We break out of the loop only when a -valid direction is given. _But_ we misspelled `promptDirection`, which -will result in an "undefined variable" error. Because the `catch` -block completely ignores its exception value (`e`), assuming it knows -what the problem is, it wrongly treats the binding error as indicating -bad input. Not only does this cause an infinite loop, it -"buries" the useful error message about the misspelled binding. +El constructo `for (;;)` es una forma de crear intencionalmente un ciclo que +no termine por si mismo. Salimos del ciclo solamente una cuando +dirección válida sea dada. _Pero_ escribimos mal `pedirDireccion`, lo que +dará como resultado un error de "variable indefinida". Ya que el bloque +`catch` ignora por completo su valor de excepción (`e`), suponiendo que sabe +cuál es el problema, trata erróneamente al error de vinculación como indicador +de una mala entrada. Esto no solo causa un ciclo infinito, también +"entierra" el útil mensaje de error acerca de la vinculación mal escrita. -As a general rule, don't blanket-catch exceptions unless it is for the -purpose of "routing" them somewhere—for example, over the network to -tell another system that our program crashed. And even then, think -carefully about how you might be hiding information. +Como regla general, no incluyas excepciones a menos de que sean con el +propósito de "enrutarlas" hacia alguna parte—por ejemplo, a través de la red +para decirle a otro sistema que nuestro programa se bloqueó. E incluso entonces, +piensa cuidadosamente sobre cómo podrias estar ocultando información. {{index "exception handling"}} -So we want to catch a _specific_ kind of exception. We can do this by -checking in the `catch` block whether the exception we got is the one -we are interested in and rethrowing it otherwise. But how do we -recognize an exception? +Por lo tanto, queremos detectar un tipo de excepción _específico_. +Podemos hacer esto al revisar el bloque `catch` si la excepción que +tenemos es en la que estamos interesados ​​y relanzar de otra manera. Pero como +hacemos para reconocer una excepción? -We could compare its `message` property against the ((error)) message -we happen to expect. But that's a shaky way to write code—we'd be -using information that's intended for human consumption (the message) -to make a programmatic decision. As soon as someone changes (or -translates) the message, the code will stop working. +Podríamos comparar su propiedad `message` con el mensaje de ((error)) que +sucede estamos esperando. Pero esa es una forma inestable de escribir +código—estariamos utilizando información destinada al consumo humano (el mensaje) +para tomar una decisión programática. Tan pronto como alguien cambie (o +traduzca) el mensaje, el código dejaria de funcionar. {{index "Error type", "instanceof operator", "promptDirection function"}} -Rather, let's define a new type of error and use `instanceof` to -identify it. +En vez de esto, definamos un nuevo tipo de error y usemos `instanceof` para +identificarlo. ```{includeCode: true} -class InputError extends Error {} +class ErrorDeEntrada extends Error {} -function promptDirection(question) { - let result = prompt(question); - if (result.toLowerCase() == "left") return "L"; - if (result.toLowerCase() == "right") return "R"; - throw new InputError("Invalid direction: " + result); +function pedirDireccion(pregunta) { + let resultado = prompt(pregunta); + if (resultado.toLowerCase() == "izquierda") return "I"; + if (resultado.toLowerCase() == "derecha") return "D"; + throw new ErrorDeEntrada("Direccion invalida: " + resultado); } ``` {{index "throw keyword", inheritance}} -The new error class extends `Error`. It doesn't define its own -constructor, which means that it inherits the `Error` constructor, -which expects a string message as argument. In fact, it doesn't define -anything at all—the class is empty. `InputError` objects behave like -`Error` objects, except that they have a different class by which we -can recognize them. +La nueva clase de error extiende `Error`. No define su propio +constructor, lo que significa que hereda el constructor `Error`, +que espera un mensaje de string como argumento. De hecho, no define +nada—la clase está vacía. Los objetos `ErrorDeEntrada` se comportan como +objetos `Error`, excepto que tienen una clase diferente por la cual +podemos reconocerlos. {{index "exception handling"}} -Now the loop can catch these more carefully. +Ahora el ciclo puede atraparlos con mas cuidado. ```{test: no} for (;;) { try { - let dir = promptDirection("Where?"); - console.log("You chose ", dir); + let direccion = pedirDireccion("Donde?"); + console.log("Tu eliges ", direccion); break; } catch (e) { - if (e instanceof InputError) { - console.log("Not a valid direction. Try again."); + if (e instanceof ErrorDeEntrada) { + console.log ("No es una dirección válida. Inténtalo de nuevo"); } else { throw e; } @@ -756,97 +760,99 @@ for (;;) { {{index debugging}} -This will catch only instances of `InputError` and let unrelated -exceptions through. If you reintroduce the typo, the undefined binding -error will be properly reported. +Esto capturará solo las instancias de `error` y dejará que las excepciones +no relacionadas pasen a través. Si reintroduce el error tipográfico, +el error de la vinculación indefinida será reportado correctamente. -## Assertions +## Afirmaciones {{index "assert function", assertion, debugging}} -_Assertions_ are checks inside a program that verify that something is -the way it is supposed to be. They are used not to handle situations -that can come up in normal operation but to find programmer mistakes. +Las _afirmaciones_ son comprobaciones dentro de un programa que verifican que +algo este en la forma en la que se supone que debe estar. Se usan no para +manejar situaciones que puedan aparecer en el funcionamiento normal, +pero para encontrar errores hechos por el programador. -If, for example, `firstElement` is described as a function that should -never be called on empty arrays, we might write it like this: +Si, por ejemplo, `primerElemento` se describe como una función que nunca +se debería invocar en arrays vacíos, podríamos escribirla así: ``` -function firstElement(array) { +function primerElemento(array) { if (array.length == 0) { - throw new Error("firstElement called with []"); + throw new Error("primerElemento llamado con []"); } return array[0]; } ``` -{{index validation, "run-time error", crash, assumption}} +{{index validation, "run-time error", crash, assumption, array}} -Now, instead of silently returning undefined (which you get when -reading an array property that does not exist), this will loudly blow -up your program as soon as you misuse it. This makes it less likely -for such mistakes to go unnoticed and easier to find their cause when -they occur. +Ahora, en lugar de silenciosamente retornar `undefined` (que es lo que obtienes +cuando lees una propiedad de array que no existe), esto explotará fuertemente +tu programa tan pronto como lo uses mal. Esto hace que sea menos probable +que tales errores pasen desapercibidos, y sea más fácil encontrar su causa +cuando estos ocurran. -I do not recommend trying to write assertions for every possible kind -of bad input. That'd be a lot of work and would lead to very noisy -code. You'll want to reserve them for mistakes that are easy to make -(or that you find yourself making). +No recomiendo tratar de escribir afirmaciones para todos los tipos posibles +de entradas erroneas. Eso sería mucho trabajo y llevaría a código muy ruidoso. +Querrás reservarlas para errores que son fáciles de hacer +(o que te encuentras haciendo constantemente). -## Summary +## Resumen -Mistakes and bad input are facts of life. An important part of -programming is finding, diagnosing, and fixing bugs. Problems can -become easier to notice if you have an automated test suite or add -assertions to your programs. +Los errores y las malas entradas son hechos de la vida. Una parte importante de +la programación es encontrar, diagnosticar y corregir errores. Los problemas +pueden será más fáciles de notar si tienes un conjunto de pruebas +automatizadas o si agregas afirmaciones a tus programas. -Problems caused by factors outside the program's control should -usually be handled gracefully. Sometimes, when the problem can be -handled locally, special return values are a good way to track them. -Otherwise, exceptions may be preferable. +Por lo general, los problemas causados ​​por factores fuera del control del +programa deberían ser manejados con gracia. A veces, cuando el problema pueda ser +manejado localmente, los valores de devolución especiales son una buena forma +de rastrearlos. De lo contrario, las excepciones pueden ser preferibles. -Throwing an exception causes the call stack to be unwound until the -next enclosing `try/catch` block or until the bottom of the stack. The -exception value will be given to the `catch` block that catches it, -which should verify that it is actually the expected kind of exception -and then do something with it. To help address the unpredictable -control flow caused by exceptions, `finally` blocks can be used to -ensure that a piece of code _always_ runs when a block finishes. +Al lanzar una excepción, se desenrolla la pila de llamadas hasta +el próximo bloque `try/catch` o hasta el final de la pila. +Se le dará el valor de excepción al bloque `catch` que lo atrape, +que debería verificar que en realidad es el tipo esperado de excepción +y luego hacer algo con eso. Para ayudar a controlar el impredecible +flujo de control causado por las excepciones, los bloques `finally` +se pueden usar para asegurarte de que una parte del código _siempre_ se +ejecute cuando un bloque termina. -## Exercises +## Ejercicios -### Retry +### Reintentar {{index "primitiveMultiply (exercise)", "exception handling", "throw keyword"}} -Say you have a function `primitiveMultiply` that in 20 percent of -cases multiplies two numbers and in the other 80 percent of cases raises an -exception of type `MultiplicatorUnitFailure`. Write a function that -wraps this clunky function and just keeps trying until a call -succeeds, after which it returns the result. +Digamos que tienes una función `multiplicacionPrimitiva` que, en el 20 por +ciento de los casos, multiplica dos números, y en el otro 80 por ciento, +genera una excepción del tipo `FalloUnidadMultiplicadora`. Escribe una función +que envuelva esta torpe función y solo siga intentando hasta que una llamada +tenga éxito, después de lo cual retorna el resultado. {{index "catch keyword"}} -Make sure you handle only the exceptions you are trying to handle. +Asegúrete de solo manejar las excepciones que estás tratando de manejar. {{if interactive ```{test: no} -class MultiplicatorUnitFailure extends Error {} +class FalloUnidadMultiplicadora extends Error {} -function primitiveMultiply(a, b) { +function multiplicacionPrimitiva(a, b) { if (Math.random() < 0.2) { return a * b; } else { - throw new MultiplicatorUnitFailure("Klunk"); + throw new FalloUnidadMultiplicadora("Klunk"); } } -function reliableMultiply(a, b) { - // Your code here. +function multiplicacionConfiable(a, b) { + // Tu código aqui. } -console.log(reliableMultiply(8, 8)); +console.log(multiplicacionConfiable(8, 8)); // → 64 ``` if}} @@ -855,99 +861,100 @@ if}} {{index "primitiveMultiply (exercise)", "try keyword", "catch keyword", "throw keyword"}} -The call to `primitiveMultiply` should definitely happen in a `try` -block. The corresponding `catch` block should rethrow the exception -when it is not an instance of `MultiplicatorUnitFailure` and ensure -the call is retried when it is. +La llamada a `multiplicacionPrimitiva` definitivamente debería suceder en un +bloquear `try`. El bloque `catch` correspondiente debe volver a lanzar la +excepción cuando no esta no sea una instancia de `FalloUnidadMultiplicadora` +y asegurar que la llamada sea reintentada cuando lo es. -To do the retrying, you can either use a loop that stops only when a -call succeeds—as in the [`look` example](error#look) earlier in this -chapter—or use ((recursion)) and hope you don't get a string of -failures so long that it overflows the stack (which is a pretty safe -bet). +Para reintentar, puedes usar un ciclo que solo se rompa cuando +la llamada tenga éxito, como en el [ejemplo de `mirar`](error#look) +anteriormente en este capítulo—o usar ((recursión)) y esperar que no obtengas +una cadena de fallas tan largas que desborde la pila +(lo cual es una apuesta bastante segura). hint}} -### The locked box +### La caja bloqueada {{index "locked box (exercise)"}} -Consider the following (rather contrived) object: +Considera el siguiente objeto (bastante artificial): ``` -const box = { - locked: true, - unlock() { this.locked = false; }, - lock() { this.locked = true; }, - _content: [], - get content() { - if (this.locked) throw new Error("Locked!"); - return this._content; +const caja = { + bloqueada: true, + desbloquear() { this.bloqueada = false; }, + bloquear() { this.bloqueada = true; }, + _contenido: [], + get contenido() { + if (this.bloqueada) throw new Error("Bloqueada!"); + return this._contenido; } }; ``` {{index "private property", "access control"}} -It is a ((box)) with a lock. There is an array in the box, but you can -get at it only when the box is unlocked. Directly accessing the -private `_content` property is forbidden. +Es solo una ((caja)) con una cerradura. Hay un array en la caja, pero solo +puedes accederlo cuando la caja esté desbloqueada. Acceder directamente a la +propiedad privada `_contenido` está prohibido. {{index "finally keyword", "exception handling"}} -Write a function called `withBoxUnlocked` that takes a function value -as argument, unlocks the box, runs the function, and then ensures that -the box is locked again before returning, regardless of whether the -argument function returned normally or threw an exception. +Escribe una función llamada `conCajaDesbloqueada` que toma un valor de función +como su argumento, desbloquea la caja, ejecuta la función y luego se asegura +de que la caja se bloquee nuevamente antes de retornar, independientemente de +si la función argumento retorno normalmente o lanzo una excepción. {{if interactive ``` -const box = { - locked: true, - unlock() { this.locked = false; }, - lock() { this.locked = true; }, - _content: [], - get content() { - if (this.locked) throw new Error("Locked!"); - return this._content; +const caja = { + bloqueada: true, + desbloquear() { this.bloqueada = false; }, + bloquear() { this.bloqueada = true; }, + _contenido: [], + get contenido() { + if (this.bloqueada) throw new Error("Bloqueada!"); + return this._contenido; } }; -function withBoxUnlocked(body) { - // Your code here. +function conCajaDesbloqueada(cuerpo) { + // Tu código aqui. } -withBoxUnlocked(function() { - box.content.push("gold piece"); +conCajaDesbloqueada(function() { + caja.contenido.push("moneda de oro"); }); try { - withBoxUnlocked(function() { - throw new Error("Pirates on the horizon! Abort!"); + conCajaDesbloqueada(function() { + throw new Error("Piratas en el horizonte! Abortar!"); }); } catch (e) { - console.log("Error raised: " + e); + console.log("Error encontrado:", e); } -console.log(box.locked); +console.log(caja.bloqueada); // → true ``` -if}} +Por puntos extras, asegúrete de que si llamas a `conCajaDesbloqueada` +cuando la caja ya está desbloqueada, la caja permanece desbloqueada. -For extra points, make sure that if you call `withBoxUnlocked` when -the box is already unlocked, the box stays unlocked. +if}} {{hint {{index "locked box (exercise)", "finally keyword", "try keyword"}} -This exercise calls for a `finally` block. Your function should first -unlock the box and then call the argument function from inside a `try` -body. The `finally` block after it should lock the box again. +Este ejercicio requiere de un bloque `finally`. Tu función deberia primero +desbloquear la caja y luego llamar a la función argumento desde dentro de +cuerpo `try`. El bloque `finally` después de el debería bloquear la caja +nuevamente. -To make sure we don't lock the box when it wasn't already locked, -check its lock at the start of the function and unlock and lock -it only when it started out locked. +Para asegurarte de que no bloqueemos la caja cuando no estaba ya bloqueada, +comprueba su bloqueo al comienzo de la función y desbloquea y bloquea +solo cuando la caja comenzó bloqueada. hint}} diff --git a/09_regexp.md b/09_regexp.md index f8f8291f0..0cd045d0f 100644 --- a/09_regexp.md +++ b/09_regexp.md @@ -1,9 +1,9 @@ -# Regular Expressions +# Expresiones Regulares {{quote {author: "Jamie Zawinski", chapter: true} -Some people, when confronted with a problem, think 'I know, I'll use -regular expressions.' Now they have two problems. +Algunas personas, cuando confrontadas con un problema, piensan 'Ya sé, usaré +expresiones regulares.' Ahora tienen dos problemas. quote}} @@ -13,9 +13,9 @@ quote}} {{quote {author: "Master Yuan-Ma", title: "The Book of Programming", chapter: true} -Yuan-Ma said, 'When you cut against the grain of the wood, much -strength is needed. When you program against the grain of the problem, -much code is needed.' +Yuan-Ma dijo: 'Cuando cortas contra el grano de la madera, mucha +fuerza se necesita. Cuando programas contra el grano del problema, +mucho código se necesita. quote}} @@ -25,73 +25,74 @@ if}} {{index evolution, adoption, integration}} -Programming ((tool))s and techniques survive and spread in a chaotic, -evolutionary way. It's not always the pretty or brilliant ones that -win but rather the ones that function well enough within the right -niche or that happen to be integrated with another successful piece of -technology. +Las ((herramientas)) y técnicas de la programación sobreviven y se propagan de +una forma caótica y evolutiva. No siempre son los bonitas o las brillantes +las que ganan, sino más bien las que funcionan lo suficientemente bien +dentro del nicho correcto o que sucede se integran con otra pieza exitosa de +tecnología. {{index "domain-specific language"}} -In this chapter, I will discuss one such tool, _((regular -expression))s_. Regular expressions are a way to describe ((pattern))s -in string data. They form a small, separate language that is part of -JavaScript and many other languages and systems. +En este capítulo, discutiré una de esas herramientas, _((expresiones regulare))s_. +Las expresiones regulares son una forma de describir ((patrones)) en datos +de tipo string. Estas forman un lenguaje pequeño e independiente que es parte de +JavaScript y de muchos otros lenguajes y sistemas. {{index [interface, design]}} -Regular expressions are both terribly awkward and extremely useful. -Their syntax is cryptic, and the programming interface JavaScript -provides for them is clumsy. But they are a powerful ((tool)) for -inspecting and processing strings. Properly understanding regular -expressions will make you a more effective programmer. +Las expresiones regulares son terriblemente incómodas y extremadamente útiles. +Su sintaxis es críptica, y la ((interfaz)) de programación que JavaScript +proporciona para ellas es torpe. Pero son una poderosa ((herramienta)) para +inspeccionar y procesar cadenas. Entender apropiadamente a las expresiones +regulares te hará un programador más efectivo. -## Creating a regular expression +## Creando una expresión regular {{index ["regular expression", creation], "RegExp class", "literal expression", "slash character"}} -A regular expression is a type of object. It can be either constructed -with the `RegExp` constructor or written as a literal value by -enclosing a pattern in forward slash (`/`) characters. +Una expresión regular es un tipo de objeto. Puede ser construido +con el constructor `RegExp` o escrito como un valor literal al +envolver un patrón en caracteres de barras diagonales (`/`). ``` let re1 = new RegExp("abc"); let re2 = /abc/; ``` -Both of those regular expression objects represent the same -((pattern)): an _a_ character followed by a _b_ followed by a _c_. +Ambos objetos de expresión regular representan el mismo +((patrón)): un carácter _a_ seguido por una _b_ seguida de una _c_. -{{index ["backslash character", "in regular expressions"], "RegExp class"}} +{{index "backslash character", "RegExp class"}} -When using the `RegExp` constructor, the pattern is written as a -normal string, so the usual rules apply for backslashes. +Cuando se usa el constructor `RegExp`, el patrón se escribe como un +string normal, por lo que las reglas habituales se aplican a las barras +invertidas. {{index ["regular expression", escaping], [escaping, "in regexps"], "slash character"}} -The second notation, where the pattern appears between slash -characters, treats backslashes somewhat differently. First, since a -forward slash ends the pattern, we need to put a backslash before any -forward slash that we want to be _part_ of the pattern. In addition, -backslashes that aren't part of special character codes (like `\n`) -will be _preserved_, rather than ignored as they are in strings, and -change the meaning of the pattern. Some characters, such as question -marks and plus signs, have special meanings in regular expressions and -must be preceded by a backslash if they are meant to represent the -character itself. +La segunda notación, donde el patrón aparece entre caracteres de barras diagonales, +trata a las barras invertidas de una forma diferente. Primero, dado que una +barra diagonal termina el patrón, tenemos que poner una barra invertida antes +de cualquier barra diagonal que queremos sea _parte_ del patrón. En adición, +las barras invertidas que no sean parte de códigos especiales de caracteres +(como `\n`) seran _preservadas_, en lugar de ignoradas, ya que están en strings, y +cambian el significado del patrón. Algunos caracteres, como los signos de +interrogación pregunta y los signos de adición, tienen significados especiales +en las expresiones regulares y deben ir precedidos por una barra inversa +si se pretende que representen al caracter en sí mismo. ``` -let eighteenPlus = /eighteen\+/; +let dieciochoMas = /dieciocho\+/; ``` -## Testing for matches +## Probando por coincidencias {{index matching, "test method", ["regular expression", methods]}} -Regular expression objects have a number of methods. The simplest one -is `test`. If you pass it a string, it will return a ((Boolean)) -telling you whether the string contains a match of the pattern in the -expression. +Los objetos de expresión regular tienen varios métodos. El más simple +es `test` ("probar"). Si le pasas un string, retornar un ((Booleano)) +diciéndote si el string contiene una coincidencia del patrón en la +expresión. ``` console.log(/abc/.test("abcde")); @@ -102,109 +103,109 @@ console.log(/abc/.test("abxde")); {{index pattern}} -A ((regular expression)) consisting of only nonspecial characters -simply represents that sequence of characters. If _abc_ occurs -anywhere in the string we are testing against (not just at the start), -`test` will return `true`. +Una ((expresión regular)) que consista solamente de caracteres no especiales +simplemente representara esa secuencia de caracteres. Si _abc_ ocurre +en cualquier parte del string con la que estamos probando (no solo al comienzo), +`test` retornara `true`. -## Sets of characters +## Conjuntos de caracteres {{index "regular expression", "indexOf method"}} -Finding out whether a string contains _abc_ could just as well be done -with a call to `indexOf`. Regular expressions allow us to express more -complicated ((pattern))s. +Averiguar si un string contiene _abc_ bien podría hacerse con una llamada a +`indexOf`. Las expresiones regulares nos permiten expresar ((patrones)) más +complicados. -Say we want to match any ((number)). In a regular expression, putting -a ((set)) of characters between square brackets makes that part of the -expression match any of the characters between the brackets. +Digamos que queremos encontrar cualquier ((número)). En una expresión regular, +poner un ((conjunto)) de caracteres entre corchetes hace que esa parte de la +expresión coincida con cualquiera de los caracteres entre los corchetes. -Both of the following expressions match all strings that contain a ((digit)): +Ambas expresiones coincidiran con todas los strings que contengan un ((dígito)): ``` -console.log(/[0123456789]/.test("in 1992")); +console.log(/[0123456789]/.test("en 1992")); // → true -console.log(/[0-9]/.test("in 1992")); +console.log(/[0-9]/.test("en 1992")); // → true ``` -{{index "hyphen character"}} +{{index "dash character"}} -Within square brackets, a hyphen (`-`) between two characters can be -used to indicate a ((range)) of characters, where the ordering is -determined by the character's ((Unicode)) number. Characters 0 to 9 -sit right next to each other in this ordering (codes 48 to 57), so -`[0-9]` covers all of them and matches any ((digit)). +Dentro de los corchetes, un guion (`-`) entre dos caracteres puede ser +utilizado para indicar un ((rango)) de caracteres, donde el orden es +determinado por el número ((Unicode)) del carácter. Los caracteres 0 a 9 +estan uno al lado del otro en este orden (códigos 48 a 57), por lo que +`[0-9]` los cubre a todos y coincide con cualquier ((dígito)). -{{index [whitespace, matching], "alphanumeric character", "period character"}} +{{index whitespace, "alphanumeric character", "period character"}} -A number of common character groups have their own -built-in shortcuts. Digits are one of them: `\d` means the same thing -as `[0-9]`. +Un numero de caracteres comunes tienen sus propios atajos incorporados. +Los dígitos son uno de ellos: `\d` significa lo mismo que `[0-9]`. -{{index "newline character", [whitespace, matching]}} +{{index "newline character"}} {{table {cols: [1, 5]}}} -| `\d` | Any ((digit)) character -| `\w` | An alphanumeric character ("((word character))") -| `\s` | Any whitespace character (space, tab, newline, and similar) -| `\D` | A character that is _not_ a digit -| `\W` | A nonalphanumeric character -| `\S` | A nonwhitespace character -| `.` | Any character except for newline +| `\d` | Cualquier caracter ((dígito)) +| `\w` | Un caracter alfanumérico +| `\s` | Cualquier carácter de espacio en blanco (espacio, tabulación, nueva línea y similar) +| `\D` | Un caracter que _no_ es un dígito +| `\W` | Un caracter no alfanumérico +| `\S` | Un caracter que no es un espacio en blanco +| `.` | Cualquier caracter a excepción de una nueva línea -So you could match a ((date)) and ((time)) format like 01-30-2003 -15:20 with the following expression: +Por lo que podrías coincidir con un formato de ((fecha)) y ((hora)) como 30-01-2003 +15:20 con la siguiente expresión: ``` -let dateTime = /\d\d-\d\d-\d\d\d\d \d\d:\d\d/; -console.log(dateTime.test("01-30-2003 15:20")); +let fechaHora = /\d\d-\d\d-\d\d\d\d \d\d:\d\d/; +console.log(fechaHora.test("30-01-2003 15:20")); // → true -console.log(dateTime.test("30-jan-2003 15:20")); +console.log(fechaHora.test("30-jan-2003 15:20")); // → false ``` -{{index ["backslash character", "in regular expressions"]}} +{{index "backslash character"}} -That looks completely awful, doesn't it? Half of it is backslashes, -producing a background noise that makes it hard to spot the actual -((pattern)) expressed. We'll see a slightly improved version of this -expression [later](regexp#date_regexp_counted). +Eso se ve completamente horrible, no? La mitad de la expresión son barras invertidas, +produciendo un ruido de fondo que hace que sea difícil detectar el ((patrón)) +real que queremos expresar. Veremos una versión ligeramente mejorada de esta +expresión [más tarde](regexp#date_regexp_counted). {{index [escaping, "in regexps"], "regular expression", set}} -These backslash codes can also be used inside ((square brackets)). For -example, `[\d.]` means any digit or a period character. But the period -itself, between square brackets, loses its special meaning. The same -goes for other special characters, such as `+`. +Estos códigos de barra invertida también pueden usarse dentro de ((corchetes)). +Por ejemplo, `[\d.]` representa cualquier dígito o un carácter de punto. +Pero el punto en sí mismo, entre corchetes, pierde su significado especial. +Lo mismo va para otros caracteres especiales, como `+`. {{index "square brackets", inversion, "caret character"}} -To _invert_ a set of characters—that is, to express that you want to -match any character _except_ the ones in the set—you can write a caret -(`^`) character after the opening bracket. +Para _invertir_ un conjunto de caracteres, es decir, para expresar que deseas +coincidir con cualquier carácter _excepto_ con los que están en el +conjunto—puedes escribir un carácter de intercalación (`^`) después del +corchete de apertura. ``` -let notBinary = /[^01]/; -console.log(notBinary.test("1100100010100110")); +let noBinario = /[^01]/; +console.log(noBinario.test("1100100010100110")); // → false -console.log(notBinary.test("1100100010200110")); +console.log(noBinario.test("1100100010200110")); // → true ``` -## Repeating parts of a pattern +## Repitiendo partes de un patrón {{index ["regular expression", repetition]}} -We now know how to match a single digit. What if we want to match a -whole number—a ((sequence)) of one or more ((digit))s? +Ya sabemos cómo hacer coincidir un solo dígito. Qué pasa si queremos hacer +coincidir un número completo—una ((secuencia)) de uno o más ((dígito))s? {{index "plus character", repetition, "+ operator"}} -When you put a plus sign (`+`) after something in a regular -expression, it indicates that the element may be repeated more than -once. Thus, `/\d+/` matches one or more digit characters. +Cuando pones un signo más (`+`) después de algo en una expresión regular, +este indica que el elemento puede repetirse más de +una vez. Por lo tanto, `/\d+/` coincide con uno o más caracteres de dígitos. ``` console.log(/'\d+'/.test("'123'")); @@ -219,159 +220,161 @@ console.log(/'\d*'/.test("''")); {{index "* operator", asterisk}} -The star (`*`) has a similar meaning but also allows the pattern to -match zero times. Something with a star after it never prevents a -pattern from matching—it'll just match zero instances if it can't find -any suitable text to match. +La estrella (`*`) tiene un significado similar pero también permite que el +patrón coincida cero veces. Algo con una estrella después de el nunca evitara un +patrón de coincidirlo—este solo coincidirá con cero instancias si no +puede encontrar ningun texto adecuado para coincidir. {{index "British English", "American English", "question mark"}} -A question mark makes a part of a pattern _((optional))_, meaning it -may occur zero times or one time. In the following example, the _u_ -character is allowed to occur, but the pattern also matches when it is -missing. +Un signo de interrogación hace que alguna parte de un patrón sea _((opcional))_, +lo que significa que puede ocurrir cero o mas veces. En el siguiente ejemplo, +el carácter _h_ está permitido, pero el patrón también retorna verdadero +cuando esta letra no esta. ``` -let neighbor = /neighbou?r/; -console.log(neighbor.test("neighbour")); +let reusar = /reh?usar/; +console.log(reusar.test("rehusar")); // → true -console.log(neighbor.test("neighbor")); +console.log(reusar.test("reusar")); // → true ``` -{{index repetition, [braces, "in regular expression"]}} +{{index repetition, "curly braces"}} -To indicate that a pattern should occur a precise number of times, use -braces. Putting `{4}` after an element, for example, requires it -to occur exactly four times. It is also possible to specify a -((range)) this way: `{2,4}` means the element must occur at least -twice and at most four times. +Para indicar que un patrón deberia ocurrir un número preciso de veces, usa +llaves. Por ejemplo, al poner `{4}` después de un elemento, hace que requiera +que este ocurra exactamente cuatro veces. También es posible especificar un +((rango)) de esta manera: `{2,4}` significa que el elemento debe ocurrir al +menos dos veces y como máximo cuatro veces. {{id date_regexp_counted}} -Here is another version of the ((date)) and ((time)) pattern that -allows both single- and double-((digit)) days, months, and hours. It -is also slightly easier to decipher. +Aquí hay otra versión del patrón ((fecha)) y ((hora)) que +permite días tanto en ((dígitos)) individuales como dobles, meses y horas. +Es también un poco más fácil de descifrar. ``` -let dateTime = /\d{1,2}-\d{1,2}-\d{4} \d{1,2}:\d{2}/; -console.log(dateTime.test("1-30-2003 8:45")); +let fechaHora = /\d{1,2}-\d{1,2}-\d{4} \d{1,2}:\d{2}/; +console.log(fechaHora.test("30-1-2003 8:45")); // → true ``` -You can also specify open-ended ((range))s when using braces -by omitting the number after the comma. So, `{5,}` means five or more -times. +También puedes especificar ((rangos)) de final abierto al usar ((llaves)) +omitiendo el número después de la coma. Entonces, `{5,}` significa cinco o más +veces. -## Grouping subexpressions +## Agrupando subexpresiones -{{index ["regular expression", grouping], grouping, [parentheses, "in regular expressions"]}} +{{index ["regular expression", grouping], grouping}} -To use an operator like `*` or `+` on more than one element at a time, -you have to use parentheses. A part of a regular expression that -is enclosed in parentheses counts as a single element as far as the -operators following it are concerned. +Para usar un operador como `*` o `+` en más de un elemento a la vez, +tienes que usar ((paréntesis)). Una parte de una expresión regular que +se encierre entre paréntesis cuenta como un elemento único en cuanto a +los operadores que la siguen están preocupados. ``` -let cartoonCrying = /boo+(hoo+)+/i; -console.log(cartoonCrying.test("Boohoooohoohooo")); +let caricaturaLlorando = /boo+(hoo+)+/i; +console.log(caricaturaLlorando.test("Boohoooohoohooo")); // → true ``` {{index crying}} -The first and second `+` characters apply only to the second _o_ in -_boo_ and _hoo_, respectively. The third `+` applies to the whole -group `(hoo+)`, matching one or more sequences like that. +El primer y segundo caracter `+` aplican solo a la segunda _o_ en +_boo_ y _hoo_, respectivamente. El tercer `+` se aplica a la totalidad +del grupo `(hoo+)`, haciendo coincidir una o más secuencias como esa. {{index "case sensitivity", capitalization, ["regular expression", flags]}} -The `i` at the end of the expression in the example makes this regular -expression case insensitive, allowing it to match the uppercase _B_ in -the input string, even though the pattern is itself all lowercase. +La `i` al final de la expresión en el ejemplo hace que esta expresión regular +sea insensible a mayúsculas y minúsculas, lo que permite que coincida con la +letra mayúscula _B_ en el string que se le da de entrada, asi el +patrón en sí mismo este en minúsculas. -## Matches and groups +## Coincidencias y grupos -{{index ["regular expression", grouping], "exec method", [array, "RegExp match"]}} +{{index ["regular expression", grouping], "exec method", array}} -The `test` method is the absolute simplest way to match a regular -expression. It tells you only whether it matched and nothing else. -Regular expressions also have an `exec` (execute) method that will -return `null` if no match was found and return an object with -information about the match otherwise. +El método `test` es la forma más simple de hacer coincidir una +expresión. Solo te dice si coincide y nada más. +Las expresiones regulares también tienen un método `exec` ("ejecutar") que +retorna `null` si no se encontró una coincidencia y retorna un objeto con +información sobre la coincidencia de lo contrario. ``` -let match = /\d+/.exec("one two 100"); -console.log(match); +let coincidencia = /\d+/.exec("uno dos 100"); +console.log(coincidencia); // → ["100"] -console.log(match.index); +console.log(coincidencia.index); // → 8 ``` {{index "index property", [string, indexing]}} -An object returned from `exec` has an `index` property that tells us -_where_ in the string the successful match begins. Other than that, -the object looks like (and in fact is) an array of strings, whose -first element is the string that was matched. In the previous example, -this is the sequence of ((digit))s that we were looking for. +Un objeto retornado por `exec` tiene una propiedad `index` ("indice") que nos +dice _donde_ en el string comienza la coincidencia exitosa. Aparte de eso, +el objeto parece (y de hecho es) un array de strings, cuyo +primer elemento es el string que coincidio—en el ejemplo anterior, +esta es la secuencia de ((dígito))s que estábamos buscando. {{index [string, methods], "match method"}} -String values have a `match` method that behaves similarly. +Los valores de tipo string tienen un método `match` que se comporta de +manera similar. ``` -console.log("one two 100".match(/\d+/)); +console.log("uno dos 100".match(/\d+/)); // → ["100"] ``` {{index grouping, "capture group", "exec method"}} -When the regular expression contains subexpressions grouped with -parentheses, the text that matched those groups will also show up in -the array. The whole match is always the first element. The next -element is the part matched by the first group (the one whose opening -parenthesis comes first in the expression), then the second group, and -so on. +Cuando la expresión regular contenga subexpresiones agrupadas con +paréntesis, el texto que coincida con esos grupos también aparecerá en +el array. La coincidencia completa es siempre el primer elemento. El siguiente +elemento es la parte que coincidio con el primer grupo (el que abre +paréntesis primero en la expresión), luego el segundo grupo, y +asi sucesivamente. ``` -let quotedText = /'([^']*)'/; -console.log(quotedText.exec("she said 'hello'")); -// → ["'hello'", "hello"] +let textoCitado = /'([^']*)'/; +console.log(textoCitado.exec("ella dijo 'hola'")); +// → ["'hola'", "hola"] ``` {{index "capture group"}} -When a group does not end up being matched at all (for example, when -followed by a question mark), its position in the output array will -hold `undefined`. Similarly, when a group is matched multiple times, -only the last match ends up in the array. +Cuando un grupo no termina siendo emparejado en absoluto (por ejemplo, cuando +es seguido de un signo de interrogación), su posición en el array de salida +sera `undefined`. Del mismo modo, cuando un grupo coincida multiples veces, +solo la ultima coincidencia termina en el array. ``` -console.log(/bad(ly)?/.exec("bad")); -// → ["bad", undefined] +console.log(/mal(isimo)?/.exec("mal")); +// → ["mal", undefined] console.log(/(\d)+/.exec("123")); // → ["123", "3"] ``` {{index "exec method", ["regular expression", methods], extraction}} -Groups can be useful for extracting parts of a string. If we don't -just want to verify whether a string contains a ((date)) but also -extract it and construct an object that represents it, we can wrap -parentheses around the digit patterns and directly pick the date out -of the result of `exec`. +Los grupos pueden ser útiles para extraer partes de un string. Si no solo +queremos verificar si un string contiene una ((fecha)) pero también +extraerla y construir un objeto que la represente, podemos envolver +paréntesis alrededor de los patrones de dígitos y tomar directamente la fecha +del resultado de `exec`. -But first we'll take a brief detour, in which we discuss the built-in way to -represent date and ((time)) values in JavaScript. +Pero primero, un breve desvío, en el que discutiremos la forma incorporada de +representar valores de fecha y ((hora)) en JavaScript. -## The Date class +## La clase Date ("Fecha") {{index constructor, "Date class"}} -JavaScript has a standard class for representing ((date))s—or, rather, -points in ((time)). It is called `Date`. If you simply create a date -object using `new`, you get the current date and time. +JavaScript tiene una clase estándar para representar ((fecha))s—o mejor dicho, +puntos en el ((tiempo)). Se llama `Date`. Si simplemente creas un objeto +fecha usando `new`, obtienes la fecha y hora actual. ```{test: no} console.log(new Date()); @@ -380,7 +383,7 @@ console.log(new Date()); {{index "Date class"}} -You can also create an object for a specific time. +También puedes crear un objeto para un tiempo específico. ``` console.log(new Date(2009, 11, 9)); @@ -391,20 +394,21 @@ console.log(new Date(2009, 11, 9, 12, 59, 59, 999)); {{index "zero-based counting", [interface, design]}} -JavaScript uses a convention where month numbers start at zero (so -December is 11), yet day numbers start at one. This is confusing and -silly. Be careful. +JavaScript usa una convención en donde los números de los meses comienzan en +cero (por lo que Diciembre es 11), sin embargo, los números de los días +comienzan en uno. Esto es confuso y tonto. Ten cuidado. -The last four arguments (hours, minutes, seconds, and milliseconds) -are optional and taken to be zero when not given. +Los últimos cuatro argumentos (horas, minutos, segundos y milisegundos) +son opcionales y se toman como cero cuando no se dan. {{index "getTime method"}} -Timestamps are stored as the number of milliseconds since the start of -1970, in the UTC ((time zone)). This follows a convention set by -"((Unix time))", which was invented around that time. You can use -negative numbers for times before 1970. The `getTime` method on a date -object returns this number. It is big, as you can imagine. +Las marcas de tiempo se almacenan como la cantidad de milisegundos desde el +inicio de 1970, en la ((zona horaria)) UTC. Esto sigue una convención +establecida por el "((Tiempo Unix))", el cual se inventó en ese momento. +Puedes usar números negativos para los tiempos anteriores a 1970. Usar el método +`getTime` ("obtenerTiempo") en un objeto fecha retorna este número. +Es bastante grande, como te puedes imaginar. ``` console.log(new Date(2013, 11, 19).getTime()); @@ -415,238 +419,240 @@ console.log(new Date(1387407600000)); {{index "Date.now function", "Date class"}} -If you give the `Date` constructor a single argument, that argument is -treated as such a millisecond count. You can get the current -millisecond count by creating a new `Date` object and calling -`getTime` on it or by calling the `Date.now` function. +Si le das al constructor `Date` un único argumento, ese argumento sera +tratado como un conteo de milisegundos. Puedes obtener el recuento +de milisegundos actual creando un nuevo objeto `Date` y llamando +`getTime` en él o llamando a la función `Date.now`. {{index "getFullYear method", "getMonth method", "getDate method", "getHours method", "getMinutes method", "getSeconds method", "getYear method"}} -Date objects provide methods such as `getFullYear`, `getMonth`, -`getDate`, `getHours`, `getMinutes`, and `getSeconds` to extract their -components. Besides `getFullYear` there's also `getYear`, which gives -you the year minus 1900 (`98` or `119`) and is mostly useless. +Los objetos de fecha proporcionan métodos como `getFullYear` +("obtenerAñoCompleto"), `getMonth` ("obtenerMes"), `getDate` ("obtenerFecha"), +`getHours` ("obtenerHoras"), `getMinutes` ("obtenerMinutos"), y `getSeconds` +("obtenerSegundos") para extraer sus componentes. Además de `getFullYear`, +también existe `getYear` ("obtenerAño"), que te da como resultado un valor +de año de dos dígitos bastante inútil (como `93` o `14`). -{{index "capture group", "getDate method", [parentheses, "in regular expressions"]}} +{{index "capture group", "getDate function"}} -Putting parentheses around the parts of the expression that we are -interested in, we can now create a date object from a string. +Al poner ((paréntesis)) alrededor de las partes de la expresión en las que +estamos interesados, ahora podemos crear un objeto de fecha a partir de un +string. ``` -function getDate(string) { - let [_, month, day, year] = +function obtenerFecha(string) { + let [_, dia, mes, año] = /(\d{1,2})-(\d{1,2})-(\d{4})/.exec(string); - return new Date(year, month - 1, day); + return new Date(año, mes - 1, dia); } -console.log(getDate("1-30-2003")); +console.log(obtenerFecha("30-1-2003")); // → Thu Jan 30 2003 00:00:00 GMT+0100 (CET) ``` {{index destructuring, "underscore character"}} -The `_` (underscore) binding is ignored and used only to skip the -full match element in the array returned by `exec`. +La vinculación `_` (guion bajo) es ignorada, y solo se usa para omitir el +elemento de coincidencia completa en el array retornado por `exec`. -## Word and string boundaries +## Palabra y límites de string {{index matching, ["regular expression", boundary]}} -Unfortunately, `getDate` will also happily extract the nonsensical -date 00-1-3000 from the string `"100-1-30000"`. A match may happen -anywhere in the string, so in this case, it'll just start at the -second character and end at the second-to-last character. +Desafortunadamente, `obtenerFecha` felizmente también extraerá la absurda +fecha 00-1-3000 del string `"100-1-30000"`. Una coincidencia puede suceder +en cualquier lugar del string, por lo que en este caso, esta simplemente +comenzará en el segundo carácter y terminara en el penúltimo carácter. {{index boundary, "caret character", "dollar sign"}} -If we want to enforce that the match must span the whole string, we -can add the markers `^` and `$`. The caret matches the start of the -input string, whereas the dollar sign matches the end. So, `/^\d+$/` -matches a string consisting entirely of one or more digits, `/^!/` -matches any string that starts with an exclamation mark, and `/x^/` -does not match any string (there cannot be an _x_ before the start of -the string). +Si queremos hacer cumplir que la coincidencia deba abarcar el string completamente, +puedes agregar los marcadores `^` y `$`. El signo de intercalación ("^") +coincide con el inicio del string de entrada, mientras que el signo de dólar +coincide con el final. Entonces, `/^\d+$/` coincide con un string compuesto +por uno o más dígitos, `/^!/` coincide con cualquier string que comience con +un signo de exclamación, y `/x^/` no coincide con ningun string +(no puede haber una _x_ antes del inicio del string). {{index "word boundary", "word character"}} -If, on the other hand, we just want to make sure the date starts and -ends on a word boundary, we can use the marker `\b`. A word boundary -can be the start or end of the string or any point in the string that -has a word character (as in `\w`) on one side and a nonword character -on the other. +Si, por el otro lado, solo queremos asegurarnos de que la fecha comience y +termina en un límite de palabras, podemos usar el marcador `\b`. Un límite de +palabra puede ser el inicio o el final del string o cualquier punto en el +string que tenga un carácter de palabra (como en `\w`) en un lado y un +carácter de no-palabra en el otro. ``` -console.log(/cat/.test("concatenate")); +console.log(/cat/.test("concatenar")); // → true -console.log(/\bcat\b/.test("concatenate")); +console.log(/\bcat\b/.test("concatenar")); // → false ``` {{index matching}} -Note that a boundary marker doesn't match an actual character. It just -enforces that the regular expression matches only when a certain -condition holds at the place where it appears in the pattern. +Ten en cuenta que un marcador de límite no coincide con un carácter real. Solo +hace cumplir que la expresión regular coincida solo cuando una cierta +condición se mantenga en el lugar donde aparece en el patrón. -## Choice patterns +## Patrones de elección {{index branching, ["regular expression", alternatives], "farm example"}} -Say we want to know whether a piece of text contains not only a number -but a number followed by one of the words _pig_, _cow_, or _chicken_, -or any of their plural forms. +Digamos que queremos saber si una parte del texto contiene no solo un número +pero un número seguido de una de las palabras _cerdo_, _vaca_, o _pollo_, +o cualquiera de sus formas plurales. -We could write three regular expressions and test them in turn, but -there is a nicer way. The ((pipe character)) (`|`) denotes a -((choice)) between the pattern to its left and the pattern to its -right. So I can say this: +Podríamos escribir tres expresiones regulares y probarlas a su vez, pero +hay una manera más agradable. El ((carácter de tubería)) (`|`) denota una +((elección)) entre el patrón a su izquierda y el patrón a su +derecha. Entonces puedo decir esto: ``` -let animalCount = /\b\d+ (pig|cow|chicken)s?\b/; -console.log(animalCount.test("15 pigs")); +let conteoAnimales = /\b\d+ (cerdo|vaca|pollo)s?\b/; +console.log(conteoAnimales.test("15 cerdo")); // → true -console.log(animalCount.test("15 pigchickens")); +console.log(conteoAnimales.test("15 cerdopollos")); // → false ``` -{{index [parentheses, "in regular expressions"]}} +{{index parentheses}} -Parentheses can be used to limit the part of the pattern that the pipe -operator applies to, and you can put multiple such operators next to -each other to express a choice between more than two alternatives. +Los paréntesis se pueden usar para limitar la parte del patrón a la que aplica +el operador de tuberia, y puedes poner varios de estos operadores unos a los +lados de los otros para expresar una elección entre más de dos alternativas. -## The mechanics of matching +## Las mecánicas del emparejamiento -{{index ["regular expression", matching], [matching, algorithm], "search problem"}} +{{index ["regular expression", matching], [matching, algorithm], searching}} -Conceptually, when you use `exec` or `test`, the regular expression -engine looks for a match in your string by trying to match the -expression first from the start of the string, then from the second -character, and so on, until it finds a match or reaches the end of the -string. It'll either return the first match that can be found or fail -to find any match at all. +Conceptualmente, cuando usas `exec` o `test` el motor de la expresión regular +busca una coincidencia en tu string al tratar de hacer coincidir la +expresión primero desde el comienzo del string, luego desde el segundo +caracter, y así sucesivamente hasta que encuentra una coincidencia o llega al +final del string. Retornara la primera coincidencia que se puede encontrar o +fallara en encontrar cualquier coincidencia. {{index ["regular expression", matching], [matching, algorithm]}} -To do the actual matching, the engine treats a regular expression -something like a ((flow diagram)). This is the diagram for the -livestock expression in the previous example: +Para realmente hacer la coincidencia, el motor trata una expresión regular +algo así como un ((diagrama de flujo)). Este es el diagrama para la +expresión de ganado en el ejemplo anterior: {{figure {url: "img/re_pigchickens.svg", alt: "Visualization of /\\b\\d+ (pig|cow|chicken)s?\\b/"}}} {{index traversal}} -Our expression matches if we can find a path from the left side of the -diagram to the right side. We keep a current position in the string, -and every time we move through a box, we verify that the part of the -string after our current position matches that box. +Nuestra expresión coincide si podemos encontrar un camino desde el lado +izquierdo del diagrama al lado derecho. Mantenemos una posición actual en el +string, y cada vez que nos movemos a través de una caja, verificaremos que la +parte del string después de nuestra posición actual coincida con esa +caja. -So if we try to match `"the 3 pigs"` from position 4, our progress -through the flow chart would look like this: +Entonces, si tratamos de coincidir `"los 3 cerdos"` desde la posición 4, +nuestro progreso a través del diagrama de flujo se vería así: - - At position 4, there is a word ((boundary)), so we can move past - the first box. +- En la posición 4, hay un ((límite)) de palabra, por lo que podemos pasar + la primera caja. - - Still at position 4, we find a digit, so we can also move past the - second box. +- Aún en la posición 4, encontramos un dígito, por lo que también podemos pasar + la segunda caja. - - At position 5, one path loops back to before the second (digit) - box, while the other moves forward through the box that holds a - single space character. There is a space here, not a digit, so we - must take the second path. +- En la posición 5, una ruta regresa a antes de la segunda caja (dígito), + mientras que la otro se mueve hacia adelante a través de la caja que contiene + un caracter de espacio simple. Hay un espacio aquí, no un dígito, asi que + debemos tomar el segundo camino. - - We are now at position 6 (the start of _pigs_) and at the three-way - branch in the diagram. We don't see _cow_ or _chicken_ here, but we - do see _pig_, so we take that branch. +- Ahora estamos en la posición 6 (el comienzo de "cerdos") y en el camino de + tres vías en el diagrama. No vemos "vaca" o "pollo" aquí, pero + vemos "cerdo", entonces tomamos esa rama. - - At position 9, after the three-way branch, one path skips the _s_ - box and goes straight to the final word boundary, while the other - path matches an _s_. There is an _s_ character here, not a word - boundary, so we go through the _s_ box. +- En la posición 9, después de la rama de tres vías, un camino se salta la + caja _s_ y va directamente al límite de la palabra final, mientras que la + otra ruta coincide con una _s_. Aquí hay un carácter _s_, no una palabra + límite, por lo que pasamos por la caja _s_. - - We're at position 10 (the end of the string) and can match only a - word ((boundary)). The end of a string counts as a word boundary, - so we go through the last box and have successfully matched this - string. +- Estamos en la posición 10 (al final del string) y solo podemos hacer coincidir + una palabra ((límite)). El final de un string cuenta como un límite de palabra, + así que pasamos por la última caja y hemos emparejado con éxito este string. {{id backtracking}} -## Backtracking +## Retrocediendo {{index ["regular expression", backtracking], "binary number", "decimal number", "hexadecimal number", "flow diagram", [matching, algorithm], backtracking}} -The regular expression `/\b([01]+b|[\da-f]+h|\d+)\b/` matches either a -binary number followed by a _b_, a hexadecimal number (that is, base -16, with the letters _a_ to _f_ standing for the digits 10 to 15) -followed by an _h_, or a regular decimal number with no suffix -character. This is the corresponding diagram: +La expresión regular `/\b([01]+b|[\da-f]+h|\d+)\b/` coincide con un +número binario seguido de una _b_, un número hexadecimal (es decir, en base +16, con las letras _a_ a _f_ representando los dígitos 10 a 15) +seguido de una _h_, o un número decimal regular sin caracter de sufijo. +Este es el diagrama correspondiente: {{figure {url: "img/re_number.svg", alt: "Visualization of /\\b([01]+b|\\d+|[\\da-f]+h)\\b/"}}} {{index branching}} -When matching this expression, it will often happen that the top -(binary) branch is entered even though the input does not actually -contain a binary number. When matching the string `"103"`, for -example, it becomes clear only at the 3 that we are in the wrong -branch. The string _does_ match the expression, just not the branch we -are currently in. +Al hacer coincidir esta expresión, a menudo sucederá que la rama superior +(binaria) sea ingresada aunque la entrada en realidad no contenga un número +binario. Al hacer coincidir el string `"103"`, por ejemplo, queda claro solo en +el 3 que estamos en la rama equivocada. El string _si_ coincide con la +expresión, pero no con la rama en la que nos encontramos actualmente. -{{index backtracking, "search problem"}} +{{index backtracking, searching}} -So the matcher _backtracks_. When entering a branch, it remembers its -current position (in this case, at the start of the string, just past -the first boundary box in the diagram) so that it can go back and try -another branch if the current one does not work out. For the string -`"103"`, after encountering the 3 character, it will start trying the -branch for hexadecimal numbers, which fails again because there is no -_h_ after the number. So it tries the decimal number branch. This one -fits, and a match is reported after all. +Entonces el "emparejador" _retrocede_. Al ingresar a una rama, este recuerda su +posición actual (en este caso, al comienzo del string, justo después +del primer cuadro de límite en el diagrama) para que pueda retroceder e intentar +otra rama si la actual no funciona. Para el string `"103"`, después de +encontrar los 3 caracteres, comenzará a probar la +rama para números hexadecimales, que falla nuevamente porque no hay +_h_ después del número. Por lo tanto, intenta con la rama de número decimal. +Esta encaja, y se informa de una coincidencia después de todo. {{index [matching, algorithm]}} -The matcher stops as soon as it finds a full match. This means that if -multiple branches could potentially match a string, only the first one -(ordered by where the branches appear in the regular expression) is -used. - -Backtracking also happens for ((repetition)) operators like + and `*`. -If you match `/^.*x/` against `"abcxe"`, the `.*` part will first try -to consume the whole string. The engine will then realize that it -needs an _x_ to match the pattern. Since there is no _x_ past the end -of the string, the star operator tries to match one character less. -But the matcher doesn't find an _x_ after `abcx` either, so it -backtracks again, matching the star operator to just `abc`. _Now_ it -finds an _x_ where it needs it and reports a successful match from -positions 0 to 4. +El emparejador se detiene tan pronto como encuentra una coincidencia completa. +Esto significa que si múltiples ramas podrían coincidir con un string, solo +la primera (ordenado por donde las ramas aparecen en la expresión regular) es +usada. + +El retroceso también ocurre para ((repetición)) de operadores como + y `*`. +Si hace coincidir `/^.*x/` contra `"abcxe"`, la parte `.*` intentará primero +consumir todo el string. El motor entonces se dará cuenta de que +necesita una _x_ para que coincida con el patrón. Como no hay _x_ al pasar +el final del string, el operador de estrella intenta hacer coincidir un +caracter menos. Pero el emparejador tampoco encuentra una _x_ después de `abcx`, +por lo que retrocede nuevamente, haciendo coincidir el operador de estrella con +`abc`. _Ahora_ encuentra una _x_ donde lo necesita e informa de una +coincidencia exitosa de las posiciones 0 a 4. {{index performance, complexity}} -It is possible to write regular expressions that will do a _lot_ of -backtracking. This problem occurs when a pattern can match a piece of -input in many different ways. For example, if we get confused while -writing a binary-number regular expression, we might accidentally -write something like `/([01]+)+b/`. +Es posible escribir expresiones regulares que harán un _monton_ de +retrocesos. Este problema ocurre cuando un patrón puede coincidir con una +pieza de entrada en muchas maneras diferentes. Por ejemplo, si nos +confundimos mientras escribimos una expresión regular de números binarios, +podríamos accidentalmente escribir algo como `/([01]+)+b/`. {{figure {url: "img/re_slow.svg", alt: "Visualization of /([01]+)+b/",width: "6cm"}}} {{index "inner loop", [nesting, "in regexps"]}} -If that tries to match some long series of zeros and ones with no -trailing _b_ character, the matcher first goes through the inner -loop until it runs out of digits. Then it notices there is no _b_, so -it backtracks one position, goes through the outer loop once, and -gives up again, trying to backtrack out of the inner loop once more. -It will continue to try every possible route through these two loops. -This means the amount of work _doubles_ with each additional -character. For even just a few dozen characters, the resulting match -will take practically forever. +Si intentas hacer coincidir eso con algunas largas series de ceros y unos sin +un caracter _b_ al final, el emparejador primero pasara por el ciclo interior +hasta que se quede sin dígitos. Entonces nota que no hay _b_, asi que +retrocede una posición, atraviesa el ciclo externo una vez, y +se da por vencido otra vez, tratando de retroceder fuera del ciclo interno una +vez más. Continuará probando todas las rutas posibles a través de estos dos +bucles. Esto significa que la cantidad de trabajo se _duplica_ con cada +caracter. Incluso para unas pocas docenas de caracters, la coincidencia +resultante tomará prácticamente para siempre. -## The replace method +## El método replace {{index "replace method", "regular expression"}} -String values have a `replace` method that can be used to replace -part of the string with another string. +Los valores de string tienen un método `replace` ("reemplazar") que se puede +usar para reemplazar parte del string con otro string. ``` console.log("papa".replace("p", "m")); @@ -655,10 +661,10 @@ console.log("papa".replace("p", "m")); {{index ["regular expression", flags], ["regular expression", global]}} -The first argument can also be a regular expression, in which case the -first match of the regular expression is replaced. When a `g` option -(for _global_) is added to the regular expression, _all_ matches in -the string will be replaced, not just the first. +El primer argumento también puede ser una expresión regular, en cuyo caso ña +primera coincidencia de la expresión regular es reemplazada. Cuando una opción +`g` (para _global_) se agrega a la expresión regular, _todas_ las coincidencias +en el string será reemplazadas, no solo la primera. ``` console.log("Borobudur".replace(/[ou]/, "a")); @@ -669,20 +675,20 @@ console.log("Borobudur".replace(/[ou]/g, "a")); {{index [interface, design], argument}} -It would have been sensible if the choice between replacing one match -or all matches was made through an additional argument to `replace` or -by providing a different method, `replaceAll`. But for some -unfortunate reason, the choice relies on a property of the regular -expression instead. +Hubiera sido sensato si la elección entre reemplazar una coincidencia +o todas las coincidencias se hiciera a través de un argumento adicional en +`replace` o proporcionando un método diferente, `replaceAll` ("reemplazarTodas"). +Pero por alguna desafortunada razón, la elección se basa en una propiedad de +los expresiones regulares en su lugar. {{index grouping, "capture group", "dollar sign", "replace method", ["regular expression", grouping]}} -The real power of using regular expressions with `replace` comes from -the fact that we can refer to matched groups in the replacement -string. For example, say we have a big string containing the names of -people, one name per line, in the format `Lastname, Firstname`. If we -want to swap these names and remove the comma to get a `Firstname -Lastname` format, we can use the following code: +El verdadero poder de usar expresiones regulares con `replace` viene del +hecho de que podemos referirnos a grupos coincidentes en la string de reemplazo. +Por ejemplo, supongamos que tenemos una gran string que contenga los nombres de +personas, un nombre por línea, en el formato `Apellido, Nombre`. Si +deseamos intercambiar estos nombres y eliminar la coma para obtener un +formato `Nombre Apellido`, podemos usar el siguiente código: ``` console.log( @@ -693,454 +699,457 @@ console.log( // Philip Wadler ``` -The `$1` and `$2` in the replacement string refer to the parenthesized -groups in the pattern. `$1` is replaced by the text that matched -against the first group, `$2` by the second, and so on, up to `$9`. -The whole match can be referred to with `$&`. +Los `$1` y `$2` en el string de reemplazo se refieren a los grupos entre +paréntesis del patrón. `$1` se reemplaza por el texto que coincide +con el primer grupo, `$2` por el segundo, y así sucesivamente, hasta `$9`. +Puedes hacer referencia a la coincidencia completa con `$&`. {{index [function, "higher-order"], grouping, "capture group"}} -It is possible to pass a function—rather than a string—as the second -argument to `replace`. For each replacement, the function will be -called with the matched groups (as well as the whole match) as -arguments, and its return value will be inserted into the new string. +Es posible pasar una función, en lugar de un string, como segundo +argumento para `replace`. Para cada reemplazo, la función será +llamada con los grupos coincidentes (así como con la coincidencia completa) +como argumentos, y su valor de retorno se insertará en el nuevo string. -Here's a small example: +Aquí hay un pequeño ejemplo: ``` -let s = "the cia and fbi"; +let s = "la cia y el fbi"; console.log(s.replace(/\b(fbi|cia)\b/g, str => str.toUpperCase())); -// → the CIA and FBI +// → la CIA y el FBI ``` -Here's a more interesting one: +Y aquí hay uno más interesante: ``` -let stock = "1 lemon, 2 cabbages, and 101 eggs"; -function minusOne(match, amount, unit) { - amount = Number(amount) - 1; - if (amount == 1) { // only one left, remove the 's' - unit = unit.slice(0, unit.length - 1); - } else if (amount == 0) { - amount = "no"; +let almacen = "1 limon, 2 lechugas, y 101 huevos"; +function menosUno(coincidencia, cantidad, unidad) { + cantidad = Number(cantidad) - 1; + if (cantidad == 1) { // solo queda uno, remover la 's' + unidad = unidad.slice(0, unidad.length - 1); + } else if (cantidad == 0) { + cantidad = "sin"; } - return amount + " " + unit; + return cantidad + " " + unidad; } -console.log(stock.replace(/(\d+) (\w+)/g, minusOne)); -// → no lemon, 1 cabbage, and 100 eggs +console.log(almacen.replace(/(\d+) (\w+)/g, menosUno)); +// → sin limon, 1 lechuga, y 100 huevos ``` -This takes a string, finds all occurrences of a number followed by an -alphanumeric word, and returns a string wherein every such occurrence -is decremented by one. +Esta función toma un string, encuentra todas las ocurrencias de un número +seguido de una palabra alfanumérica, y retorna un string en la que cada +ocurrencia es decrementada por uno. -The `(\d+)` group ends up as the `amount` argument to the function, -and the `(\w+)` group gets bound to `unit`. The function converts -`amount` to a number—which always works since it matched `\d+`—and -makes some adjustments in case there is only one or zero left. +El grupo `(\d+)` termina como el argumento `cantidad` para la función, +y el grupo `(\w+)` se vincula a `unidad`. La función convierte +`cantidad` a un número—lo que siempre funciona, ya que coincidio con `\d+`—y +realiza algunos ajustes en caso de que solo quede uno o cero. -## Greed +## Codicia {{index greed, "regular expression"}} -It is possible to use `replace` to write a function that removes all -((comment))s from a piece of JavaScript ((code)). Here is a first -attempt: +Es posible usar `replace` para escribir una función que elimine todo los +((comentario))s de un fragmento de ((código)) JavaScript. Aquí hay un primer +intento: ```{test: wrap} -function stripComments(code) { - return code.replace(/\/\/.*|\/\*[^]*\*\//g, ""); +function removerComentarios(codigo) { + return codigo.replace(/\/\/.*|\/\*[^]*\*\//g, ""); } -console.log(stripComments("1 + /* 2 */3")); +console.log(removerComentarios("1 + /* 2 */3")); // → 1 + 3 -console.log(stripComments("x = 10;// ten!")); +console.log(removerComentarios("x = 10;// ten!")); // → x = 10; -console.log(stripComments("1 /* a */+/* b */ 1")); +console.log(removerComentarios("1 /* a */+/* b */ 1")); // → 1 1 ``` {{index "period character", "slash character", "newline character", "empty set", "block comment", "line comment"}} -The part before the _or_ operator matches two slash characters -followed by any number of non-newline characters. The part for -multiline comments is more involved. We use `[^]` (any character that -is not in the empty set of characters) as a way to match any -character. We cannot just use a period here because block comments can -continue on a new line, and the period character does not match -newline characters. +La parte anterior al operador _o_ coincide con dos caracteres de barra inclinada +seguido de cualquier número de caracteres que no sean nuevas lineas. La parte +para los comentarios de líneas múltiples es más complicado. Usamos `[^]` +(cualquier caracter que no está en el conjunto de caracteres vacíos) +como una forma de unir cualquier caracter. No podemos simplemente usar un +punto aquí porque los comentarios de bloque pueden continuar en una nueva +línea, y el carácter del período no coincide con caracteres de nuevas lineas. -But the output for the last line appears to have gone wrong. Why? +Pero la salida de la última línea parece haber salido mal. Por qué? {{index backtracking, greed, "regular expression"}} -The `[^]*` part of the expression, as I described in the section on -backtracking, will first match as much as it can. If that causes the -next part of the pattern to fail, the matcher moves back one character -and tries again from there. In the example, the matcher first tries to -match the whole rest of the string and then moves back from there. It -will find an occurrence of `*/` after going back four characters and -match that. This is not what we wanted—the intention was to match a -single comment, not to go all the way to the end of the code and find -the end of the last block comment. - -Because of this behavior, we say the repetition operators (`+`, `*`, -`?`, and `{}`) are _((greed))y_, meaning they match as much as they -can and backtrack from there. If you put a ((question mark)) after -them (`+?`, `*?`, `??`, `{}?`), they become nongreedy and start by -matching as little as possible, matching more only when the remaining -pattern does not fit the smaller match. - -And that is exactly what we want in this case. By having the star -match the smallest stretch of characters that brings us to a `*/`, we -consume one block comment and nothing more. +La parte `[^]*` de la expresión, como describí en la sección +retroceder, primero coincidirá tanto como sea posible. Si eso causa un falo en +la siguiente parte del patrón, el emparejador retrocede un caracter +e intenta nuevamente desde allí. En el ejemplo, el emparejador primero intenta +emparejar el resto del string y luego se mueve hacia atrás desde allí. Este +encontrará una ocurrencia de `*/` después de retroceder cuatro caracteres y +emparejar eso. Esto no es lo que queríamos, la intención era hacer coincidir un +solo comentario, no ir hasta el final del código y encontrar +el final del último comentario de bloque. + +Debido a este comportamiento, decimos que los operadores de repetición (`+`, `*`, +`?` y `{}`) son _ ((codiciosos)), lo que significa que coinciden con tanto como +pueden y retroceden desde allí. Si colocas un ((signo de interrogación)) después +de ellos (`+?`, `*?`, `??`, `{}?`), se vuelven no-codiciosos y comienzan a +hacer coincidir lo menos posible, haciendo coincidir más solo cuando el +patrón restante no se ajuste a la coincidencia más pequeña. + +Y eso es exactamente lo que queremos en este caso. Al hacer que la estrella +coincida con el tramo más pequeño de caracteres que nos lleve a un `*/`, +consumimos un comentario de bloque y nada más. ```{test: wrap} -function stripComments(code) { - return code.replace(/\/\/.*|\/\*[^]*?\*\//g, ""); +function removerComentarios(codigo) { + return codigo.replace(/\/\/.*|\/\*[^]*?\*\//g, ""); } -console.log(stripComments("1 /* a */+/* b */ 1")); +console.log(removerComentarios("1 /* a */+/* b */ 1")); // → 1 + 1 ``` -A lot of ((bug))s in ((regular expression)) programs can be traced to -unintentionally using a greedy operator where a nongreedy one would -work better. When using a ((repetition)) operator, consider the -nongreedy variant first. +Una gran cantidad de ((errores)) en los programas de ((expresiones regulares)) +se pueden rastrear a intencionalmente usar un operador codicioso, donde uno +que no sea codicioso trabajaria mejor. Al usar un operador de ((repetición)), +considera la variante no-codiciosa primero. -## Dynamically creating RegExp objects +## Creando objetos RegExp dinámicamente {{index ["regular expression", creation], "underscore character", "RegExp class"}} -There are cases where you might not know the exact ((pattern)) you -need to match against when you are writing your code. Say you want to -look for the user's name in a piece of text and enclose it in -underscore characters to make it stand out. Since you will know the -name only once the program is actually running, you can't use the -slash-based notation. +Hay casos en los que quizás no sepas el ((patrón)) exacto +necesario para coincidir cuando estes escribiendo tu código. Imagina +que quieres buscar el nombre del usuario en un texto y encerrarlo en +caracteres de subrayado para que se destaque. Como solo sabrás el +nombrar una vez que el programa se está ejecutando realmente, no puedes +usar la notación basada en barras. -But you can build up a string and use the `RegExp` ((constructor)) on -that. Here's an example: +Pero puedes construir un string y usar el ((constructor)) `RegExp` en el. +Aquí hay un ejemplo: ``` -let name = "harry"; -let text = "Harry is a suspicious character."; -let regexp = new RegExp("\\b(" + name + ")\\b", "gi"); -console.log(text.replace(regexp, "_$1_")); -// → _Harry_ is a suspicious character. +let nombre = "harry"; +let texto = "Harry es un personaje sospechoso."; +let regexp = new RegExp("\\b(" + nombre + ")\\b", "gi"); +console.log(texto.replace(regexp, "_$1_")); +// → _Harry_ es un personaje sospechoso. ``` -{{index ["regular expression", flags], ["backslash character", "in regular expressions"]}} +{{index ["regular expression", flags], "backslash character"}} -When creating the `\b` ((boundary)) markers, we have to use two -backslashes because we are writing them in a normal string, not a -slash-enclosed regular expression. The second argument to the `RegExp` -constructor contains the options for the regular expression—in this -case, `"gi"` for global and case insensitive. +Al crear los marcadores de ((límite)) `\b`, tenemos que usar dos +barras invertidas porque las estamos escribiendo en un string normal, no en una +expresión regular contenida en barras. El segundo argumento para el +constructor `RegExp` contiene las opciones para la expresión regular—en este +caso, `"gi"` para global e insensible a mayúsculas y minúsculas. -But what if the name is `"dea+hl[]rd"` because our user is a ((nerd))y -teenager? That would result in a nonsensical regular expression that -won't actually match the user's name. +Pero, y si el nombre es `"dea+hl[]rd"` porque nuestro usuario es un ((nerd)) +adolescente? Eso daría como resultado una expresión regular sin sentido que +en realidad no coincidirá con el nombre del usuario. -{{index ["backslash character", "in regular expressions"], [escaping, "in regexps"], ["regular expression", escaping]}} +{{index "backslash character", [escaping, "in regexps"], ["regular expression", escaping]}} -To work around this, we can add backslashes before any character that -has a special meaning. +Para solucionar esto, podemos agregar barras diagonales inversas antes de +cualquier caracter que tenga un significado especial. ``` -let name = "dea+hl[]rd"; -let text = "This dea+hl[]rd guy is super annoying."; -let escaped = name.replace(/[\\[.+*?(){|^$]/g, "\\$&"); -let regexp = new RegExp("\\b" + escaped + "\\b", "gi"); -console.log(text.replace(regexp, "_$&_")); -// → This _dea+hl[]rd_ guy is super annoying. +let nombre = "dea+hl[]rd"; +let texto = "Este sujeto dea+hl[]rd es super fastidioso."; +let escapados = nombre.replace(/[\\[.+*?(){|^$]/g, "\\$&"); +let regexp = new RegExp("\\b" + escapados + "\\b", "gi"); +console.log(texto.replace(regexp, "_$&_")); +// → Este sujeto _dea+hl[]rd_ es super fastidioso. ``` -## The search method +## El método search -{{index ["regular expression", methods], "indexOf method", "search method"}} +{{index searching, ["regular expression", methods], "indexOf method", "search method"}} -The `indexOf` method on strings cannot be called with a regular -expression. But there is another method, `search`, that does expect a -regular expression. Like `indexOf`, it returns the first index on -which the expression was found, or -1 when it wasn't found. +El método `indexOf` en strings no puede invocarse con una expresión regular. +Pero hay otro método, `search` ("buscar"), que espera una expresión regular. +Al igual que `indexOf`, retorna el primer índice en +que se encontró la expresión, o -1 cuando no se encontró. ``` -console.log(" word".search(/\S/)); +console.log(" palabra".search(/\S/)); // → 2 console.log(" ".search(/\S/)); // → -1 ``` -Unfortunately, there is no way to indicate that the match should start -at a given offset (like we can with the second argument to `indexOf`), -which would often be useful. +Desafortunadamente, no hay forma de indicar que la coincidencia debería comenzar +a partir de un desplazamiento dado (como podemos con el segundo argumento para +`indexOf`), que a menudo sería útil. -## The lastIndex property +## La propiedad lastIndex {{index "exec method", "regular expression"}} -The `exec` method similarly does not provide a convenient way to start -searching from a given position in the string. But it does provide an -*in*convenient way. +De manera similar el método `exec` no proporciona una manera conveniente de +comenzar buscando desde una posición dada en el string. Pero proporciona una +manera *in*conveniente. -{{index ["regular expression", matching], matching, "source property", "lastIndex property"}} +{{index ["regular expression", matching], matching, "source property", "lastIndex property*"}} -Regular expression objects have properties. One such property is -`source`, which contains the string that expression was created from. -Another property is `lastIndex`, which controls, in some limited -circumstances, where the next match will start. +Los objetos de expresión regular tienen propiedades. Una de esas propiedades es +`source` ("fuente"), que contiene el string de donde se creó la expresión. +Otra propiedad es `lastIndex` ("ultimoIndice"), que controla, en algunas +circunstancias limitadas, donde comenzará la siguiente coincidencia. {{index [interface, design], "exec method", ["regular expression", global]}} -Those circumstances are that the regular expression must have the -global (`g`) or sticky (`y`) option enabled, and the match must happen -through the `exec` method. Again, a less confusing solution would have -been to just allow an extra argument to be passed to `exec`, but -confusion is an essential feature of JavaScript's regular expression -interface. +Esas circunstancias son que la expresión regular debe tener la opción global +(`g`) o adhesiva (`y`) habilitada, y la coincidencia debe suceder +a través del método `exec`. De nuevo, una solución menos confusa hubiese +sido permitir que un argumento adicional fuera pasado a `exec`, pero +la confusión es una característica esencial de la interfaz de las +expresiones regulares de JavaScript. ``` -let pattern = /y/g; -pattern.lastIndex = 3; -let match = pattern.exec("xyzzy"); -console.log(match.index); +let patron = /y/g; +patron.lastIndex = 3; +let coincidencia = patron.exec("xyzzy"); +console.log(coincidencia.index); // → 4 -console.log(pattern.lastIndex); +console.log(patron.lastIndex); // → 5 ``` {{index "side effect", "lastIndex property"}} -If the match was successful, the call to `exec` automatically updates -the `lastIndex` property to point after the match. If no match was -found, `lastIndex` is set back to zero, which is also the value it has -in a newly constructed regular expression object. +Si la coincidencia fue exitosa, la llamada a `exec` actualiza automáticamente +a la propiedad `lastIndex` para que apunte después de la coincidencia. Si no +se encontraron coincidencias, `lastIndex` vuelve a cero, que es también el +valor que tiene un objeto de expresión regular recién construido. -The difference between the global and the sticky options is that, when -sticky is enabled, the match will succeed only if it starts directly -at `lastIndex`, whereas with global, it will search ahead for a -position where a match can start. +La diferencia entre las opciones globales y las adhesivas es que, cuandoa +adhesivo está habilitado, la coincidencia solo tendrá éxito si comienza +directamente en `lastIndex`, mientras que con global, buscará una +posición donde pueda comenzar una coincidencia. ``` let global = /abc/g; console.log(global.exec("xyz abc")); // → ["abc"] -let sticky = /abc/y; -console.log(sticky.exec("xyz abc")); +let adhesivo = /abc/y; +console.log(adhesivo.exec("xyz abc")); // → null ``` {{index bug}} -When using a shared regular expression value for multiple `exec` -calls, these automatic updates to the `lastIndex` property can cause -problems. Your regular expression might be accidentally starting at an -index that was left over from a previous call. +Cuando se usa un valor de expresión regular compartido para múltiples llamadas +a `exec`, estas actualizaciones automáticas a la propiedad `lastIndex` pueden +causar problemas. Tu expresión regular podría estar accidentalmente comenzando +en un índice que quedó de una llamada anterior. ``` -let digit = /\d/g; -console.log(digit.exec("here it is: 1")); +let digito = /\d/g; +console.log(digito.exec("aqui esta: 1")); // → ["1"] -console.log(digit.exec("and now: 1")); +console.log(digito.exec("y ahora: 1")); // → null ``` {{index ["regular expression", global], "match method"}} -Another interesting effect of the global option is that it changes the -way the `match` method on strings works. When called with a global -expression, instead of returning an array similar to that returned by -`exec`, `match` will find _all_ matches of the pattern in the string -and return an array containing the matched strings. +Otro efecto interesante de la opción global es que cambia la +forma en que funciona el método `match` en strings. Cuando se llama con una +expresión global, en lugar de retornar un matriz similar al retornado por +`exec`,` match` encontrará _todas_ las coincidencias del patrón en el string +y retornar un array que contiene los strings coincidentes. ``` console.log("Banana".match(/an/g)); // → ["an", "an"] ``` -So be cautious with global regular expressions. The cases where they -are necessary—calls to `replace` and places where you want to -explicitly use `lastIndex`—are typically the only places where you -want to use them. +Por lo tanto, ten cuidado con las expresiones regulares globales. Los casos +donde son necesarias—llamadas a `replace` y lugares donde deseas +explícitamente usar `lastIndex`—son generalmente los únicos lugares donde +querras usarlas. -### Looping over matches +### Ciclos sobre coincidencias {{index "lastIndex property", "exec method", loop}} -A common thing to do is to scan through all occurrences of a pattern -in a string, in a way that gives us access to the match object in the -loop body. We can do this by using `lastIndex` and `exec`. +Una cosa común que hacer es escanear todas las ocurrencias de un patrón +en un string, de una manera que nos de acceso al objeto de coincidencia en el +cuerpo del ciclo. Podemos hacer esto usando `lastIndex` y `exec`. ``` -let input = "A string with 3 numbers in it... 42 and 88."; -let number = /\b\d+\b/g; -let match; -while (match = number.exec(input)) { - console.log("Found", match[0], "at", match.index); +let entrada = "Un string con 3 numeros en el... 42 y 88."; +let numero = /\b\d+\b/g; +let coincidencia; +while (coincidencia = numero.exec(entrada)) { + console.log("Se encontro", coincidencia[0], "en", coincidencia.index); } -// → Found 3 at 14 -// Found 42 at 33 -// Found 88 at 40 +// → Se encontro 3 en 14 +// Se encontro 42 en 33 +// Se encontro 88 en 38 ``` -{{index "while loop", ["= operator", "as expression"], [binding, "as state"]}} +{{index "while loop", "= operator"}} -This makes use of the fact that the value of an ((assignment)) -expression (`=`) is the assigned value. So by using `match = -number.exec(input)` as the condition in the `while` statement, we -perform the match at the start of each iteration, save its result in a -binding, and stop looping when no more matches are found. +Esto hace uso del hecho de que el valor de una expresión de ((asignación)) (`=`) +es el valor asignado. Entonces al usar `coincidencia = numero.exec(entrada)` +como la condición en la declaración `while`, +realizamos la coincidencia al inicio de cada iteración, guardamos +su resultado en una ((vinculación)), y terminamos de repetir cuando no se +encuentran más coincidencias. {{id ini}} -## Parsing an INI file +## Análisis de un archivo INI {{index comment, "file format", "enemies example", "INI file"}} -To conclude the chapter, we'll look at a problem that calls for -((regular expression))s. Imagine we are writing a program to -automatically collect information about our enemies from the -((Internet)). (We will not actually write that program here, just the -part that reads the ((configuration)) file. Sorry.) The configuration -file looks like this: +Para concluir el capítulo, veremos un problema que requiere de +((expresiones regulares)). Imagina que estamos escribiendo un programa para +recolectar automáticamente información sobre nuestros enemigos de el +((Internet)). (No escribiremos ese programa aquí, solo la +parte que lee el archivo de ((configuración)). Lo siento.) El archivo +de configuración se ve así: ```{lang: "text/plain"} -searchengine=https://duckduckgo.com/?q=$1 -spitefulness=9.7 +motordebusqueda=https://duckduckgo.com/?q=$1 +malevolencia=9.7 + +; los comentarios estan precedidos por un punto y coma... +; cada seccion contiene un enemigo individual -; comments are preceded by a semicolon... -; each section concerns an individual enemy [larry] -fullname=Larry Doe -type=kindergarten bully -website=http://www.geocities.com/CapeCanaveral/11451 +nombrecompleto=Larry Doe +tipo=bravucon del preescolar +sitioweb=http://www.geocities.com/CapeCanaveral/11451 [davaeorn] -fullname=Davaeorn -type=evil wizard -outputdir=/home/marijn/enemies/davaeorn +nombrecompleto=Davaeorn +tipo=hechizero malvado +directoriosalida=/home/marijn/enemies/davaeorn ``` {{index grammar}} -The exact rules for this format (which is a widely used format, -usually called an _INI_ file) are as follows: +Las reglas exactas para este formato (que es un formato ampliamente utilizado, +usualmente llamado un archivo _INI_) son las siguientes: -- Blank lines and lines starting with semicolons are ignored. +- Las líneas en blanco y líneas que comienzan con punto y coma se ignoran. -- Lines wrapped in `[` and `]` start a new ((section)). +- Las líneas envueltas en `[` y `]` comienzan una nueva ((sección)). -- Lines containing an alphanumeric identifier followed by an `=` - character add a setting to the current section. +- Líneas que contienen un identificador alfanumérico seguido de un + carácter `=` agregan una configuración a la sección actual. -- Anything else is invalid. +- Cualquier otra cosa no es válida. -Our task is to convert a string like this into an object whose -properties hold strings for settings written before the first -section header and subobjects for sections, with those subobjects -holding the section's settings. +Nuestra tarea es convertir un string como este en un objeto cuyas +propiedades contengas strings para configuraciones sin sección y sub-objetos +para secciones, con esos subobjetos conteniendo la configuración de la sección. {{index "carriage return", "line break", "newline character"}} -Since the format has to be processed ((line)) by line, splitting up -the file into separate lines is a good start. We saw -the `split` method in [Chapter ?](data#split). -Some operating systems, however, use not just a newline character to -separate lines but a carriage return character followed by a newline -(`"\r\n"`). Given that the `split` method also allows a regular -expression as its argument, we can use a regular expression like -`/\r?\n/` to split in a way that allows both `"\n"` and `"\r\n"` -between lines. +Dado que el formato debe procesarse ((línea)) por línea, dividir +el archivo en líneas separadas es un buen comienzo. Usamos `string.split("\n")` +para hacer esto en el [Capítulo 4](datos#split). Algunos sistemas operativos, +sin embargo, usan no solo un carácter de nueva línea para separar lineas +sino un carácter de retorno de carro seguido de una nueva línea +(`"\r\n"`). Dado que el método `split` también permite una expresión +regular como su argumento, podemos usar una expresión regular como +`/\r?\n/` para dividir el string de una manera que permita tanto `"\n"` como +`"\r\n"` entre líneas. ```{startCode: true} -function parseINI(string) { - // Start with an object to hold the top-level fields - let result = {}; - let section = result; - string.split(/\r?\n/).forEach(line => { - let match; - if (match = line.match(/^(\w+)=(.*)$/)) { - section[match[1]] = match[2]; - } else if (match = line.match(/^\[(.*)\]$/)) { - section = result[match[1]] = {}; - } else if (!/^\s*(;.*)?$/.test(line)) { - throw new Error("Line '" + line + "' is not valid."); +function analizarINI(string) { + // Comenzar con un objeto para mantener los campos de nivel superior + let resultado = {}; + let seccion = resultado; + string.split(/\r?\n/).forEach(linea => { + let coincidencia; + if (coincidencia = linea.match(/^(\w+)=(.*)$/)) { + seccion[coincidencia[1]] = coincidencia[2]; + } else if (coincidencia = linea.match(/^\[(.*)\]$/)) { + seccion = resultado[coincidencia[1]] = {}; + } else if (!/^\s*(;.*)?$/.test(linea)) { + throw new Error("Linea '" + linea + "' no es valida."); } }); - return result; + return resultado; } -console.log(parseINI(` -name=Vasilis -[address] -city=Tessaloniki`)); -// → {name: "Vasilis", address: {city: "Tessaloniki"}} +console.log(analizarINI(` +nombre=Vasilis +[direccion] +ciudad=Tessaloniki`)); +// → {nombre: "Vasilis", direccion: {ciudad: "Tessaloniki"}} ``` {{index "parseINI function", parsing}} -The code goes over the file's lines and builds up an object. -Properties at the top are stored directly into that object, whereas -properties found in sections are stored in a separate section object. -The `section` binding points at the object for the current section. +El código pasa por las líneas del archivo y crea un objeto. +Las propiedades en la parte superior se almacenan directamente en ese objeto, +mientras que las propiedades que se encuentran en las secciones se almacenan +en un objeto de sección separado. La vinculación `sección` apunta al +objeto para la sección actual. -There are two kinds of significant lines—section headers or property -lines. When a line is a regular property, it is stored in the current -section. When it is a section header, a new section object is created, -and `section` is set to point at it. +Hay dos tipos de de líneas significativas—encabezados de seccion o lineas de +propiedades. Cuando una línea es una propiedad regular, esta se almacena en la +sección actual. Cuando se trata de un encabezado de sección, se crea un nuevo +objeto de sección, y `seccion` se configura para apuntar a él. {{index "caret character", "dollar sign", boundary}} -Note the recurring use of `^` and `$` to make sure the expression -matches the whole line, not just part of it. Leaving these out results -in code that mostly works but behaves strangely for some input, which -can be a difficult bug to track down. - -{{index "if keyword", assignment, ["= operator", "as expression"]}} +Nota el uso recurrente de `^` y `$` para asegurarse de que la expresión +coincida con toda la línea, no solo con parte de ella. Dejando afuera +estos resultados en código que funciona principalmente, pero que se comporta +de forma extraña para algunas entradas, lo que puede ser un error difícil de +rastrear. -The pattern `if (match = string.match(...))` is similar to the trick -of using an assignment as the condition for `while`. You often aren't -sure that your call to `match` will succeed, so you can access the -resulting object only inside an `if` statement that tests for this. To -not break the pleasant chain of `else if` forms, we assign the result -of the match to a binding and immediately use that assignment as the -test for the `if` statement. +{{index "if keyword", assignment, "= operator"}} -{{index [parentheses, "in regular expressions"]}} +El patrón `if (coincidencia = string.match (...))` es similar al truco +de usar una asignación como condición para `while`. A menudo no estas +seguro de que tu llamada a `match` tendrá éxito, para que puedas acceder al +objeto resultante solo dentro de una declaración `if` que pruebe esto. Para +no romper la agradable cadena de las formas `else if`, asignamos el resultado +de la coincidencia a una vinculación e inmediatamente usamos esa asignación +como la prueba para la declaración `if`. -If a line is not a section header or a property, the function checks -whether it is a comment or an empty line using the expression -`/^\s*(;.*)?$/`. Do you see how it works? The part between the -parentheses will match comments, and the `?` makes sure it also -matches lines containing only whitespace. When a line doesn't match -any of the expected forms, the function throws an exception. +Si una línea no es un encabezado de sección o una propiedad, la función verifica +si es un comentario o una línea vacía usando la expresión +`/^\s*(;.*)?$/`. Ves cómo funciona? La parte entre los ((paréntesis)) +coincidirá con los comentarios, y el `?` asegura que también +coincida con líneas que contengan solo espacios en blanco. Cuando una línea +no coincida con cualquiera de las formas esperadas, la función arroja una +excepción. -## International characters +## Caracteres internacionales {{index internationalization, Unicode, ["regular expression", internationalization]}} -Because of JavaScript's initial simplistic implementation and the fact -that this simplistic approach was later set in stone as ((standard)) -behavior, JavaScript's regular expressions are rather dumb about -characters that do not appear in the English language. For example, as -far as JavaScript's regular expressions are concerned, a "((word -character))" is only one of the 26 characters in the Latin alphabet -(uppercase or lowercase), decimal digits, and, for some reason, the -underscore character. Things like _é_ or _β_, which most definitely -are word characters, will not match `\w` (and _will_ match uppercase -`\W`, the nonword category). +Debido a la simplista implementación inicial de JavaScript y al hecho de +que este enfoque simplista fue luego establecido en piedra como comportamiento +((estándar)), las expresiones regulares de JavaScript son bastante tontas +acerca de los caracteres que no aparecen en el idioma inglés. Por ejemplo, en +cuanto a las expresiones regulares de JavaScript, una "((palabra +caracter))" es solo uno de los 26 caracteres en el alfabeto latino +(mayúsculas o minúsculas), dígitos decimales, y, por alguna razón, el +carácter de guion bajo. Cosas como _é_ o _β_, que definitivamente +son caracteres de palabras, no coincidirán con `\w` (y _si_ +coincidiran con `\W` mayúscula, la categoría no-palabra). -{{index [whitespace, matching]}} +{{index whitespace}} -By a strange historical accident, `\s` (whitespace) does not have this -problem and matches all characters that the Unicode standard considers -whitespace, including things like the ((nonbreaking space)) and the -((Mongolian vowel separator)). +Por un extraño accidente histórico, `\s` (espacio en blanco) no tiene este +problema y coincide con todos los caracteres que el estándar Unicode considera +espacios en blanco, incluyendo cosas como el (espacio de no separación) y el +((Separador de vocales Mongol)). -Another problem is that, by default, regular expressions work on code -units, as discussed in [Chapter ?](higher_order#code_units), not -actual characters. This means characters that are composed of two -code units behave strangely. +Otro problema es que, de forma predeterminada, las expresiones regulares +funcionan en unidades del código, como se discute en el +[Capítulo 5](orden_superior#unidades_del_codigo), no en +caracteres reales. Esto significa que los caracteres que estan compustos +de dos unidades de código se comportan de manera extraña. ``` console.log(/🍎{3}/.test("🍎🍎🍎")); @@ -1151,22 +1160,23 @@ console.log(/<.>/u.test("<🌹>")); // → true ``` -The problem is that the 🍎 in the first line is treated as two code -units, and the `{3}` part is applied only to the second one. -Similarly, the dot matches a single code unit, not the two that make -up the rose ((emoji)). +El problema es que la 🍎 en la primera línea se trata como dos unidades de +código, y la parte `{3}` se aplica solo a la segunda. Del mismo modo, +el punto coincide con una sola unidad de código, no con las dos que componen +al ((emoji)) de rosa. -You must add a `u` option (for ((Unicode))) to your regular -expression to make it treat such characters properly. The wrong -behavior remains the default, unfortunately, because changing that -might cause problems for existing code that depends on it. +Debe agregar una opción `u` (para ((Unicode))) a tu expresión regular +para hacerla tratar a tales caracteres apropiadamente. El comportamiento +incorrecto sigue siendo el predeterminado, desafortunadamente, porque cambiarlo +podría causar problemas en código ya existente que depende de él. {{index "character category", [Unicode, property]}} -Though this was only just standardized and is, at the time of writing, -not widely supported yet, it is possible to use `\p` in a regular -expression (that must have the Unicode option enabled) to match all -characters to which the Unicode standard assigns a given property. +Aunque esto solo se acaba de estandarizar y aun no es, al momento de escribir +este libro, ampliamente compatible con muchs nabegadores, es posible usar +`\p` en una expresión regular (que debe tener habilitada la opción Unicode) +para que coincida con todos los caracteres a los que el estándar Unicode +lis asigna una propiedad determinada. ```{test: never} console.log(/\p{Script=Greek}/u.test("α")); @@ -1179,174 +1189,175 @@ console.log(/\p{Alphabetic}/u.test("!")); // → false ``` -Unicode defines a number of useful properties, though finding the one -that you need may not always be trivial. You can use the -`\p{Property=Value}` notation to match any character that has the -given value for that property. If the property name is left off, as in -`\p{Name}`, the name is assumed to be either a binary property such as -`Alphabetic` or a category such as `Number`. +Unicode define una cantidad de propiedades útiles, aunque encontrar la +que necesitas puede no ser siempre trivial. Puedes usar la notación +`\p{Property=Value}` para hacer coincidir con cualquier carácter que tenga el +valor dado para esa propiedad. Si el nombre de la propiedad se deja afuera, +como en `\p{Name}`, se asume que el nombre es una propiedad binaria como +`Alfabético` o una categoría como `Número`. {{id summary_regexp}} -## Summary +## Resumen -Regular expressions are objects that represent patterns in strings. -They use their own language to express these patterns. +Las expresiones regulares son objetos que representan patrones en strings. +Ellas usan su propio lenguaje para expresar estos patrones. {{table {cols: [1, 5]}}} -| `/abc/` | A sequence of characters -| `/[abc]/` | Any character from a set of characters -| `/[^abc]/` | Any character _not_ in a set of characters -| `/[0-9]/` | Any character in a range of characters -| `/x+/` | One or more occurrences of the pattern `x` -| `/x+?/` | One or more occurrences, nongreedy -| `/x*/` | Zero or more occurrences -| `/x?/` | Zero or one occurrence -| `/x{2,4}/` | Two to four occurrences -| `/(abc)/` | A group -| `/a|b|c/` | Any one of several patterns -| `/\d/` | Any digit character -| `/\w/` | An alphanumeric character ("word character") -| `/\s/` | Any whitespace character -| `/./` | Any character except newlines -| `/\b/` | A word boundary -| `/^/` | Start of input -| `/$/` | End of input - -A regular expression has a method `test` to test whether a given -string matches it. It also has a method `exec` that, when a match is -found, returns an array containing all matched groups. Such an array -has an `index` property that indicates where the match started. - -Strings have a `match` method to match them against a regular -expression and a `search` method to search for one, returning only the -starting position of the match. Their `replace` method can replace -matches of a pattern with a replacement string or function. - -Regular expressions can have options, which are written after the -closing slash. The `i` option makes the match case insensitive. The -`g` option makes the expression _global_, which, among other things, -causes the `replace` method to replace all instances instead of just -the first. The `y` option makes it sticky, which means that it will -not search ahead and skip part of the string when looking for a match. -The `u` option turns on Unicode mode, which fixes a number of problems -around the handling of characters that take up two code units. - -Regular expressions are a sharp ((tool)) with an awkward handle. They -simplify some tasks tremendously but can quickly become unmanageable -when applied to complex problems. Part of knowing how to use them is -resisting the urge to try to shoehorn things that they cannot cleanly -express into them. - -## Exercises +| `/abc/` | Una secuencia de caracteres +| `/[abc]/` | Cualquier caracter de un conjunto de caracteres +| `/[^abc]/` | Cualquier carácter que _no_ este en un conjunto de caracteres +| `/[0-9]/` | Cualquier caracter en un rango de caracteres +| `/x+/` | Una o más ocurrencias del patrón `x` +| `/x+?/` | Una o más ocurrencias, no codiciosas +| `/x*/` | Cero o más ocurrencias +| `/x?/` | Cero o una ocurrencia +| `/x{2,4}/` | De dos a cuatro ocurrencias +| `/(abc)/` | Un grupo +| `/a|b|c/` | Cualquiera de varios patrones +| `/\d/` | Cualquier caracter de digito +| `/\w/` | Un caracter alfanumérico ("carácter de palabra") +| `/\s/` | Cualquier caracter de espacio en blanco +| `/./` | Cualquier caracter excepto líneas nuevas +| `/\b/` | Un límite de palabra +| `/^/` | Inicio de entrada +| `/$/` | Fin de la entrada + +Una expresión regular tiene un método `test` para probar si una determinada +string coincide cn ella. También tiene un método `exec` que, cuando una +coincidencia es encontrada, retorna un array que contiene todos los grupos +que coincidieron. Tal array tiene una propiedad `index` que indica en +dónde comenzó la coincidencia. + +Los strings tienen un método `match` para coincidirlas con una expresión +regular y un método `search` para buscar por una, retornando solo la +posición inicial de la coincidencia. Su método `replace` puede reemplazar +coincidencias de un patrón con un string o función de reemplazo. + +Las expresiones regulares pueden tener opciones, que se escriben después de la +barra que cierra la expresión. La opción `i` hace que la coincidencia no +distinga entre mayúsculas y minúsculas. La opción `g` hace que la expresión sea +_global_, que, entre otras cosas, hace que el método `replace` reemplace todas +las instancias en lugar de solo la primera. La opción `y` la hace adhesivo, +lo que significa que hará que no busque con anticipación y omita la parte del +string cuando busque una coincidencia. La opción `u` activa el modo Unicode, +lo que soluciona varios problemas alrededor del manejo de caracteres que +toman dos unidades de código. + +Las expresiones regulares son ((herramientas)) afiladas con un manejo incómodo. +Ellas simplifican algunas tareas enormemente, pero pueden volverse inmanejables +rápidamente cuando se aplican a problemas complejos. Parte de saber cómo +usarlas es resistiendo el impulso de tratar de calzar cosas que no pueden ser +expresadas limpiamente en ellas. + +## Ejercicios {{index debugging, bug}} -It is almost unavoidable that, in the course of working on these -exercises, you will get confused and frustrated by some regular -expression's inexplicable ((behavior)). Sometimes it helps to enter -your expression into an online tool like -[_https://debuggex.com_](https://www.debuggex.com/) to see whether its -visualization corresponds to what you intended and to ((experiment)) -with the way it responds to various input strings. +Es casi inevitable que, durante el curso de trabajar en estos +ejercicios, te sentiras confundido y frustrado por el ((comportamiento)) +inexplicable de alguna regular expresión. A veces ayuda ingresar +tu expresión en una herramienta en línea como +[_debuggex.com_](https://www.debuggex.com/) para ver si +su visualización corresponde a lo que pretendías y a ((experimentar)) +con la forma en que responde a varios strings de entrada. -### Regexp golf +### Golf Regexp {{index "program size", "code golf", "regexp golf (exercise)"}} -_Code golf_ is a term used for the game of trying to express a -particular program in as few characters as possible. Similarly, -_regexp golf_ is the practice of writing as tiny a regular expression -as possible to match a given pattern, and _only_ that pattern. +_Golf de Codigo_ es un término usado para el juego de intentar expresar un +programa particular con el menor número de caracteres posible. Similarmente, +_Golf de Regexp_ es la práctica de escribir una expresión regular tan pequeña +como sea posible para que coincida con un patrón dado, y _sólo_ con ese patrón. {{index boundary, matching}} -For each of the following items, write a ((regular expression)) to -test whether any of the given substrings occur in a string. The -regular expression should match only strings containing one of the -substrings described. Do not worry about word boundaries unless -explicitly mentioned. When your expression works, see whether you can -make it any smaller. +Para cada uno de los siguientes elementos, escribe una ((expresión regular)) para +probar si alguna de las substrings dadas ocurre en un string. La +expresión regular debe coincidir solo con strings que contengan una de las +substrings descritas. No te preocupes por los límites de palabras a menos que +sean explícitamente mencionados. Cuando tu expresión funcione, ve si puedes +hacerla más pequeña. - 1. _car_ and _cat_ - 2. _pop_ and _prop_ - 3. _ferret_, _ferry_, and _ferrari_ - 4. Any word ending in _ious_ - 5. A whitespace character followed by a period, comma, colon, or semicolon - 6. A word longer than six letters - 7. A word without the letter _e_ (or _E_) + 1. _car_ y _cat_ + 2. _pop_ y _prop_ + 3. _ferret_, _ferry_, y _ferrari_ + 4. Cualquier palabra que termine _ious_ + 5. Un carácter de espacio en blanco seguido de un punto, coma, dos puntos o punto y coma + 6. Una palabra con mas de seis letras + 7. Una palabra sin la letra _e_ (o _E_) -Refer to the table in the [chapter summary](regexp#summary_regexp) for -help. Test each solution with a few test strings. +Consulta la tabla en el [resumen del capítulo](regexp#summary_regexp) para +ayudarte. Pruebe cada solución con algunos strings de prueba. {{if interactive ``` -// Fill in the regular expressions +// Llena con las expresiones regulares -verify(/.../, +verificar(/.../, ["my car", "bad cats"], ["camper", "high art"]); -verify(/.../, +verificar(/.../, ["pop culture", "mad props"], ["plop", "prrrop"]); -verify(/.../, +verificar(/.../, ["ferret", "ferry", "ferrari"], ["ferrum", "transfer A"]); -verify(/.../, +verificar(/.../, ["how delicious", "spacious room"], ["ruinous", "consciousness"]); -verify(/.../, +verificar(/.../, ["bad punctuation ."], ["escape the period"]); -verify(/.../, +verificar(/.../, ["hottentottententen"], ["no", "hotten totten tenten"]); -verify(/.../, +verificar(/.../, ["red platypus", "wobbling nest"], ["earth bed", "learning ape", "BEET"]); -function verify(regexp, yes, no) { - // Ignore unfinished exercises +function verificar(regexp, si, no) { + // Ignora ejercicios sin terminar if (regexp.source == "...") return; - for (let str of yes) if (!regexp.test(str)) { - console.log(`Failure to match '${str}'`); + for (let str of si) if (!regexp.test(str)) { + console.log(`Fallo al coincidir '${str}'`); } for (let str of no) if (regexp.test(str)) { - console.log(`Unexpected match for '${str}'`); + console.log(`Coincidencia inesperada para '${str}'`); } } ``` if}} -### Quoting style +### Estilo entre comillas {{index "quoting style (exercise)", "single-quote character", "double-quote character"}} -Imagine you have written a story and used single ((quotation mark))s -throughout to mark pieces of dialogue. Now you want to replace all the -dialogue quotes with double quotes, while keeping the single quotes -used in contractions like _aren't_. +Imagina que has escrito una historia y has utilizado ((comillas))s simples +en todas partes para marcar piezas de diálogo. Ahora quieres reemplazar todas +las comillas de diálogo con comillas dobles, pero manteniendo las comillas +simples usadas en contracciones como _aren't_. {{index "replace method"}} -Think of a pattern that distinguishes these two -kinds of quote usage and craft a call to the `replace` method that -does the proper replacement. +Piensa en un patrón que distinga de estos dos tipos de uso de citas y crea +una llamada al método `replace` que haga el reemplazo apropiado. {{if interactive ```{test: no} -let text = "'I'm the cook,' he said, 'it's my job.'"; -// Change this call. -console.log(text.replace(/A/g, "B")); +let texto = "'I'm the cook,' he said, 'it's my job.'"; +// Cambia esta llamada +console.log(texto.replace(/A/g, "B")); // → "I'm the cook," he said, "it's my job." ``` if}} @@ -1355,48 +1366,48 @@ if}} {{index "quoting style (exercise)", boundary}} -The most obvious solution is to replace only quotes with a nonword -character on at least one side—something like `/\W'|'\W/`. But you -also have to take the start and end of the line into account. +La solución más obvia es solo reemplazar las citas con una palabra no +personaje en al menos un lado. Algo como `/\W'|'\W/`. Pero +también debes tener en cuenta el inicio y el final de la línea. -{{index grouping, "replace method", [parentheses, "in regular expressions"]}} +{{index grouping, "replace method"}} -In addition, you must ensure that the replacement also includes the -characters that were matched by the `\W` pattern so that those are not -dropped. This can be done by wrapping them in parentheses and -including their groups in the replacement string (`$1`, `$2`). Groups -that are not matched will be replaced by nothing. +Además, debes asegurarte de que el reemplazo también incluya los +caracteres que coincidieron con el patrón `\W` para que estos no sean +dejados. Esto se puede hacer envolviéndolos en ((paréntesis)) e +incluyendo sus grupos en la cadena de reemplazo (`$1`,`$2`). Los grupos +que no están emparejados serán reemplazados por nada. hint}} -### Numbers again +### Números otra vez -{{index sign, "fractional number", [syntax, number], minus, "plus character", exponent, "scientific notation", "period character"}} +{{index sign, "fractional number", syntax, minus, "plus character", exponent, "scientific notation", "period character"}} -Write an expression that matches only JavaScript-style ((number))s. It -must support an optional minus _or_ plus sign in front of the number, -the decimal dot, and exponent notation—`5e-3` or `1E10`—again with an -optional sign in front of the exponent. Also note that it is not -necessary for there to be digits in front of or after the dot, but the -number cannot be a dot alone. That is, `.5` and `5.` are valid -JavaScript numbers, but a lone dot _isn't_. +Escribe una expresión que coincida solo con el estilo de ((número))s en +JavaScript. Esta debe admitir un signo opcional menos _o_ más delante del número, +el punto decimal, y la notación de exponente—`5e-3` o `1E10`— nuevamente con un +signo opcional en frente del exponente. También ten en cuenta que no es +necesario que hayan dígitos delante o detrás del punto, pero el +el número no puede ser solo un punto. Es decir, `.5` y `5.` son numeros válidos +de JavaScript, pero solo un punto _no_ lo es. {{if interactive ```{test: no} -// Fill in this regular expression. -let number = /^...$/; +// Completa esta expresión regular. +let numero = /^...$/; -// Tests: +// Pruebas: for (let str of ["1", "-1", "+15", "1.55", ".5", "5.", "1.3e2", "1E-4", "1e+12"]) { - if (!number.test(str)) { - console.log(`Failed to match '${str}'`); + if (!numero.test(str)) { + console.log(`Fallo al coincidir '${str}'`); } } for (let str of ["1a", "+-1", "1.2.3", "1+1", "1e4.5", ".5.", "1f5", "."]) { - if (number.test(str)) { - console.log(`Incorrectly accepted '${str}'`); + if (numero.test(str)) { + console.log(`Incorrectamente acepto '${str}'`); } } ``` @@ -1405,25 +1416,25 @@ if}} {{hint -{{index ["regular expression", escaping], ["backslash character", "in regular expressions"]}} +{{index ["regular expression", escaping], "backslash character"}} -First, do not forget the backslash in front of the period. +Primero, no te olvides de la barra invertida delante del punto. -Matching the optional ((sign)) in front of the ((number)), as well as -in front of the ((exponent)), can be done with `[+\-]?` or `(\+|-|)` -(plus, minus, or nothing). +Coincidir el ((signo)) opcional delante de el ((número)), así como +delante del ((exponente)), se puede hacer con `[+\-]?` o `(\+|-|)` +(más, menos o nada). {{index "pipe character"}} -The more complicated part of the exercise is the problem of matching -both `"5."` and `".5"` without also matching `"."`. For this, a good -solution is to use the `|` operator to separate the two cases—either -one or more digits optionally followed by a dot and zero or more -digits _or_ a dot followed by one or more digits. +La parte más complicada del ejercicio es el problema hacer coincidir +ambos `"5."` y `".5"` sin también coincidir coincidir con `"."`. Para esto, +una buena solución es usar el operador `|` para separar los dos casos—ya sea +uno o más dígitos seguidos opcionalmente por un punto y cero o más +dígitos _o_ un punto seguido de uno o más dígitos. {{index exponent, "case sensitivity", ["regular expression", flags]}} -Finally, to make the _e_ case insensitive, either add an `i` option to -the regular expression or use `[eE]`. +Finalmente, para hacer que la _e_ pueda ser mayuscula o minuscula, +agrega una opción `i` a la expresión regular o usa `[eE]`. hint}} diff --git a/10_modules.md b/10_modules.md index 530a29d36..58cf6f584 100644 --- a/10_modules.md +++ b/10_modules.md @@ -1,10 +1,10 @@ {{meta {load_files: ["code/packages_chapter_10.js", "code/chapter/07_robot.js"]}}} -# Modules +# Módulos {{quote {author: "Tef", title: "Programming is Terrible", chapter: true} -Write code that is easy to delete, not easy to extend. +Escriba código que sea fácil de borrar, no fácil de extender. quote}} @@ -12,257 +12,257 @@ quote}} {{figure {url: "img/chapter_picture_10.jpg", alt: "Picture of a building built from modular pieces", chapter: framed}}} -{{index organization, [code, "structure of"]}} +{{index organization, "code structure"}} -The ideal program has a crystal-clear structure. The way it works is -easy to explain, and each part plays a well-defined role. +El programa ideal tiene una estructura cristalina. La forma en que funciona es +fácil de explicar, y cada parte juega un papel bien definido. {{index "organic growth"}} -A typical real program grows organically. New pieces of functionality -are added as new needs come up. Structuring—and preserving -structure—is additional work. It's work that will pay off only in the -future, the _next_ time someone works on the program. So it is -tempting to neglect it and allow the parts of the program to become -deeply entangled. +Un típico programa real crece orgánicamente. Nuevas piezas de funcionalidad +se agregan a medida que surgen nuevas necesidades. Estructurar—y preservar la +estructura—es trabajo adicional, trabajo que solo valdra la pena en el +futuro, la _siguiente_ vez que alguien trabaje en el programa. Así que es +tentador descuidarlo, y permitir que las partes del programa se vuelvan +profundamente enredadas. {{index readability, reuse, isolation}} -This causes two practical issues. First, understanding such a system -is hard. If everything can touch everything else, it is difficult to -look at any given piece in isolation. You are forced to build up a -holistic understanding of the entire thing. Second, if you want to -use any of the functionality from such a program in another situation, -rewriting it may be easier than trying to disentangle it from its -context. +Esto causa dos problemas prácticos. En primer lugar, entender tal sistema +es difícil. Si todo puede tocar todo lo demás, es difícil ver a cualquier +pieza dada de forma aislada. Estas obligado a construir un +entendimiento holístico de todo el asunto. En segundo lugar, si quieres +usar cualquiera de las funcionalidades de dicho programa en otra situación, +reescribirla podria resultar más fácil que tratar de desenredarla de su +contexto. -The phrase "((big ball of mud))" is often used for such large, -structureless programs. Everything sticks together, and when you try -to pick out a piece, the whole thing comes apart, and your hands get -dirty. +El término "((gran bola de barro))" se usa a menudo para tales programas +grandes, sin estructura. Todo se mantiene pegado, y cuando intentas +sacar una pieza, todo se desarma y tus manos se ensucian. -## Modules +## Módulos -{{index dependency, [interface, module]}} +{{index dependency}} -_Modules_ are an attempt to avoid these problems. A ((module)) is a -piece of program that specifies which other pieces it relies on -and which functionality it provides for other modules -to use (its _interface_). +Los _módulos_ son un intento de evitar estos problemas. Un ((módulo)) es una +pieza del programa que especifica en qué otras piezas este depende ( +sus _dependencias_) y qué funcionalidad proporciona para que otros módulos +usen (su _((interfaz))_). {{index "big ball of mud"}} -Module interfaces have a lot in common with object interfaces, as we -saw them in [Chapter ?](object#interface). They make part of the -module available to the outside world and keep the rest private. By -restricting the ways in which modules interact with each other, the -system becomes more like ((LEGO)), where pieces interact through -well-defined connectors, and less like mud, where everything mixes -with everything. +Las interfaces de los módulos tienen mucho en común con las interfaces de +objetos, como las vimos en el [Capítulo 6](objeto#interface). Estas hacen parte +del módulo disponible para el mundo exterior y mantienen el resto privado. Al +restringir las formas en que los módulos interactúan entre sí, el +el sistema se parece más a un juego de ((LEGOS)), donde las piezas interactúan +a través de conectores bien definidos, y menos como barro, donde todo se mezcla +con todo. {{index dependency}} -The relations between modules are called _dependencies_. When a module -needs a piece from another module, it is said to depend on that -module. When this fact is clearly specified in the module itself, it -can be used to figure out which other modules need to be present to be -able to use a given module and to automatically load dependencies. +Las relaciones entre los módulos se llaman _dependencias_. Cuando un módulo +necesita una pieza de otro módulo, se dice que depende de ese +módulo. Cuando este hecho está claramente especificado en el módulo en sí, +puede usarse para descubrir qué otros módulos deben estar presentes para poder +ser capaces de usar un módulo dado y cargar dependencias automáticamente. -To separate modules in that way, each needs its own private ((scope)). +Para separar módulos de esa manera, cada uno necesita su propio alcance privado. -Just putting your JavaScript code into different ((file))s does not -satisfy these requirements. The files still share the same global -namespace. They can, intentionally or accidentally, interfere with -each other's bindings. And the dependency structure remains unclear. -We can do better, as we'll see later in the chapter. +Simplemente poner todo tu código JavaScript en diferentes ((archivo))s no +satisface estos requisitos. Los archivos aún comparten el mismo espacio de +nombres global. Pueden, intencionalmente o accidentalmente, interferir con +las vinculaciones de cada uno. Y la estructura de dependencia sigue sin +estar clara. Podemos hacerlo mejor, como veremos más adelante en el capítulo. {{index design}} -Designing a fitting module structure for a program can be difficult. -In the phase where you are still exploring the problem, trying -different things to see what works, you might want to not worry about -it too much since it can be a big distraction. Once you have -something that feels solid, that's a good time to take a step back and -organize it. +Diseñar una estructura de módulo ajustada para un programa puede ser difícil. +En la fase en la que todavía estás explorando el problema, intentando +cosas diferentes para ver que funciona, es posible que desees no preocuparte +demasiado por eso, ya que puede ser una gran distracción. Una vez que tengas +algo que se sienta sólido, es un buen momento para dar un paso atrás y +organizarlo. -## Packages +## Paquetes {{index bug, dependency, structure, reuse}} -One of the advantages of building a program out of separate pieces, -and being actually able to run those pieces on their own, is that you -might be able to apply the same piece in different programs. +Una de las ventajas de construir un programa a partir de piezas separadas, +y ser capaces de ejecutar esas piezas por si mismas, es que tú +podrías ser capaz de aplicar la misma pieza en diferentes programas. {{index "parseINI function"}} -But how do you set this up? Say I want to use the `parseINI` function -from [Chapter ?](regexp#ini) in another program. If it is clear what -the function depends on (in this case, nothing), I can just copy all the -necessary code into my new project and use it. But then, if I find a -mistake in that code, I'll probably fix it in whichever program -I'm working with at the time and forget to also fix it in the other -program. +Pero cómo se configura esto? Digamos que quiero usar la función `analizarINI` +del [Capítulo 9](regexp#ini) en otro programa. Si está claro de qué +depende la función (en este caso, nada), puedo copiar todo el +código necesario en mi nuevo proyecto y usarlo. Pero luego, si encuentro un +error en ese código, probablemente lo solucione en el programa +en el que estoy trabajando en ese momento y me olvido de arreglarlo en el otro +programa. {{index duplication, "copy-paste programming"}} -Once you start duplicating code, you'll quickly find yourself wasting -time and energy moving copies around and keeping them up-to-date. +Una vez que comience a duplicar código, rápidamente te encontraras perdiendo +tiempo y energía moviendo las copias alrededor y manteniéndolas actualizadas. -That's where _((package))s_ come in. A package is a chunk of code that -can be distributed (copied and installed). It may contain one or more -modules and has information about which other packages it depends on. -A package also usually comes with documentation explaining what it -does so that people who didn't write it might still be able to use -it. +Ahí es donde los _((paquete))s_ entran. Un paquete es un pedazo de código que +puede ser distribuido (copiado e instalado). Puede contener uno o más +módulos, y tiene información acerca de qué otros paquetes depende. +Un paquete también suele venir con documentación que explica qué es +lo que hace, para que las personas que no lo escribieron todavía puedan hacer +uso de el. -When a problem is found in a package or a new feature is added, the -package is updated. Now the programs that depend on it (which may also -be packages) can upgrade to the new ((version)). +Cuando se encuentra un problema en un paquete, o se agrega una nueva +característica, el el paquete es actualizado. Ahora los programas que dependen +de él (que también pueden ser otros paquetes) pueden actualizar a la +nueva ((versión)). {{id modules_npm}} {{index installation, upgrading, "package manager", download, reuse}} -Working in this way requires ((infrastructure)). We need a place to -store and find packages and a convenient way to install and upgrade -them. In the JavaScript world, this infrastructure is provided by -((NPM)) ([_https://npmjs.org_](https://npmjs.org)). +Trabajar de esta manera requiere ((infraestructura)). Necesitamos un lugar para +almacenar y encontrar paquetes, y una forma conveniente de instalar y +actualizarlos. En el mundo de JavaScript, esta infraestructura es provista por +((NPM)) ([_npmjs.org_](https://npmjs.org)). -NPM is two things: an online service where one can download (and -upload) packages and a program (bundled with Node.js) that helps you -install and manage them. +NPM es dos cosas: un servicio en línea donde uno puede descargar (y +subir) paquetes, y un programa (incluido con Node.js) que te ayuda a +instalar y administrarlos. {{index "ini package"}} -At the time of writing, there are more than half a million different -packages available on NPM. A large portion of those are rubbish, I -should mention, but almost every useful, publicly available package -can be found on there. For example, an INI file parser, similar to the -one we built in [Chapter ?](regexp), is available under the package -name `ini`. +Al momento de escribir esto, hay más de medio millón de paquetes diferentes +disponibles en NPM. Una gran parte de ellos son basura, debería mencionar, +pero casi todos los paquetes útiles, disponibles públicamente, +se puede encontrar allí. Por ejemplo, un analizador de archivos INI, similar al +uno que construimos en el [Capítulo 9](regexp), está disponible bajo el +nombre de paquete `ini`. {{index "command line"}} -[Chapter ?](node) will show how to install such packages locally using -the `npm` command line program. +En el [Capítulo 20](node) veremos cómo instalar dichos paquetes de forma local +utilizando el programa de línea de comandos `npm`. -Having quality packages available for download is extremely valuable. -It means that we can often avoid reinventing a program that 100 -people have written before and get a solid, well-tested -implementation at the press of a few keys. +Tener paquetes de calidad disponibles para descargar es extremadamente valioso. +Significa que a menudo podemos evitar tener que reinventar un programa que cien +personas han escrito antes, y obtener una implementación sólida y bien probado +con solo presionar algunas teclas. {{index maintenance}} -Software is cheap to copy, so once someone has written it, -distributing it to other people is an efficient process. But writing -it in the first place _is_ work, and responding to people who have -found problems in the code, or who want to propose new features, is -even more work. +El software es barato de copiar, por lo que una vez lo haya escrito alguien, +distribuirlo a otras personas es un proceso eficiente. Pero escribirlo +en el primer lugar, _es_ trabajo y responder a las personas que han +encontrado problemas en el código, o que quieren proponer nuevas +características, es aún más trabajo. -By default, you own the ((copyright)) to the code you write, and other -people may use it only with your permission. But because some people -are just nice and because publishing good software can help make you -a little bit famous among programmers, many packages are published -under a ((license)) that explicitly allows other people to use it. +Por defecto, tu posees el ((copyright)) del código que escribes, y otras +personas solo pueden usarlo con tu permiso. Pero ya que algunas personas +son simplemente agradables, y porque la publicación de un buen software +puede ayudarte a hacerte un poco famoso entre los programadores, se publican +muchos paquetes bajo una ((licencia)) que explícitamente permite a otras +personas usarlos. -Most code on ((NPM)) is licensed this way. Some licenses require you -to also publish code that you build on top of the package under the -same license. Others are less demanding, just requiring that you keep -the license with the code as you distribute it. The JavaScript -community mostly uses the latter type of license. When using other -people's packages, make sure you are aware of their license. +La mayoría del código en ((NPM)) esta licenciado de esta manera. Algunas +licencias requieren que tu publiques también el código bajo la +misma licencia del paquete que estas usando. Otras son menos exigentes, +solo requieren que guardes la licencia con el código cuando lo distribuyas. +La comunidad de JavaScript principalmente usa ese último tipo de licencia. +Al usar paquetes de otras personas, asegúrete de conocer su licencia. -## Improvised modules +## Módulos improvisados -Until 2015, the JavaScript language had no built-in module system. -Yet people had been building large systems in JavaScript for more than a decade, and they _needed_ ((module))s. +Hasta 2015, el lenguaje JavaScript no tenía un sistema de módulos incorporado. +Sin embargo, la gente había estado construyendo sistemas grandes en JavaScript +durante más de una década y ellos _necesitaban_ ((módulos)). -{{index [function, scope], [interface, module], [object, as module]}} +{{index [function, scope]}} -So they designed their own ((module system))s on top of the language. -You can use JavaScript functions to create local scopes and -objects to represent module interfaces. +Así que diseñaron sus propios ((sistema de módulos)) arriba del lenguaje. +Puedes usar funciones de JavaScript para crear alcances locales, y +((objeto))s para representar las ((interfaces)) de los módulos. {{index "Date class", "weekDay module"}} -This is a module for going between day names and numbers (as returned -by `Date`'s `getDay` method). Its interface consists of `weekDay.name` -and `weekDay.number`, and it hides its local binding `names` inside -the scope of a function expression that is immediately invoked. +Este es un módulo para ir entre los nombres de los días y números (como son +retornados por el método `getDay` de `Date`). Su interfaz consiste en +`diaDeLaSemana.nombre` y `diaDeLaSemana.numero`, y oculta su vinculación +local `nombres` dentro del alcance de una expresión de función que se invoca +inmediatamente. ``` -const weekDay = function() { - const names = ["Sunday", "Monday", "Tuesday", "Wednesday", - "Thursday", "Friday", "Saturday"]; +const diaDeLaSemana = function() { + const nombres = ["Domingo", "Lunes", "Martes", "Miercoles", + "Jueves", "Viernes", "Sabado"]; return { - name(number) { return names[number]; }, - number(name) { return names.indexOf(name); } + nombre(numero) { return nombres[numero]; }, + numero(nombre) { return nombres.indexOf(nombre); } }; }(); -console.log(weekDay.name(weekDay.number("Sunday"))); -// → Sunday +console.log(diaDeLaSemana.nombre(diaDeLaSemana.numero("Domingo"))); +// → Domingo ``` -{{index dependency, [interface, module]}} +{{index dependency}} -This style of modules provides ((isolation)), to a certain degree, but -it does not declare dependencies. Instead, it just puts its -interface into the ((global scope)) and expects its dependencies, -if any, to do the same. For a long time this was the main approach -used in web programming, but it is mostly obsolete now. +Este estilo de módulos proporciona ((aislamiento)), hasta cierto punto, pero +no declara dependencias. En cambio, simplemente pone su ((interfaz)) en el +((alcance global)) y espera que sus dependencias, si hay alguna, hagan lo mismo. +Durante mucho tiempo, este fue el enfoque principal utilizado en la +programación web, pero ahora está mayormente obsoleto. -If we want to make dependency relations part of the code, we'll have -to take control of loading dependencies. Doing that requires being -able to execute strings as code. JavaScript can do this. +Si queremos hacer que las relaciones de dependencia sean parte del código, +tendremos que tomar el control de las dependencias que deben ser cargadas. +Hacer eso requiere que seamos capaces de ejecutar strings como código. +JavaScript puede hacer esto. {{id eval}} -## Evaluating data as code +## Evaluando datos como código {{index evaluation, interpretation}} -There are several ways to take data (a string of code) and run it as -part of the current program. +Hay varias maneras de tomar datos (un string de código) y ejecutarlos como +una parte del programa actual. {{index isolation, eval}} -The most obvious way is the special operator `eval`, which will -execute a string in the _current_ ((scope)). This is usually a bad idea -because it breaks some of the properties that scopes normally have, -such as it being easily predictable which binding a given name refers -to. +La forma más obvia es usar el operador especial `eval`, que ejecuta un +string en el ((alcance)) _actual_. Esto usualmente es una mala idea +porque rompe algunas de las propiedades que normalmente tienen los alcances, +tal como fácilmente predecir a qué vinculación se refiere un nombre dado. ``` const x = 1; -function evalAndReturnX(code) { - eval(code); +function evaluarYRetornarX(codigo) { + eval(codigo); return x; } -console.log(evalAndReturnX("var x = 2")); +console.log(evaluarYRetornarX("var x = 2")); // → 2 -console.log(x); -// → 1 ``` {{index "Function constructor"}} -A less scary way of interpreting data as code is to use the `Function` -constructor. It takes two arguments: a string containing a -comma-separated list of argument names and a string containing the -function body. It wraps the code in a function value so that it gets -its own scope and won't do odd things with other scopes. +Una forma menos aterradora de interpretar datos como código es usar el +constructor `Function`. Este toma dos argumentos: un string que contiene una +lista de nombres de argumentos separados por comas y un string que contiene el +cuerpo de la función. ``` -let plusOne = Function("n", "return n + 1;"); -console.log(plusOne(4)); +let masUno = Function("n", "return n + 1;"); +console.log(masUno(4)); // → 5 ``` -This is precisely what we need for a module system. We can wrap the -module's code in a function and use that function's scope as module -((scope)). +Esto es precisamente lo que necesitamos para un sistema de módulos. Podemos +envolver el código del módulo en una función y usar el alcance de esa función +como el ((alcance)) del módulo. ## CommonJS @@ -270,34 +270,34 @@ module's code in a function and use that function's scope as module {{index "CommonJS modules"}} -The most widely used approach to bolted-on JavaScript modules is -called _CommonJS modules_. ((Node.js)) uses it and is the system used -by most packages on ((NPM)). +El enfoque más utilizado para incluir módulos en JavaScript es +llamado _módulos CommonJS_. ((Node.js)) lo usa, y es el sistema utilizado +por la mayoría de los paquetes en ((NPM)). -{{index "require function", [interface, module]}} +{{index "require function"}} -The main concept in CommonJS modules is a function called `require`. -When you call this with the module name of a dependency, it makes sure -the module is loaded and returns its interface. +El concepto principal en los módulos CommonJS es una función llamada `require` +("requerir"). Cuando la llamas con el nombre del módulo de una dependencia, +esta se asegura de que el módulo sea cargado y retorna su ((interfaz)). {{index "exports object"}} -Because the loader wraps the module code in a function, modules -automatically get their own local scope. All they have to -do is call `require` to access their dependencies and put their -interface in the object bound to `exports`. +Debido a que el cargador envuelve el código del módulo en una función, los +módulos obtienen automáticamente su propio alcance local. Todo lo que tienen que +hacer es llamar a `require` para acceder a sus dependencias, y poner su +interfaz en el objeto vinculado a `exports` ("exportaciones"). {{index "formatDate module", "Date class", "ordinal package", "date-names package"}} -This example module provides a date-formatting function. It uses two -((package))s from NPM—`ordinal` to convert numbers to strings like -`"1st"` and `"2nd"`, and `date-names` to get the English names for -weekdays and months. It exports a single function, `formatDate`, which -takes a `Date` object and a ((template)) string. +Este módulo de ejemplo proporciona una función de formateo de fecha. Utiliza dos +((paquete))s de NPM—`ordinal` para convertir números a strings como +`"1st"` y `"2nd"`, y `date-names` para obtener los nombres en inglés de los +días de la semana y meses. Este exporta una sola función, `formatDate`, que +toma un objeto `Date` y un string ((plantilla)). -The template string may contain codes that direct the format, such as -`YYYY` for the full year and `Do` for the ordinal day of the month. -You could give it a string like `"MMMM Do YYYY"` to get output like +El string de plantilla puede contener códigos que dirigen el formato, como +`YYYY` para todo el año y `Do` para el día ordinal del mes. +Podrías darle un string como `"MMMM Do YYYY"` para obtener resultados como "November 22nd 2017". ``` @@ -318,13 +318,14 @@ exports.formatDate = function(date, format) { {{index "destructuring binding"}} -The interface of `ordinal` is a single function, whereas `date-names` -exports an object containing multiple things—`days` and `months` are -arrays of names. Destructuring is very convenient when creating -bindings for imported interfaces. +La interfaz de `ordinal` es una función única, mientras que `date-names` +exporta un objeto que contiene varias cosas—los dos valores que usamos son +arrays de nombres. La desestructuración es muy conveniente cuando creamos +vinculaciones para interfaces importadas. -The module adds its interface function to `exports` so that modules -that depend on it get access to it. We could use the module like this: +El módulo agrega su función de interfaz a `exports`, de modo que los módulos +que dependen de el tengan acceso a el. Podríamos usar el módulo de esta +manera: ``` const {formatDate} = require("./format-date"); @@ -338,72 +339,71 @@ console.log(formatDate(new Date(2017, 9, 13), {{id require}} -We can define `require`, in its most minimal form, like this: +Podemos definir `require`, en su forma más mínima, así: ```{test: wrap, sandbox: require} require.cache = Object.create(null); -function require(name) { - if (!(name in require.cache)) { - let code = readFile(name); - let module = {exports: {}}; - require.cache[name] = module; - let wrapper = Function("require, exports, module", code); - wrapper(require, module.exports, module); +function require(nombre) { + if (!(nombre in require.cache)) { + let codigo = leerArchivo(nombre); + let modulo = {exportaciones: {}}; + require.cache[nombre] = modulo; + let envolvedor = Function("require, exportaciones, modulo", codigo); + envolvedor(require, modulo.exportaciones, modulo); } - return require.cache[name].exports; + return require.cache[nombre].exportaciones; } ``` -{{index [file, access]}} - -In this code, `readFile` is a made-up function that reads a file and -returns its contents as a string. Standard JavaScript provides no such -functionality—but different JavaScript environments, such as the -browser and Node.js, provide their own ways of accessing files. -The example just pretends that `readFile` exists. +En este código, `leerArchivo` es una función inventada que lee un archivo y +retorna su contenido como un string. El estándar de JavaScript no ofrece tal +funcionalidad—pero diferentes entornos de JavaScript, como el +navegador y Node.js, proporcionan sus propias formas de acceder a ((archivos)). +El ejemplo solo pretende que `leerArchivo` existe. {{index cache, "Function constructor"}} -To avoid loading the same module multiple times, `require` keeps a -store (cache) of already loaded modules. When called, it first checks -if the requested module has been loaded and, if not, loads it. This -involves reading the module's code, wrapping it in a function, and -calling it. +Para evitar cargar el mismo módulo varias veces, `require` mantiene un +(caché) almacenado de módulos que ya han sido cargados. Cuando se llama, +primero verifica si el módulo solicitado ya ha sido cargado y, si no, lo carga. +Esto implica leer el código del módulo, envolverlo en una función y +llamárla. -{{index "ordinal package", "exports object", "module object", [interface, module]}} +{{index "ordinal package", "exports object", "module object"}} -The interface of the `ordinal` package we saw before is not an -object but a function. A quirk of the CommonJS modules is that, -though the module system will create an empty interface object for you -(bound to `exports`), you can replace that with any value by -overwriting `module.exports`. This is done by many modules to export a -single value instead of an interface object. +La ((interfaz)) del paquete `ordinal` que vimos antes no es un +objeto, sino una función. Una peculiaridad de los módulos CommonJS es que, +aunque el sistema de módulos creará un objeto de interfaz vacío para ti +(vinculado a `exports`), puedes reemplazarlo con cualquier valor al +sobrescribir `module.exports`. Esto lo hacen muchos módulos para exportar un +valor único en lugar de un objeto de interfaz. -By defining `require`, `exports`, and `module` as ((parameter))s for -the generated wrapper function (and passing the appropriate values -when calling it), the loader makes sure that these bindings are -available in the module's ((scope)). +Al definir `require`, `exportaciones` y `modulo` como ((parametros)) para +la función de envoltura generada (y pasando los valores apropiados +al llamarla), el cargador se asegura de que estas vinculaciones esten +disponibles en el ((alcance)) del módulo. {{index resolution, "relative path"}} -The way the string given to `require` is translated to an actual -filename or web address differs in different systems. When it starts with -`"./"` or `"../"`, it is generally interpreted as relative to the -current module's filename. So `"./format-date"` would be the file -named `format-date.js` in the same directory. +La forma en que el string dado a `require` se traduce a un nombre de archivo +real o dirección web difiere en diferentes sistemas. Cuando comienza con +`"./"` o `"../"`, generalmente se interpreta como relativo al +nombre del archivo actual. Entonces `"./format-date"` sería el archivo +llamado `format-date.js` en el mismo directorio. -When the name isn't relative, Node.js will look for an installed -package by that name. In the example code in this chapter, we'll -interpret such names as referring to NPM packages. We'll go into more -detail on how to install and use NPM modules in [Chapter ?](node). +Cuando el nombre no es relativo, Node.js buscará por un paquete instalado +con ese nombre. En el código de ejemplo de este capítulo, +interpretaremos esos nombres como referencias a paquetes de NPM. +Entraremos en más detalles sobre cómo instalar y usar los módulos de NPM en +el [Capítulo 20](node). {{id modules_ini}} {{index "ini package"}} -Now, instead of writing our own INI file parser, we can use one from -((NPM)). +Ahora, en lugar de escribir nuestro propio analizador de archivos INI, +podemos usar uno de ((NPM)): ``` const {parse} = require("ini"); @@ -412,31 +412,33 @@ console.log(parse("x = 10\ny = 20")); // → {x: "10", y: "20"} ``` -## ECMAScript modules +## Módulos ECMAScript -((CommonJS modules)) work quite well and, in combination with NPM, -have allowed the JavaScript community to start sharing code on a large -scale. +Los ((módulos CommonJS)) funcionan bastante bien y, en combinación con NPM, +han permitido que la comunidad de JavaScript comience a compartir +código en una gran escala. {{index "exports object", linter}} -But they remain a bit of a duct-tape ((hack)). The ((notation)) is -slightly awkward—the things you add to `exports` are not available in -the local ((scope)), for example. And because `require` is a normal -function call taking any kind of argument, not just a string literal, -it can be hard to determine the dependencies of a module without -running its code. +Pero siguen siendo un poco de un ((truco)) con cinta adhesiva. La ((notación)) es +ligeramente incomoda—las cosas que agregas a `exports` no están disponibles en +el ((alcance)) local, por ejemplo. Y ya que `require` es una llamada de función +normal tomando cualquier tipo de argumento, no solo un string literal, +puede ser difícil de determinar las dependencias de un módulo sin +correr su código primero. {{index "import keyword", dependency, "ES modules"}} {{id es}} -This is why the JavaScript standard from 2015 introduces its own, -different module system. It is usually called _((ES modules))_, where -_ES_ stands for ((ECMAScript)). The main concepts of dependencies and -interfaces remain the same, but the details differ. For one thing, the -notation is now integrated into the language. Instead of calling a -function to access a dependency, you use a special `import` keyword. +Esta es la razón por la cual el estándar de JavaScript introdujo su propio, +sistema de módulos diferente a partir de 2015. Por lo general es llamado +_((módulos ES))_, donde _ES_ significa ((ECMAScript)). Los principales +conceptos de dependencias e interfaces siguen siendo los mismos, +pero los detalles difieren. Por un lado, +la notación está ahora integrada en el lenguaje. En lugar de llamar a una +función para acceder a una dependencia, utilizas una palabra clave +`import` ("importar") especial. ``` import ordinal from "ordinal"; @@ -447,209 +449,204 @@ export function formatDate(date, format) { /* ... */ } {{index "export keyword", "formatDate module"}} -Similarly, the `export` keyword is used to export things. It may -appear in front of a function, class, or binding definition (`let`, -`const`, or `var`). +Similarmente, la palabra clave `export` se usa para exportar cosas. Puede +aparecer delante de una función, clase o definición de vinculación (`let`, +`const`, o `var`). -{{index [binding, exported]}} - -An ES module's interface is not a single value but a set of named -bindings. The preceding module binds `formatDate` to a function. When -you import from another module, you import the _binding_, not the -value, which means an exporting module may change the value of the -binding at any time, and the modules that import it will see its new -value. +La interfaz de un módulo ES no es un valor único, sino un conjunto de +((vinculaciones)) con nombres. El módulo anterior vincula `formatDate` +a una función. Cuando importas desde otro módulo, importas la _vinculación_, +no el valor, lo que significa que un módulo exportado puede cambiar el +valor de la vinculación en cualquier momento, y que los módulos que la importen +verán su nuevo valor. {{index "default export"}} -When there is a binding named `default`, it is treated as the module's -main exported value. If you import a module like `ordinal` in the -example, without braces around the binding name, you get its `default` -binding. Such modules can still export other bindings under different -names alongside their `default` export. +Cuando hay una vinculación llamada `default`, esta se trata como el +principal valor del módulo exportado. Si importas un módulo como `ordinal` en +el ejemplo, sin llaves alrededor del nombre de la vinculación, obtienes su +vinculación `default`. Dichos módulos aún pueden exportar otras vinculaciones +bajo diferentes nombres ademas de su exportación por `default`. -To create a default export, you write `export default` before an -expression, a function declaration, or a class declaration. +Para crear una exportación por default, escribe `export default` antes de una +expresión, una declaración de función o una declaración de clase. ``` -export default ["Winter", "Spring", "Summer", "Autumn"]; +export default ["Invierno", "Primavera", "Verano", "Otoño"]; ``` -It is possible to rename imported bindings using the word `as`. +Es posible renombrar la vinculación importada usando la palabra `as` ("como"). ``` -import {days as dayNames} from "date-names"; +import {days as nombresDias} from "date-names"; -console.log(dayNames.length); +console.log(nombresDias.length); // → 7 ``` -Another important difference is that ES module imports happen before -a module's script starts running. That means `import` declarations -may not appear inside functions or blocks, and the names of -dependencies must be quoted strings, not arbitrary expressions. - -At the time of writing, the JavaScript community is in the process of -adopting this module style. But it has been a slow process. It took -a few years, after the format was specified, for browsers and Node.js -to start supporting it. And though they mostly support it now, this -support still has issues, and the discussion on how such modules -should be distributed through ((NPM)) is still ongoing. +Al momento de escribir esto, la comunidad de JavaScript está en proceso de +adoptar este estilo de módulos. Pero ha sido un proceso lento. Tomó +algunos años, después de que se haya especificado el formato paraq que los +navegadores y Node.js comenzaran a soportarlo. Y a pesar de que lo soportan +mayormente ahora, este soporte todavía tiene problemas, y la discusión sobre +cómo dichos módulos deberían distribuirse a través de ((NPM)) todavía está en +curso. -Many projects are written using ES modules and then automatically -converted to some other format when published. We are in a -transitional period in which two different module systems are used -side by side, and it is useful to be able to read and write code in -either of them. +Muchos proyectos se escriben usando módulos ES y luego se convierten +automáticamente a algún otro formato cuando son publicados. Estamos en +período de transición en el que se utilizan dos sistemas de módulos diferentes +uno al lado del otro, y es útil poder leer y escribir código en +cualquiera de ellos. -## Building and bundling +## Construyendo y empaquetando {{index compilation, "type checking"}} -In fact, many JavaScript projects aren't even, technically, written in -JavaScript. There are extensions, such as the type checking -((dialect)) mentioned in [Chapter ?](error#typing), that are widely -used. People also often start using planned extensions to the language -long before they have been added to the platforms that actually run -JavaScript. - -To make this possible, they _compile_ their code, translating it from -their chosen JavaScript dialect to plain old JavaScript—or even to a -past version of JavaScript—so that old ((browsers)) can run it. - -{{index latency, performance, [file, access], [network, speed]}} - -Including a modular program that consists of 200 different -files in a ((web page)) produces its own problems. If fetching a -single file over the network takes 50 milliseconds, loading -the whole program takes 10 seconds, or maybe half that if you can -load several files simultaneously. That's a lot of wasted time. -Because fetching a single big file tends to be faster than fetching a -lot of tiny ones, web programmers have started using tools that roll -their programs (which they painstakingly split into modules) back into -a single big file before they publish it to the Web. Such tools are -called _((bundler))s_. +De hecho, muchos proyectos de JavaScript ni siquiera están, técnicamente, +escritos en JavaScript. Hay extensiones, como el ((dialecto)) de comprobación +de tipos mencionado en el [Capítulo 7](error#typing), que son ampliamente +usados. Las personas también suelen comenzar a usar extensiones planificadas +para el lenguaje mucho antes de que estas hayan sido agregadas a las plataformas +que realmente corren JavaScript. + +Para que esto sea posible, ellos _compilan_ su código, traduciéndolo del +dialecto de JavaScript que eligieron a JavaScript simple y antiguo—o incluso a +una versión anterior de JavaScript, para que ((navegadores)) antiguos puedan +ejecutarlo. + +{{index latency, performance}} + +Incluir un programa modular que consiste de 200 archivos diferentes +en una ((página web)) produce sus propios problemas. Si buscar un +solo ((archivo)) sobre la ((red)) tarda 50 milisegundos, cargar +todo el programa tardaria 10 segundos, o tal vez la mitad si puedes +cargar varios archivos simultáneamente. Eso es mucho tiempo perdido. +Ya que buscar un solo archivo grande tiende a ser más rápido que buscar muchos +archivos pequeños, los programadores web han comenzado a usar herramientas que +convierten sus programas (los cuales cuidadosamente estan dividos en módulos) +de nuevo en un único archivo grande antes de publicarlo en la Web. Tales +herramientas son llamado _((empaquetadores))_. {{index "file size"}} -And we can go further. Apart from the number of files, the _size_ of -the files also determines how fast they can be transferred over the -network. Thus, the JavaScript community has invented _((minifier))s_. -These are tools that take a JavaScript program and make it smaller by -automatically removing comments and whitespace, renaming bindings, and -replacing pieces of code with equivalent code that take up less space. +Y podemos ir más allá. Además de la cantidad de archivos, el _tamaño_ de +los archivos también determina qué tan rápido se pueden transferir a través de la +red. Por lo tanto, la comunidad de JavaScript ha inventado _((minificadores))_. +Estas son herramientas que toman un programa de JavaScript y lo hacen más pequeño +al eliminar automáticamente los comentarios y espacios en blanco, cambia el nombre +de las vinculaciones, y reemplaza piezas de código con código equivalente +que ocupa menos espacio. {{index pipeline, tool}} -So it is not uncommon for the code that you find in an NPM package or -that runs on a web page to have gone through _multiple_ stages of -transformation—converted from modern JavaScript to historic -JavaScript, from ES module format to CommonJS, bundled, and minified. -We won't go into the details of these tools in this book since they -tend to be boring and change rapidly. Just be aware that the -JavaScript code you run is often not the code as it was written. - -## Module design +Por lo tanto, no es raro que el código que encuentres en un paquete de NPM o +que se ejecute en una página web haya pasado por _multiples_ etapas de +transformación: conversión de JavaScript moderno a JavaScript histórico, +del formato de módulos ES a CommonJS, empaquetado y minificado. +No vamos a entrar en los detalles de estas herramientas en este libro, ya que +tienden a ser aburridos y cambian rápidamente. Solo ten en cuenta que +el código JavaScript que ejecutas a menudo no es el código tal y como fue +escrito. -{{index [module, design], [interface, module], [code, "structure of"]}} +## Diseño de módulos -Structuring programs is one of the subtler aspects of programming. Any -nontrivial piece of functionality can be modeled in various ways. +{{index [module, design], interface, "code structure"}} -Good program design is subjective—there are trade-offs involved and -matters of taste. The best way to learn the value of well-structured -design is to read or work on a lot of programs and notice what works -and what doesn't. Don't assume that a painful mess is "just the way it -is". You can improve the structure of almost everything by putting -more thought into it. +La estructuración de programas es uno de los aspectos más sutiles de la +programación. Cualquier pieza de funcionalidad no trivial se puede modelar de +varias maneras. -{{index [interface, module]}} +Un buen diseño de programa es subjetivo—hay ventajas/desventajas involucradas, y +cuestiones de gusto. La mejor manera de aprender el valor de una buena +estructura de diseño es leer o trabajar en muchos programas y notar lo que +funciona y lo qué no. No asumas que un desastroso doloroso es +"solo la forma en que las cosas son ". Puedes mejorar la estructura de casi +todo al ponerle mas pensamiento. -One aspect of module design is ease of use. If you are designing -something that is intended to be used by multiple people—or even by -yourself, in three months when you no longer remember the specifics of -what you did—it is helpful if your interface is simple and -predictable. +Un aspecto del diseño de módulos es la facilidad de uso. Si estás diseñando +algo que está destinado a ser utilizado por varias personas—o incluso por +ti mismo, en tres meses cuando ya no recuerdes los detalles de +lo que hiciste—es útil si tu ((interfaz)) es simple y predicible. {{index "ini package", JSON}} -That may mean following existing conventions. A good example is the -`ini` package. This module imitates the standard `JSON` object by -providing `parse` and `stringify` (to write an INI file) functions, -and, like `JSON`, converts between strings and plain objects. So the -interface is small and familiar, and after you've worked with it once, -you're likely to remember how to use it. +Eso puede significar seguir convenciones existentes. Un buen ejemplo es el +paquete `ini`. Este módulo imita el objeto estándar `JSON` al +proporcionar las funciones `parse` y `stringify` (para escribir un archivo INI), +y, como `JSON`, convierte entre strings y objetos simples. Entonces +la interfaz es pequeña y familiar, y después de haber trabajado con ella una +vez, es probable que recuerdes cómo usarla. {{index "side effect", "hard disk", composability}} -Even if there's no standard function or widely used package to -imitate, you can keep your modules predictable by using simple ((data -structure))s and doing a single, focused thing. Many of the INI-file -parsing modules on NPM provide a function that directly reads such a -file from the hard disk and parses it, for example. This makes it -impossible to use such modules in the browser, where we don't have -direct file system access, and adds complexity that would have been -better addressed by _composing_ the module with some file-reading -function. +Incluso si no hay una función estándar o un paquete ampliamente utilizado para +imitar, puedes mantener tus módulos predecibles mediante el uso de +estructuras de datos simples y haciendo una cosa única y enfocada. Muchos de +los módulos de análisis de archivos INI en NPM proporcionan una función que +lee directamente tal archivo del disco duro y lo analiza, por ejemplo. Esto +hace que sea imposible de usar tales módulos en el navegador, donde no tenemos +acceso directo al sistema de archivos, y agrega una complejidad que habría sido +mejor abordada al _componer_ el módulo con alguna función de lectura de +archivos. {{index "pure function"}} -This points to another helpful aspect of module design—the ease with -which something can be composed with other code. Focused modules that -compute values are applicable in a wider range of programs than bigger -modules that perform complicated actions with side effects. An INI -file reader that insists on reading the file from disk is useless in a -scenario where the file's content comes from some other source. +Lo que apunta a otro aspecto útil del diseño de módulos—la facilidad con la +qué algo se puede componer con otro código. Módulos enfocados que +que computan valores son aplicables en una gama más amplia de programas +que módulos mas grandes que realizan acciones complicadas con efectos +secundarios. Un lector de archivos INI que insista en leer el archivo desde el +disco es inútil en un escenario donde el contenido del archivo provenga de +alguna otra fuente. {{index "object-oriented programming"}} -Relatedly, stateful objects are sometimes useful or even necessary, -but if something can be done with a function, use a function. Several -of the INI file readers on NPM provide an interface style that -requires you to first create an object, then load the file into your -object, and finally use specialized methods to get at the results. -This type of thing is common in the object-oriented tradition, and -it's terrible. Instead of making a single function call and moving on, -you have to perform the ritual of moving your object through various -states. And because the data is now wrapped in a specialized object -type, all code that interacts with it has to know about that type, -creating unnecessary interdependencies. - -Often defining new data structures can't be avoided—only a few -basic ones are provided by the language standard, and many types of -data have to be more complex than an array or a map. But when an -array suffices, use an array. - -An example of a slightly more complex data structure is the graph from -[Chapter ?](robot). There is no single obvious way to represent a -((graph)) in JavaScript. In that chapter, we used an object whose -properties hold arrays of strings—the other nodes reachable from that -node. - -There are several different pathfinding packages on ((NPM)), but none -of them uses this graph format. They usually allow the graph's edges to -have a weight, which is the cost or distance associated with it. That isn't -possible in our representation. +Relacionadamente, los objetos con estado son a veces útiles e incluso necesarios, +pero si se puede hacer algo con una función, usa una función. Varios +de los lectores de archivos INI en NPM proporcionan un estilo de interfaz que +requiere que primero debes crear un objeto, luego cargar el archivo en tu objeto, +y finalmente usar métodos especializados para obtener los resultados. Este tipo +de cosas es común en la tradición orientada a objetos, y es terrible. +En lugar de hacer una sola llamada de función y seguir adelante, +tienes que realizar el ritual de mover tu objeto a través de diversos +estados. Y ya que los datos ahora están envueltos en un objeto de +tipo especializado, todo el código que interactúa con él tiene que saber +sobre ese tipo, creando interdependencias innecesarias. + +A menudo no se puede evitar la definición de nuevas estructuras de datos—solo +unas pocas básicas son provistos por el estándar de lenguaje, y muchos +tipos de datos tienen que ser más complejos que un ((array)) o un mapa. +Pero cuando el array es suficiente, usa un array. + +Un ejemplo de una estructura de datos un poco más compleja es el grafo de el +[Capítulo 7](robot). No hay una sola manera obvia de representar un +((grafo)) en JavaScript. En ese capítulo, usamos un objeto cuya propiedades +contenian arrays de strings—los otros nodos accesibles desde ese +nodo. + +Hay varios paquetes de busqueda de rutas diferentes en ((NPM)), pero ninguno +de ellos usa este formato de grafo. Por lo general, estos permiten que los +bordes del grafo tengan un peso, el costo o la distancia asociada a ellos, +lo que no es posible en nuestra representación. {{index "Dijkstra, Edsger", pathfinding, "Dijkstra's algorithm", "dijkstrajs package"}} -For example, there's the `dijkstrajs` package. A well-known approach -to pathfinding, quite similar to our `findRoute` function, is called -_Dijkstra's algorithm_, after Edsger Dijkstra, who first wrote it -down. The `js` suffix is often added to package names to indicate the -fact that they are written in JavaScript. This `dijkstrajs` package -uses a graph format similar to ours, but instead of arrays, it uses -objects whose property values are numbers—the weights of the edges. +Por ejemplo, está el paquete `dijkstrajs`. Un enfoque bien conocido +par la busqueda de rutas, bastante similar a nuestra función `encontrarRuta`, +se llama el _algoritmo de Dijkstra_, después de Edsger Dijkstra, quien fue +el primero que lo escribió. El sufijo `js` a menudo se agrega a los nombres de +los paquetes para indicar el hecho de que están escritos en JavaScript. +Este paquete `dijkstrajs` utiliza un formato de grafo similar al nuestro, +pero en lugar de arrays, utiliza objetos cuyos valores de propiedad son +números—los pesos de los bordes. -So if we wanted to use that package, we'd have to make sure that our -graph was stored in the format it expects. All edges get the same -weight since our simplified model treats each road as having the -same cost (one turn). +Si quisiéramos usar ese paquete, tendríamos que asegurarnos de que nuestro +grafo fue almacenado en el formato que este espera. ``` const {find_path} = require("dijkstrajs"); -let graph = {}; +let grafo = {}; for (let node of Object.keys(roadGraph)) { let edges = graph[node] = {}; for (let dest of roadGraph[node]) { @@ -657,158 +654,157 @@ for (let node of Object.keys(roadGraph)) { } } -console.log(find_path(graph, "Post Office", "Cabin")); -// → ["Post Office", "Alice's House", "Cabin"] +console.log(find_path(grafo, "Oficina de Correos", "Cabaña")); +// → ["Oficina de Correos", "Casa de Alice", "Cabaña"] ``` -This can be a barrier to composition—when various packages are using -different data structures to describe similar things, combining them -is difficult. Therefore, if you want to design for composability, -find out what ((data structure))s other people are using and, when -possible, follow their example. +Esto puede ser una barrera para la composición—cuando varios paquetes están +usando diferentes estructuras de datos para describir cosas similares, +combinarlos es difícil. Por lo tanto, si deseas diseñar para la compibilidad, +averigua qué ((estructura de datos)) están usando otras personas y, cuando +sea posible, sigue su ejemplo. -## Summary +## Resumen -Modules provide structure to bigger programs by separating the code -into pieces with clear interfaces and dependencies. The interface is -the part of the module that's visible from other modules, and the -dependencies are the other modules that it makes use of. +Los módulos proporcionan de estructura a programas más grandes al separar el +código en piezas con interfaces y dependencias claras. La interfaz es +la parte del módulo que es visible desde otros módulos, y +las dependencias son los otros módulos este que utiliza. -Because JavaScript historically did not provide a module system, the -CommonJS system was built on top of it. Then at some point it _did_ get -a built-in system, which now coexists uneasily with the CommonJS -system. +Debido a que históricamente JavaScript no proporcionó un sistema de módulos, +el sistema CommonJS fue construido encima. Entonces, en algún momento, +_consiguio_ un sistema incorporado, que ahora coexiste incomodamente +con el sistema CommonJS. -A package is a chunk of code that can be distributed on its own. NPM -is a repository of JavaScript packages. You can download all kinds of -useful (and useless) packages from it. +Un paquete es una porción de código que se puede distribuir por sí misma. NPM +es un repositorio de paquetes de JavaScript. Puedes descargar todo tipo de +paquetes útiles (e inútiles) de él. -## Exercises +## Ejercicios -### A modular robot +### Un robot modular {{index "modular robot (exercise)", module, robot, NPM}} {{id modular_robot}} -These are the bindings that the project from [Chapter ?](robot) -creates: +Estas son las vinculaciones que el proyecto del [Capítulo 7](robot) crea: ```{lang: "text/plain"} -roads -buildGraph -roadGraph -VillageState -runRobot -randomPick -randomRobot -mailRoute -routeRobot -findRoute -goalOrientedRobot +caminos +construirGrafo +grafoCamino +EstadoPueblo +correrRobot +eleccionAleatoria +robotAleatorio +rutaCorreo +robotRuta +encontrarRuta +robotOrientadoAMetas ``` -If you were to write that project as a modular program, what modules -would you create? Which module would depend on which other module, and -what would their interfaces look like? +Si tuvieras que escribir ese proyecto como un programa modular, qué módulos +crearías? Qué módulo dependería de qué otro módulo, y +cómo se verían sus interfaces? -Which pieces are likely to be available prewritten on NPM? Would you -prefer to use an NPM package or write them yourself? +Qué piezas es probable que estén disponibles pre-escritas en NPM? +Preferirias usar un paquete de NPM o escribirlas tu mismo? {{hint {{index "modular robot (exercise)"}} -Here's what I would have done (but again, there is no single _right_ -way to design a given module): +Aqui esta lo que habría hecho (pero, una vez más, no hay una sola forma +_correcta_ de diseñar un módulo dado): {{index "dijkstrajs package"}} -The code used to build the road graph lives in the `graph` module. -Because I'd rather use `dijkstrajs` from NPM than our own pathfinding -code, we'll make this build the kind of graph data that `dijkstrajs` -expects. This module exports a single function, `buildGraph`. I'd have -`buildGraph` accept an array of two-element arrays, rather than -strings containing hyphens, to make the module less dependent on the -input format. +El código usado para construir el camino de grafo vive en el módulo `grafo`. +Ya que prefiero usar `dijkstrajs` de NPM en lugar de nuestro propio código de busqueda +de rutas, haremos que este construya el tipo de datos de grafos que `dijkstajs` +espera. Este módulo exporta una sola función, `construirGrafo`. Haria que +`construirGrafo` acepte un array de arrays de dos elementos, en lugar de +strings que contengan guiones, para hacer que el módulo sea menos +dependiente del formato de entrada. -The `roads` module contains the raw road data (the `roads` array) and -the `roadGraph` binding. This module depends on `./graph` and exports -the road graph. +El módulo `caminos` contiene los datos en bruto del camino (el array `caminos`) +y la vinculación `grafoCamino`. Este módulo depende de `./grafo` y exporta +el grafo del camino. {{index "random-item package"}} -The `VillageState` class lives in the `state` module. It depends on -the `./roads` module because it needs to be able to verify that a -given road exists. It also needs `randomPick`. Since that is a -three-line function, we could just put it into the `state` module as -an internal helper function. But `randomRobot` needs it too. So we'd -have to either duplicate it or put it into its own module. Since this -function happens to exist on NPM in the `random-item` package, a good -solution is to just make both modules depend on that. We can add the -`runRobot` function to this module as well, since it's small and -closely related to state management. The module exports both the -`VillageState` class and the `runRobot` function. - -Finally, the robots, along with the values they depend on such as -`mailRoute`, could go into an `example-robots` module, which depends -on `./roads` and exports the robot functions. To make it possible for -`goalOrientedRobot` to do route-finding, this module also depends -on `dijkstrajs`. - -By offloading some work to ((NPM)) modules, the code became a little -smaller. Each individual module does something rather simple and can -be read on its own. Dividing code into modules also often suggests -further improvements to the program's design. In this case, it seems a -little odd that the `VillageState` and the robots depend on a specific -road graph. It might be a better idea to make the graph an argument to -the state's constructor and make the robots read it from the state -object—this reduces dependencies (which is always good) and makes it -possible to run simulations on different maps (which is even better). - -Is it a good idea to use NPM modules for things that we could have -written ourselves? In principle, yes—for nontrivial things like the -pathfinding function you are likely to make mistakes and waste time -writing them yourself. For tiny functions like `random-item`, writing -them yourself is easy enough. But adding them wherever you need them -does tend to clutter your modules. - -However, you should also not underestimate the work involved in -_finding_ an appropriate NPM package. And even if you find one, it -might not work well or may be missing some feature you need. On top -of that, depending on NPM packages means you have to make sure they -are installed, you have to distribute them with your program, and you -might have to periodically upgrade them. - -So again, this is a trade-off, and you can decide either way depending -on how much the packages help you. +La clase `EstadoPueblo` vive en el módulo `estado`. Depende del +módulo `./caminos`, porque necesita poder verificar que un +camino dado existe. También necesita `eleccionAleatoria`. Dado que eso es una +función de tres líneas, podríamos simplemente ponerla en el módulo `estado` +como una función auxiliar interna. Pero `robotAleatorio` también la necesita. +Entonces tendriamos que duplicarla o ponerla en su propio módulo. +Dado que esta función existe en NPM en el paquete `random-item`, una buena +solución es hacer que ambos módulos dependan de el. Podemos agregar +la función `correrRobot` a este módulo también, ya que es pequeña y +estrechamente relacionada con la gestión de estado. El módulo exporta tanto +la clase `EstadoPueblo` como la función `correrRobot`. + +Finalmente, los robots, junto con los valores de los que dependen, como +`mailRoute`, podrían ir en un módulo `robots-ejemplo`, que depende +de `./caminos` y exporta las funciones de robot. Para que sea posible +que el `robotOrientadoAMetas` haga busqueda de rutas, este módulo también +depende de `dijkstrajs`. + +Al descargar algo de trabajo a los módulos de ((NPM)), el código se volvió un +poco mas pequeño. Cada módulo individual hace algo bastante simple, y puede +ser leído por sí mismo. La división del código en módulos también sugiere +a menudo otras mejoras para el diseño del programa. En este caso, parece un +poco extraño que `EstadoPueblo` y los robots dependan de un grafo de caminos. +Podría ser una mejor idea hacer del grafo un argumento para +el constructor del estado y hacer que los robots lo lean del objeto +estado—esto reduce las dependencias (lo que siempre es bueno) y hace +posible ejecutar simulaciones en diferentes mapas (lo cual es aún mejor). + +Es una buena idea usar módulos de NPM para cosas que podríamos haber +escrito nosotros mismos? En principio, sí—para cosas no triviales como la +función de busqueda de rutas es probable que cometas errores y pierdas el tiempo +escribiendola tú mismo. Para pequeñas funciones como `eleccionAleatoria`, +escribirla por ti mismo es lo suficiente fácil. Pero agregarlas +donde las necesites tiende a desordenar tus módulos. + +Sin embargo, tampoco debes subestimar el trabajo involucrado en +_encontrar_ un paquete apropiado de NPM. E incluso si encuentras uno, este +podría no funcionar bien o faltarle alguna característica que necesitas. +Ademas de eso, depender de los paquetes de NPM, significa que debes asegurarte +de que están instalados, tienes que distribuirlos con tu +programa, y podrías tener que actualizarlos periódicamente. + +Entonces, de nuevo, esta es una solución con compromisos, y tu puedes decidir de +una u otra manera dependiendo sobre cuánto te ayuden los paquetes. hint}} -### Roads module +### Módulo de Caminos {{index "roads module (exercise)"}} -Write a ((CommonJS module)), based on the example from [Chapter -?](robot), that contains the array of roads and exports the graph -data structure representing them as `roadGraph`. It should depend on a -module `./graph`, which exports a function `buildGraph` that is used -to build the graph. This function expects an array of two-element -arrays (the start and end points of the roads). +Escribe un ((módulo CommonJS)), basado en el ejemplo del [Capítulo 7](robot), +que contenga el array de caminos y exporte la estructura de datos +grafo que los representa como `grafoCamino`. Debería depender de un +modulo `./grafo`, que exporta una función `construirGrafo` que se usa +para construir el grafo. Esta función espera un array de arrays de +dos elementos (los puntos de inicio y final de los caminos). {{if interactive ```{test: no} -// Add dependencies and exports - -const roads = [ - "Alice's House-Bob's House", "Alice's House-Cabin", - "Alice's House-Post Office", "Bob's House-Town Hall", - "Daria's House-Ernie's House", "Daria's House-Town Hall", - "Ernie's House-Grete's House", "Grete's House-Farm", - "Grete's House-Shop", "Marketplace-Farm", - "Marketplace-Post Office", "Marketplace-Shop", - "Marketplace-Town Hall", "Shop-Town Hall" +// Añadir dependencias y exportaciones + +const caminos = [ + "Casa de Alicia-Casa de Bob", "Casa de Alicia-Cabaña", + "Casa de Alicia-Oficina de Correos", "Casa de Bob-Ayuntamiento", + "Casa de Daria-Casa de Ernie", "Casa de Daria-Ayuntamiento", + "Casa de Ernie-Casa de Grete", "Casa de Grete-Granja", + "Casa de Grete-Tienda", "Mercado-Granja", + "Mercado-Oficina de Correos", "Mercado-Tienda", + "Mercado-Ayuntamiento", "Tienda-Ayuntamiento" ]; ``` @@ -818,51 +814,51 @@ if}} {{index "roads module (exercise)", "destructuring binding", "exports object"}} -Since this is a ((CommonJS module)), you have to use `require` to -import the graph module. That was described as exporting a -`buildGraph` function, which you can pick out of its interface object -with a destructuring `const` declaration. +Como este es un ((módulo CommonJS)), debes usar `require` para +importar el módulo grafo. Eso fue descrito como exportar una +función `construirGrafo`, que puedes sacar de su objeto de interfaz +con una declaración `const` de desestructuración. -To export `roadGraph`, you add a property to the `exports` object. -Because `buildGraph` takes a data structure that doesn't precisely -match `roads`, the splitting of the road strings must happen in your -module. +Para exportar `grafoCamino`, agrega una propiedad al objeto `exports`. +Ya que `construirGrafo` toma una estructura de datos que no empareja +precisamente `caminos`, la división de los strings de los caminis +debe ocurrir en tu módulo. hint}} -### Circular dependencies +### Dependencias circulares {{index dependency, "circular dependency", "require function"}} -A circular dependency is a situation where module A depends on B, and -B also, directly or indirectly, depends on A. Many module systems -simply forbid this because whichever order you choose for loading such -modules, you cannot make sure that each module's dependencies have -been loaded before it runs. +Una dependencia circular es una situación en donde el módulo A depende de B, y +B también, directa o indirectamente, depende de A. Muchos sistemas de módulos +simplemente prohíbne esto porque cualquiera que sea el orden que elijas +para cargar tales módulos, no puedes asegurarse de que las dependencias de +cada módulo han sido cargadas antes de que se ejecuten. -((CommonJS modules)) allow a limited form of cyclic dependencies. As -long as the modules do not replace their default `exports` object and -don't access each other's interface until after they finish loading, -cyclic dependencies are okay. +Los ((modulos CommonJS)) permiten una forma limitada de dependencias cíclicas. +Siempre que los módulos no reemplacen a su objeto `exports` predeterminado, y +no accedan a la interfaz de las demás hasta que terminen de cargar, +las dependencias cíclicas están bien. -The `require` function given [earlier in this -chapter](modules#require) supports this type of dependency cycle. Can -you see how it handles cycles? What would go wrong when a module in a -cycle _does_ replace its default `exports` object? +La función `require` dada [anteriormente en este capítulo](modulos#require) +es compatible con este tipo de ciclo de dependencias. Puedes +ver cómo maneja los ciclos? Qué iría mal cuando un módulo en un +ciclo _reemplace_ su objeto `exports` por defecto? {{hint {{index overriding, "circular dependency", "exports object"}} -The trick is that `require` adds modules to its cache _before_ it -starts loading the module. That way, if any `require` call made while -it is running tries to load it, it is already known, and the current -interface will be returned, rather than starting to load the module -once more (which would eventually overflow the stack). +El truco es que `require` agrega módulos a su caché _antes_ de +comenzar a cargar el módulo. De esa forma, si se realiza una llamada `require` +mientras está ejecutando el intento de cargarlo, ya es conocido y la +interfaz actual sera retornada, en lugar de comenzar a cargar el módulo +una vez más (lo que eventualmente desbordaría la pila). -If a module overwrites its `module.exports` value, any other module -that has received its interface value before it finished loading will -have gotten hold of the default interface object (which is likely -empty), rather than the intended interface value. +Si un módulo sobrescribe su valor `module.exports`, cualquier otro módulo +que haya recibido su valor de interfaz antes de que termine de cargarse +ha conseguido el objeto de interfaz predeterminado (que es probable que este +vacío), en lugar del valor de interfaz previsto. hint}} diff --git a/11_async.md b/11_async.md index c916ed79a..7d028beb7 100644 --- a/11_async.md +++ b/11_async.md @@ -1,11 +1,11 @@ {{meta {load_files: ["code/crow-tech.js", "code/chapter/11_async.js"]}}} -# Asynchronous Programming +# Programación Asincrónica {{quote {author: "Laozi", title: "Tao Te Ching", chapter: true} -Who can wait quietly while the mud settles?\ -Who can remain still until the moment of action? +Quién puede esperar tranquilamente mientras el barro se asienta?\ +Quién puede permanecer en calma hasta el momento de actuar? quote}} @@ -13,552 +13,549 @@ quote}} {{figure {url: "img/chapter_picture_11.jpg", alt: "Picture of two crows on a branch", chapter: framed}}} -The central part of a computer, the part that carries out the -individual steps that make up our programs, is called the -_((processor))_. The programs we have seen so far are things that will -keep the processor busy until they have finished their work. The speed -at which something like a loop that manipulates numbers can be -executed depends pretty much entirely on the speed of the processor. +La parte central de una computadora, la parte que lleva a cabo los +pasos individuales que componen nuestros programas, es llamada +_((procesador))_. Los programas que hemos visto hasta ahora son cosas que +mantienen al procesador ocupado hasta que hayan terminado su trabajo. La +velocidad a la que algo como un ciclo que manipule números pueda ser +ejecutado, depende casi completamente de la velocidad del procesador. -{{index [memory, speed], [network, speed]}} +Pero muchos programas interactúan con cosas fuera del procesador. por +ejemplo, podrian comunicarse a través de una ((red)) de computadoras o +solicitar datos del ((disco duro))—lo que es mucho más lento que +obtenerlos desde la ((memoria)). -But many programs interact with things outside of the processor. For -example, they may communicate over a computer network or request -data from the ((hard disk))—which is a lot slower than getting it from -memory. +Cuando una cosa como tal este sucediendo, sería una pena dejar que +el procesador se mantenga inactivo—podría haber algún otro trabajo que +este pueda hacer en el mientras tanto. En parte, esto es manejado por tu +sistema operativo, que cambiará el procesador entre múltiples programas en +ejecución. Pero eso no ayuda cuando queremos que un _unico_ programa pueda +hacer progreso mientras este espera una solicitud de red. -When such a thing is happening, it would be a shame to let the -processor sit idle—there might be some other work it could do in the -meantime. In part, this is handled by your operating system, which -will switch the processor between multiple running programs. But that -doesn't help when we want a _single_ program to be able to make -progress while it is waiting for a network request. - -## Asynchronicity +## Asincronicidad {{index "synchronous programming"}} -In a _synchronous_ programming model, things happen one at a time. -When you call a function that performs a long-running action, it -returns only when the action has finished and it can return the result. -This stops your program for the time the action takes. +En un modelo de programación _sincrónico_, las cosas suceden una a la vez. +Cuando llamas a una función que realiza una acción de larga duración, solo +retorna cuando la acción ha terminado y puede retornar el resultado. +Esto detiene tu programa durante el tiempo que tome la acción. {{index "asynchronous programming"}} -An _asynchronous_ model allows multiple things to happen at the same -time. When you start an action, your program continues to run. When -the action finishes, the program is informed and gets access to the -result (for example, the data read from disk). +Un modelo _asincrónico_ permite que ocurran varias cosas al mismo tiempo. +Cuando comienzas una acción, tu programa continúa ejecutándose. Cuando +la acción termina, el programa es informado y tiene acceso al +resultado (por ejemplo, los datos leídos del disco). -We can compare synchronous and asynchronous programming using a small -example: a program that fetches two resources from the ((network)) and -then combines results. +Podemos comparar a la programación síncrona y asincrónica usando un pequeño +ejemplo: un programa que obtiene dos recursos de la ((red)) y +luego combina resultados. {{index "synchronous programming"}} -In a synchronous environment, where the request function returns only -after it has done its work, the easiest way to perform this task is to -make the requests one after the other. This has the drawback that the -second request will be started only when the first has finished. The -total time taken will be at least the sum of the two response times. +En un entorno síncrono, donde la función de solicitud solo retorna +una vez que ha hecho su trabajo, la forma más fácil de realizar esta tarea es +realizar las solicitudes una después de la otra. Esto tiene el inconveniente de +que la segunda solicitud se iniciará solo cuando la primera haya finalizado. +El tiempo total de ejecución será como minimo la suma de los dos tiempos de +respuesta. {{index parallelism}} -The solution to this problem, in a synchronous system, is to start -additional ((thread))s of control. A _thread_ is another running program -whose execution may be interleaved with other programs by the -operating system—since most modern computers contain multiple -processors, multiple threads may even run at the same time, on -different processors. A second thread could start the second request, -and then both threads wait for their results to come back, after which -they resynchronize to combine their results. +La solución a este problema, en un sistema síncrono, es comenzar +((hilo))s adicionales de control. Un _hilo_ es otro programa activo +cuya ejecución puede ser intercalada con otros programas por el +sistema operativo—ya que la mayoría de las computadoras modernas contienen +múltiples procesadores, múltiples hilos pueden incluso ejecutarse al mismo +tiempo, en diferentes procesadores. Un segundo hilo podría iniciar la segunda +solicitud, y luego ambos subprocesos esperan a que los resultados vuelvan, +después de lo cual se vuelven a resincronizar para combinar sus resultados. {{index CPU, blocking, "asynchronous programming", timeline, "callback function"}} -In the following diagram, the thick lines represent time the program -spends running normally, and the thin lines represent time spent -waiting for the network. In the synchronous model, the time taken by -the network is _part_ of the timeline for a given thread of control. -In the asynchronous model, starting a network action conceptually -causes a _split_ in the timeline. The program that initiated the -action continues running, and the action happens alongside it, -notifying the program when it is finished. +En el siguiente diagrama, las líneas gruesas representan el tiempo que el +programa pasa corriendo normalmente, y las líneas finas representan el tiempo +pasado esperando la red. En el modelo síncrono, el tiempo empleado por +la red es _parte_ de la línea de tiempo para un hilo de control dado. +En el modelo asincrónico, comenzar una acción de red conceptualmente +causa una _división_ en la línea del tiempo. El programa que inició +la acción continúa ejecutándose, y la acción ocurre junto a el, +notificando al programa cuando está termina. {{figure {url: "img/control-io.svg", alt: "Control flow for synchronous and asynchronous programming",width: "8cm"}}} -{{index ["control flow", asynchronous], "asynchronous programming", verbosity}} - -Another way to describe the difference is that waiting for actions to -finish is _implicit_ in the synchronous model, while it is _explicit_, -under our control, in the asynchronous one. - -Asynchronicity cuts both ways. It makes expressing programs that do -not fit the straight-line model of control easier, but it can also -make expressing programs that do follow a straight line more awkward. -We'll see some ways to address this awkwardness later in the chapter. - -Both of the important JavaScript programming platforms—((browser))s -and ((Node.js))—make operations that might take a while asynchronous, -rather than relying on ((thread))s. Since programming with threads is -notoriously hard (understanding what a program does is much more -difficult when it's doing multiple things at once), this is generally -considered a good thing. - -## Crow tech - -Most people are aware of the fact that ((crow))s are very smart birds. -They can use tools, plan ahead, remember things, and even communicate -these things among themselves. - -What most people don't know is that they are capable of many things -that they keep well hidden from us. I've been told by a reputable (if -somewhat eccentric) expert on ((corvid))s that crow technology is not -far behind human technology, and they are catching up. - -For example, many crow cultures have the ability to construct -computing devices. These are not electronic, as human computing -devices are, but operate through the actions of tiny insects, a -species closely related to the ((termite)), which has developed a -((symbiotic relationship)) with the crows. The birds provide them with -food, and in return the insects build and operate their complex -colonies that, with the help of the living creatures inside them, -perform computations. - -Such colonies are usually located in big, long-lived nests. The birds -and insects work together to build a network of bulbous clay -structures, hidden between the twigs of the nest, in which the insects -live and work. - -To communicate with other devices, these machines use light signals. -The crows embed pieces of reflective material in special communication -stalks, and the insects aim these to reflect light at another nest, -encoding data as a sequence of quick flashes. This means that only -nests that have an unbroken visual connection can communicate. - -Our friend the corvid expert has mapped the network of crow nests in -the village of ((Hières-sur-Amby)), on the banks of the river Rhône. -This map shows the nests and their connections: +{{index "control flow", "asynchronous programming", verbosity}} + +Otra forma de describir la diferencia es que esperar que las acciones +terminen es _implicito_ en el modelo síncrono, mientras que es _explicito_, +bajo nuestro control, en el asincrónico. + +La asincronicidad corta en ambos sentidos. Hace que expresar programas que +hagan algo no se ajuste al modelo de control lineal más fácil, pero también +puede hacer que expresar programas que siguen una línea recta sea más +incómodo. Veremos algunas formas de abordar esta incomodidad más adelante en +el capítulo. + +Ambas de las plataformas de programación JavaScript importantes—((navegadore))s +y ((Node.js))—realizan operaciones que pueden tomar un tiempo asincrónicamente, +en lugar de confiar en ((hilo))s. Dado que la programación con hilos es +notoriamente difícil (entender lo que hace un programa es mucho más +difícil cuando está haciendo varias cosas a la vez), esto es generalmente +considerado una buena cosa. + +## Tecnología cuervo + +La mayoría de las personas son conscientes del hecho de que los ((cuervo))s +son pájaros muy inteligentes. Pueden usar herramientas, planear con anticipación, +recordar cosas e incluso comunicarse estas cosas entre ellos. + +Lo que la mayoría de la gente no sabe, es que son capaces de hacer muchas cosas +que mantienen bien escondidas de nosotros. Personas de buena +reputación (un tanto excéntricas) expertas en ((córvidos)), me han dicho +que la tecnología cuervo no esta muy por detrás de la tecnología humana, +y que nos estan alcanzando. + +Por ejemplo, muchas culturas cuervo tienen la capacidad de construir +dispositivos informáticos. Estos no son electrónicos, como lo son +los dispositivos informáticos humanos, pero operan a través de las acciones de +pequeños insectos, una especie estrechamente relacionada con las ((termitas)), +que ha desarrollado una ((relación simbiótica)) con los cuervos. Los pájaros +les proporcionan comida, y a cambio los insectos construyen y operan sus +complejas colonias que, con la ayuda de las criaturas vivientes dentro de ellos, +realizan computaciones. + +Tales colonias generalmente se encuentran en nidos grandes de larga vida. +Las aves e insectos trabajan juntos para construir una red de estructuras +bulbosas hechas de arcilla, escondidas entre las ramitas del nido, +en el que los insectos viven y trabajan. + +Para comunicarse con otros dispositivos, estas máquinas usan señales de luz. +Los cuervos incrustan piezas de material reflectante en tallos de +comunicación especial, y los insectos apuntan estos para reflejar la luz hacia +otro nido, codificando los datos como una secuencia de flashes rápidos. +Esto significa que solo los nidos que tienen una conexión visual ininterrumpida +pueden comunicarse entre ellos. + +Nuestro amigo, el experto en córvidos, ha mapeado la red de nidos de cuervo en +el pueblo de ((Hières-sur-Amby)), a orillas del río Ródano. +Este mapa muestra los nidos y sus conexiones. {{figure {url: "img/Hieres-sur-Amby.png", alt: "A network of crow nests in a small village"}}} -In an astounding example of ((convergent evolution)), crow computers -run JavaScript. In this chapter we'll write some basic networking -functions for them. +En un ejemplo asombroso de ((evolución convergente)), las computadoras cuervo +ejecutan JavaScript. En este capítulo vamos a escribir algunas funciones de +redes básicas para ellos. -## Callbacks +## Devolución de llamadas {{indexsee [function, callback], "callback function"}} -One approach to ((asynchronous programming)) is to make functions that -perform a slow action take an extra argument, a _((callback -function))_. The action is started, and when it finishes, the callback -function is called with the result. +Un enfoque para la ((programación asincrónica)) es hacer que las funciones que +realizan una acción lenta, tomen un argumento adicional, una _((función de +devolución de llamada))_. La acción se inicia y, cuando esta finaliza, +la función de devolución es llamada con el resultado. {{index "setTimeout function", waiting}} -As an example, the `setTimeout` function, available both in Node.js -and in browsers, waits a given number of milliseconds (a second is a -thousand milliseconds) and then calls a function. +Como ejemplo, la función `setTimeout`, disponible tanto en Node.js +como en navegadores, espera una cantidad determinada de milisegundos +(un segundo son mil milisegundos) y luego llama una función. ```{test: no} setTimeout(() => console.log("Tick"), 500); ``` -Waiting is not generally a very important type of work, but it can be -useful when doing something like updating an animation or checking whether -something is taking longer than a given amount of ((time)). +Esperar no es generalmente un tipo de trabajo muy importante, pero puede ser +útil cuando se hace algo como actualizar una animación o verificar si +algo está tardando más que una cantidad dada de ((tiempo)). -Performing multiple asynchronous actions in a row using callbacks -means that you have to keep passing new functions to handle the -((continuation)) of the computation after the actions. +La realización de múltiples acciones asíncronas en una fila utilizando +devoluciones de llamada significa que debes seguir pasando nuevas funciones para +manejar la ((continuación)) de la computación después de las acciones. {{index "hard disk"}} -Most crow nest computers have a long-term data storage bulb, where -pieces of information are etched into twigs so that they can be -retrieved later. Etching, or finding a piece of data, takes a moment, so -the interface to long-term storage is asynchronous and uses callback -functions. +La mayoría de las computadoras en los nidos de los cuervos tienen un +bulbo de almacenamiento de datos a largo plazo, donde las piezas de información +se graban en ramitas para que estas puedan ser recuperadas más tarde. +Grabar o encontrar un fragmento de información requiere un momento, por lo que +la interfaz para el almacenamiento a largo plazo es asíncrona y utiliza +funciones de devolución de llamada. -Storage bulbs store pieces of ((JSON))-encodable data under names. A -((crow)) might store information about the places where it's hidden food under the name `"food caches"`, which could hold an array of -names that point at other pieces of data, describing the actual cache. -To look up a food ((cache)) in the storage bulbs of the _Big Oak_ -nest, a crow could run code like this: +Los bulbos de almacenamiento almacenan piezas de ((JSON))-datos codificables +bajo nombres. Un ((cuervo)) podría almacenar información sobre los lugares +donde hay comida escondida bajo el nombre `"caches de alimentos"`, que podría +contener un array de nombres que apuntan a otros datos, que describen el caché +real. Para buscar un ((caché)) de alimento en los bulbos de almacenamiento del +nido _Gran Roble_, un cuervo podría ejecutar código como este: -{{index "readStorage function"}} +{{index "leerAlmacenamiento function"}} ```{includeCode: "top_lines: 1"} -import {bigOak} from "./crow-tech"; +import {granRoble} from "./tecnologia-cuervo"; -bigOak.readStorage("food caches", caches => { - let firstCache = caches[0]; - bigOak.readStorage(firstCache, info => { - console.log(info); +granRoble.leerAlmacenamiento("caches de alimentos", caches => { + let primerCache = caches[0]; + granRoble.leerAlmacenamiento(primerCache, informacion => { + console.log(informacion); }); }); ``` -(All binding names and strings have been translated from crow language -to English.) +(Todos los nombres de las vinculaciones y los strings se han traducido del +lenguaje cuervo a Español.) -This style of programming is workable, but the indentation level -increases with each asynchronous action because you end up in another -function. Doing more complicated things, such as running multiple actions -at the same time, can get a little awkward. +Este estilo de programación es viable, pero el nivel de indentación +aumenta con cada acción asincrónica, ya que terminas en otra +función. Hacer cosas más complicadas, como ejecutar múltiples acciones +al mismo tiempo, puede ser un poco incómodo. -Crow nest computers are built to communicate using -((request))-((response)) pairs. That means one nest sends a message to -another nest, which then immediately sends a message back, confirming -receipt and possibly including a reply to a question asked in the -message. +Las computadoras cuervo están construidas para comunicarse usando pares de +((solicitud))-((respuesta)). Eso significa que un nido envía un mensaje a +otro nido, el cual inmediatamente envía un mensaje de vuelta, confirmando el +recibo y, posiblemente, incluyendo una respuesta a una pregunta formulada +en el mensaje. -Each message is tagged with a _type_, which determines how it is -handled. Our code can define handlers for specific request types, and -when such a request comes in, the handler is called to produce a -response. +Cada mensaje está etiquetado con un _tipo_, que determina cómo este es +manejado. Nuestro código puede definir manejadores para tipos de solicitud +específicos, y cuando se recibe una solicitud de este tipo, se llama al +controlador para que este produzca una respuesta. {{index "crow-tech module", "send method"}} -The interface exported by the `"./crow-tech"` module provides -callback-based functions for communication. Nests have a `send` method -that sends off a request. It expects the name of the target nest, the -type of the request, and the content of the request as its first three -arguments, and it expects a function to call when a response comes in as its -fourth and last argument. +La interfaz exportada por el módulo `"./tecnologia-cuervo"` proporciona +funciones de devolución de llamada para la comunicación. Los nidos tienen un +método `enviar` que envía una solicitud. Este espera el nombre del nido +objetivo, el tipo de solicitud y el contenido de la solicitud como sus +primeros tres argumentos, y una función a llamar cuando llega una respuesta +como su cuarto y último argumento. ``` -bigOak.send("Cow Pasture", "note", "Let's caw loudly at 7PM", - () => console.log("Note delivered.")); +granRoble.send("Pastura de Vacas", "nota", "Vamos a graznar fuerte a las 7PM", + () => console.log("Nota entregada.")); ``` -But to make nests capable of receiving that request, we first have to -define a ((request type)) named `"note"`. The code that handles the -requests has to run not just on this nest-computer but on all nests -that can receive messages of this type. We'll just assume that a crow -flies over and installs our handler code on all the nests. +Pero para hacer nidos capaces de recibir esa solicitud, primero tenemos que +definir un ((tipo de solicitud)) llamado `"nota"`. El código que maneja +las solicitudes debe ejecutarse no solo en este nido-computadora, sino en +todos los nidos que puedan recibir mensajes de este tipo. Asumiremos que un +cuervo sobrevuela e instala nuestro código controlador en todos los nidos. -{{index "defineRequestType function"}} +{{index "definirTipoSolicitud function"}} ```{includeCode: true} -import {defineRequestType} from "./crow-tech"; +import {definirTipoSolicitud} from "./tecnologia-cuervo"; -defineRequestType("note", (nest, content, source, done) => { - console.log(`${nest.name} received note: ${content}`); - done(); +definirTipoSolicitud("nota", (nido, contenido, fuente, listo) => { + console.log(`${nido.nombre} recibio nota: ${contenido}`); + listo(); }); ``` -The `defineRequestType` function defines a new type of request. The -example adds support for `"note"` requests, which just sends a note to -a given nest. Our implementation calls `console.log` so that we can -verify that the request arrived. Nests have a `name` property that -holds their name. +La función `definirTipoSolicitud` define un nuevo tipo de solicitud. El +ejemplo agrega soporte para solicitudes de tipo `"nota"`, que simplemente envían +una nota a un nido dado. Nuestra implementación llama a `console.log` para que +podamos verificar que la solicitud llegó. Los nidos tienen una propiedad +`nombre` que contiene su nombre. {{index "asynchronous programming"}} -The fourth argument given to the handler, `done`, is a callback -function that it must call when it is done with the request. If we had -used the handler's ((return value)) as the response value, that would -mean that a request handler can't itself perform asynchronous actions. -A function doing asynchronous work typically returns before the work -is done, having arranged for a callback to be called when it -completes. So we need some asynchronous mechanism—in this case, -another ((callback function))—to signal when a response is available. - -In a way, asynchronicity is _contagious_. Any function that calls a -function that works asynchronously must itself be asynchronous, using -a callback or similar mechanism to deliver its result. Calling a -callback is somewhat more involved and error-prone than simply -returning a value, so needing to structure large parts of your program -that way is not great. - -## Promises - -Working with abstract concepts is often easier when those concepts can -be represented by ((value))s. In the case of asynchronous actions, you -could, instead of arranging for a function to be called at some point -in the future, return an object that represents this future event. +El cuarto argumento dado al controlador, `listo`, es una función de +devolución de llamada que debe ser llamada cuando se finaliza con la solicitud. +Si hubiesemos utilizado el ((valor de retorno)) del controlador como el valor +de respuesta, eso significaria que un controlador de solicitud no puede realizar +acciones asincrónicas por sí mismo. Una función que realiza trabajos asíncronos +normalmente retorna antes de que el trabajo este hecho, habiendo arreglado que se +llame una devolución de llamada cuando este completada. Entonces, necesitamos algún +mecanismo asíncrono, en este caso, otra ((función de devolución de +llamada))—para indicar cuándo hay una respuesta disponible. + +En cierto modo, la asincronía es _contagiosa_. Cualquier función que llame a una +función que funcione asincrónicamente debe ser asíncrona en si misma, utilizando +una devolución de llamada o algun mecanismo similar para entregar su resultado. +Llamar devoluciones de llamada es algo más involucrado y propenso a errores que +simplemente retornar un valor, por lo que necesitar estructurar grandes +partes de tu programa de esa manera no es algo muy bueno. + +## Promesas + +Trabajar con conceptos abstractos es a menudo más fácil cuando esos conceptos +pueden ser representados por ((valores)). En el caso de acciones asíncronas, +podrías, en lugar de organizar a una función para que esta sea llamada +en algún momento en el futuro, retornar un objeto que represente este +evento en el futuro. {{index "Promise class", "asynchronous programming"}} -This is what the standard class `Promise` is for. A _promise_ is an -asynchronous action that may complete at some point and produce a -value. It is able to notify anyone who is interested when its value is -available. +Esto es para lo que es la clase estándar `Promise` ("Promesa"). +Una _promesa_ es una acción asíncrona que puede completarse en algún punto +y producir un valor. Esta puede notificar a cualquier persona que esté +interesada cuando su valor este disponible. {{index "Promise.resolve function", "resolving (a promise)"}} -The easiest way to create a promise is by calling `Promise.resolve`. -This function ensures that the value you give it is wrapped in a -promise. If it's already a promise, it is simply returned—otherwise, -you get a new promise that immediately finishes with your -value as its result. +La forma más fácil de crear una promesa es llamando a `Promise.resolve` ("Promesa.resolver"). +Esta función se asegura de que el valor que le des, sea envuelto en una +promesa. Si ya es una promesa, simplemente es retornada—de lo contrario, +obtienes una nueva promesa que termina de inmediato con tu +valor como su resultado. ``` -let fifteen = Promise.resolve(15); -fifteen.then(value => console.log(`Got ${value}`)); -// → Got 15 +let quince = Promise.resolve(15); +quince.then(valor => console.log(`Obtuve ${valor}`)); +// → Obtuve 15 ``` {{index "then method"}} -To get the result of a promise, you can use its `then` method. This -registers a ((callback function)) to be called when the promise -resolves and produces a value. You can add multiple callbacks to a -single promise, and they will be called, even if you add them after -the promise has already _resolved_ (finished). +Para obtener el resultado de una promesa, puede usar su método `then` ("entonces"). +Este registra una (función de devolución de llamada) para que sea llamada cuando la +promesa resuelva y produzca un valor. Puedes agregar múltiples devoluciones de +llamada a una única promesa, y serán llamadas, incluso si las agregas después +de que la promesa ya haya sido _resuelta_ (terminada). -But that's not all the `then` method does. It returns another promise, -which resolves to the value that the handler function returns or, if -that returns a promise, waits for that promise and then resolves to -its result. +Pero eso no es todo lo que hace el método `then`. Este retorna otra promesa, +que resuelve al valor que retorna la función del controlador o, si +esa retorna una promesa, espera por esa promesa y luego resuelve +su resultado. -It is useful to think of promises as a device to move values into an -asynchronous reality. A normal value is simply there. A promised value -is a value that _might_ already be there or might appear at some point -in the future. Computations defined in terms of promises act on such -wrapped values and are executed asynchronously as the values become -available. +Es útil pensar acerca de las promesas como dispositivos para mover valores a una +realidad asincrónica. Un valor normal simplemente esta allí. Un valor prometido +es un valor que _podría_ ya estar allí o podría aparecer en algún momento +en el futuro. Las computaciones definidas en términos de promesas actúan en +tales valores envueltos y se ejecutan de forma asíncrona a medida los +valores se vuelven disponibles. {{index "Promise class"}} -To create a promise, you can use `Promise` as a constructor. It has a -somewhat odd interface—the constructor expects a function as argument, -which it immediately calls, passing it a function that it can use to -resolve the promise. It works this way, instead of for example with a -`resolve` method, so that only the code that created the promise can -resolve it. +Para crear una promesa, puedes usar `Promise` como un constructor. Tiene una +interfaz algo extraña—el constructor espera una función como argumento, a la +cual llama inmediatamente, pasando una función que puede usar para +resolver la promesa. Funciona de esta manera, en lugar de, por ejemplo, con un +método `resolve`, de modo que solo el código que creó la promesa pueda +resolverla. {{index "storage function"}} -This is how you'd create a promise-based interface for the -`readStorage` function: +Así es como crearía una interfaz basada en promesas para la función +`leerAlmacenamiento`. ```{includeCode: "top_lines: 5"} -function storage(nest, name) { +function almacenamiento(nido, nombre) { return new Promise(resolve => { - nest.readStorage(name, result => resolve(result)); + nido.leerAlmacenamiento(nombre, resultado => resolve(resultado)); }); } -storage(bigOak, "enemies") - .then(value => console.log("Got", value)); +almacenamiento(granRoble, "enemigos") + .then(valor => console.log("Obtuve", valor)); ``` -This asynchronous function returns a meaningful value. This is the -main advantage of promises—they simplify the use of asynchronous -functions. Instead of having to pass around callbacks, promise-based -functions look similar to regular ones: they take input as arguments -and return their output. The only difference is that the output may -not be available yet. +Esta función asíncrona retorna un valor significativo. Esta es la +principal ventaja de las promesas—simplifican el uso de funciones asincrónicas. +En lugar de tener que pasar devoluciones de llamadas, las funciones basadas en +promesas son similares a las normales: toman entradas como +argumentos y retornan su resultado. La única diferencia es que la salida +puede que no este disponible inmediatamente. -## Failure +## Fracaso {{index "exception handling"}} -Regular JavaScript computations can fail by throwing an exception. -Asynchronous computations often need something like that. A network -request may fail, or some code that is part of the asynchronous -computation may throw an exception. +Las computaciones regulares en JavaScript pueden fallar lanzando una excepción. +Las computaciones asincrónicas a menudo necesitan algo así. Una solicitud de +red puede fallar, o algún código que sea parte de la computación asincrónica +puede arrojar una excepción. {{index "callback function", error}} -One of the most pressing problems with the callback style of -asynchronous programming is that it makes it extremely difficult to -make sure failures are properly reported to the callbacks. +Uno de los problemas más urgentes con el estilo de devolución de llamadas +en la programación asíncrona es que hace que sea extremadamente difícil +asegurarte de que las fallas sean reportadas correctamente a las devoluciones de +llamada. -A widely used convention is that the first argument to the callback is -used to indicate that the action failed, and the second contains the -value produced by the action when it was successful. Such callback -functions must always check whether they received an exception and -make sure that any problems they cause, including exceptions thrown by -functions they call, are caught and given to the right function. +Una convención ampliamente utilizada es que el primer argumento para la +devolución de llamada es usado para indicar que la acción falló, y el segundo +contiene el valor producido por la acción cuando tuvo éxito. Tales funciones +de devolución de llamadas siempre deben verificar si recibieron una excepción, y +asegurarse de que cualquier problema que causen, incluidas las excepciones +lanzadas por las funciones que estas llaman, sean atrapadas y entregadas a la +función correcta. {{index "rejecting (a promise)", "resolving (a promise)", "then method"}} -Promises make this easier. They can be either resolved (the action -finished successfully) or rejected (it failed). Resolve handlers (as -registered with `then`) are called only when the action is successful, -and rejections are automatically propagated to the new promise that is -returned by `then`. And when a handler throws an exception, this -automatically causes the promise produced by its `then` call to be -rejected. So if any element in a chain of asynchronous actions fails, -the outcome of the whole chain is marked as rejected, and no success -handlers are called beyond the point where it failed. +Las promesas hacen esto más fácil. Estas pueden ser resueltas (la acción +termino con éxito) o rechazadas (esta falló). Los controladores de resolución +(registrados con `then`) solo se llaman cuando la acción es exitosa, +y los rechazos se propagan automáticamente a la nueva promesa que es +retornada por `then`. Y cuando un controlador arroje una excepción, esto +automáticamente hace que la promesa producida por su llamada `then` sea +rechazada. Entonces, si cualquier elemento en una cadena de acciones +asíncronas falla, el resultado de toda la cadena se marca como rechazado, y no +se llaman más manejadores despues del punto en donde falló. {{index "Promise.reject function", "Promise class"}} -Much like resolving a promise provides a value, rejecting one also -provides one, usually called the _reason_ of the rejection. When an -exception in a handler function causes the rejection, the exception -value is used as the reason. Similarly, when a handler returns a -promise that is rejected, that rejection flows into the next promise. -There's a `Promise.reject` function that creates a new, -immediately rejected promise. +Al igual que resolver una promesa proporciona un valor, rechazar una también +proporciona uno, generalmente llamado la _razón_ el rechazo. Cuando una +excepción en una función de controlador provoca el rechazo, el valor de +la excepción se usa como la razón. Del mismo modo, cuando un controlador retorna +una promesa que es rechazada, ese rechazo fluye hacia la próxima promesa. +Hay una función `Promise.reject` que crea una nueva promesa +inmediatamente rechazada. {{index "catch method"}} -To explicitly handle such rejections, promises have a `catch` method -that registers a handler to be called when the promise is rejected, -similar to how `then` handlers handle normal resolution. It's also very -much like `then` in that it returns a new promise, which resolves to -the original promise's value if it resolves normally and to the -result of the `catch` handler otherwise. If a `catch` handler throws -an error, the new promise is also rejected. +Para manejar explícitamente tales rechazos, las promesas tienen un método +`catch` ("atraoar") que registra un controlador para que sea llamado cuando se rechaze la +promesa, similar a cómo los manejadores `then` manejan la resolución normal. +También es muy parecido a `then` en que retorna una nueva promesa, que se +resuelve en el valor de la promesa original si esta se resuelve normalmente, y al +resultado del controlador `catch` de lo contrario. Si un controlador `catch` +lanza un error, la nueva promesa también es rechazada. {{index "then method"}} -As a shorthand, `then` also accepts a rejection handler as a second -argument, so you can install both types of handlers in a single method -call. +Como una abreviatura, `then` también acepta un manejador de rechazo como +segundo argumento, por lo que puedes instalar ambos tipos de controladores en +un solo método de llamada. -A function passed to the `Promise` constructor receives a second -argument, alongside the resolve function, which it can use to reject -the new promise. +Una función que se pasa al constructor `Promise` recibe un segundo +argumento, junto con la función de resolución, que puede usar para rechazar +la nueva promesa. -The chains of promise values created by calls to `then` and `catch` -can be seen as a pipeline through which asynchronous values or -failures move. Since such chains are created by registering handlers, -each link has a success handler or a rejection handler (or both) -associated with it. Handlers that don't match the type of outcome -(success or failure) are ignored. But those that do match are called, -and their outcome determines what kind of value comes next—success -when it returns a non-promise value, rejection when it throws an -exception, and the outcome of a promise when it returns one of those. - -```{test: no} -new Promise((_, reject) => reject(new Error("Fail"))) - .then(value => console.log("Handler 1")) - .catch(reason => { - console.log("Caught failure " + reason); - return "nothing"; - }) - .then(value => console.log("Handler 2", value)); -// → Caught failure Error: Fail -// → Handler 2 nothing -``` +Las cadenas de promesas creadas por llamadas a `then` y `catch` +puede verse como una tubería a través de la cual los valores asíncronicos o +las fallas se mueven. Dado que tales cadenas se crean mediante el registro de +controladores, cada enlace tiene un controlador de éxito o un controlador de +rechazo (o ambos) asociados a ello. Controladores que no coinciden con ese tipo +de resultados (éxito o fracaso) son ignorados. Pero los que sí coinciden son +llamados, y su resultado determina qué tipo de valor viene después—éxito +cuando retorna un valor que no es una promesa, rechazo cuando arroja una +excepción, y el resultado de una promesa cuando retorna una de esas. {{index "uncaught exception", "exception handling"}} -Much like an uncaught exception is handled by the environment, -JavaScript environments can detect when a promise rejection isn't -handled and will report this as an error. +Al igual que una excepción no detectada es manejada por el entorno, +Los entornos de JavaScript pueden detectar cuándo una promesa rechazada no es +manejada, y reportará esto como un error. -## Networks are hard +## Las redes son difíciles -{{index [network, reliability]}} +{{index network}} -Occasionally, there isn't enough light for the ((crow))s' mirror -systems to transmit a signal or something is blocking the path of the -signal. It is possible for a signal to be sent but never received. +Ocasionalmente, no hay suficiente luz para los sistemas de espejos de los +cuervos para transmitir una señal, o algo bloquea el camino de la +señal. Es posible que se envíe una señal, pero que nunca se reciba. {{index "send method", error, timeout}} -As it is, that will just cause the callback given to `send` to never -be called, which will probably cause the program to stop without even -noticing there is a problem. It would be nice if, after a given period -of not getting a response, a request would _time out_ and report -failure. - -Often, transmission failures are random accidents, like a car's -headlight interfering with the light signals, and simply retrying the -request may cause it to succeed. So while we're at it, let's make our -request function automatically retry the sending of the request a few -times before it gives up. - -{{index "Promise class", "callback function", [interface, object]}} - -And, since we've established that promises are a good thing, we'll -also make our request function return a promise. In terms of what they -can express, callbacks and promises are equivalent. Callback-based -functions can be wrapped to expose a promise-based interface, and -vice versa. - -Even when a ((request)) and its ((response)) are successfully -delivered, the response may indicate failure—for example, if the -request tries to use a request type that hasn't been defined or the -handler throws an error. To support this, `send` and -`defineRequestType` follow the convention mentioned before, where the -first argument passed to callbacks is the failure reason, if any, and -the second is the actual result. - -These can be translated to promise resolution and rejection by our -wrapper. +Tal y como es, eso solo causará que la devolución de llamada dada a `send` nunca +sea llamada, lo que probablemente hará que el programa se detenga sin siquiera +notar que hay un problema. Sería bueno si, después de un determinado período +de no obtener una respuesta, una solicitud _expirará_ e informara de un +fracaso. + +A menudo, las fallas de transmisión son accidentes aleatorios, como la +luz del faro de un auto interfieriendo con las señales de luz, y simplemente +volver a intentar la solicitud puede hacer que esta tenga éxito. Entonces, +mientras estamos en eso, hagamos que nuestra función de solicitud +automáticamente reintente el envío de la solicitud momentos antes de que se de +por vencida. + +{{index "Promise class", "callback function", interface}} + +Y, como hemos establecido que las promesas son algo bueno, también haremos +que nuestra función de solicitud retorne una promesa. En +términos de lo que pueden expresar, las devoluciones de llamada y las promesas +son equivalentes. Las funciones basadas en devoluciones de llamadas se pueden +envolver para exponer una interfaz basada en promesas, y viceversa. + +Incluso cuando una ((solicitud)) y su ((respuesta)) sean entregadas +exitosamente, la respuesta puede indicar un error—por ejemplo, si la +solicitud intenta utilizar un tipo de solicitud que no haya sido definida +o si el controlador genera un error. Para soportar esto, `send` y +`definirTipoSolicitud` siguen la convención mencionada anteriormente, donde +el primer argumento pasado a las devoluciones de llamada es el motivo del +fallo, si lo hay, y el segundo es el resultado real. + +Estos pueden ser traducidos para prometer resolución y rechazo por parte de +nuestra envoltura. {{index "Timeout class", "request function", retry}} ```{includeCode: true} -class Timeout extends Error {} +class TiempoDeEspera extends Error {} -function request(nest, target, type, content) { +function request(nido, objetivo, tipo, contenido) { return new Promise((resolve, reject) => { - let done = false; - function attempt(n) { - nest.send(target, type, content, (failed, value) => { - done = true; - if (failed) reject(failed); + let listo = false; + function intentar(n) { + nido.send(objetivo, tipo, contenido, (fallo, value) => { + listo = true; + if (fallo) reject(fallo); else resolve(value); }); setTimeout(() => { - if (done) return; - else if (n < 3) attempt(n + 1); - else reject(new Timeout("Timed out")); + if (listo) return; + else if (n < 3) intentar(n + 1); + else reject(new TiempoDeEspera("Tiempo de espera agotado")); }, 250); } - attempt(1); + intentar(1); }); } ``` {{index "Promise class", "resolving (a promise)", "rejecting (a promise)"}} -Because promises can be resolved (or rejected) only once, this will -work. The first time `resolve` or `reject` is called determines the -outcome of the promise, and any further calls, such as the timeout -arriving after the request finishes or a request coming back after -another request finished, are ignored. +Debido a que las promesas solo se pueden resolver (o rechazar) una vez, esto +funcionara. La primera vez que se llame a `resolve` o `reject` se determinara el +resultado de la promesa y cualquier llamada subsecuente, como el tiempo de espera +que llega después de que finaliza la solicitud, o una solicitud que regresa +después de que otra solicitud es finalizada, es ignorada. {{index recursion}} -To build an asynchronous ((loop)), for the retries, we need to use a -recursive function—a regular loop doesn't allow us to stop and wait -for an asynchronous action. The `attempt` function makes a single -attempt to send a request. It also sets a timeout that, if no response -has come back after 250 milliseconds, either starts the next attempt -or, if this was the fourth attempt, rejects the promise with an -instance of `Timeout` as the reason. +Para construir un ((ciclo)) asincrónico, para los reintentos, necesitamos usar +un función recursiva—un ciclo regular no nos permite detenernos y esperar +por una acción asincrónica. La función `intentar` hace un solo +intento de enviar una solicitud. También establece un tiempo de espera que, si +no ha regresado una respuesta después de 250 milisegundos, comienza el +próximo intento o, si este es el cuarto intento, rechaza la promesa con una +instancia de `TiempoDeEspera` como la razón. {{index idempotence}} -Retrying every quarter-second and giving up when no response has come -in after a second is definitely somewhat arbitrary. It is even -possible, if the request did come through but the handler is just -taking a bit longer, for requests to be delivered multiple times. -We'll write our handlers with that problem in mind—duplicate messages -should be harmless. +Volver a intentar cada cuarto de segundo y rendirse cuando no ha +llegado ninguna respuesta después de un segundo es algo definitivamente +arbitrario. Es incluso posible, si la solicitud llegó pero el controlador se +esta tardando un poco más, que las solicitudes se entreguen varias veces. +Escribiremos nuestros manejadores con ese problema en mente—los mensajes +duplicados deberían de ser inofensivos. -In general, we will not be building a world-class, robust network -today. But that's okay—crows don't have very high expectations yet -when it comes to computing. +En general, no construiremos una red robusta de clase mundial +hoy. Pero eso esta bien—los cuervos no tienen expectativas muy altas todavía +cuando se trata de la computación. -{{index "defineRequestType function", "requestType function"}} +{{index "definirTipoSolicitud function", "requestType function"}} -To isolate ourselves from callbacks altogether, we'll go ahead and -also define a wrapper for `defineRequestType` that allows the handler -function to return a promise or plain value and wires that up to the -callback for us. +Para aislarnos por completo de las devoluciones de llamadas, seguiremos +adelante y también definiremos un contenedor para `definirTipoSolicitud` que +permite que la función controlador pueda retornar una promesa o valor normal, +y envia eso hasta la devolución de llamada para nosotros. ```{includeCode: true} -function requestType(name, handler) { - defineRequestType(name, (nest, content, source, - callback) => { +function tipoSolicitud(nombre, manejador) { + definirTipoSolicitud(nombre, (nido, contenido, fuente, + devolucionDeLlamada) => { try { - Promise.resolve(handler(nest, content, source)) - .then(response => callback(null, response), - failure => callback(failure)); + Promise.resolve(manejador(nido, contenido, fuente)) + .then(response => devolucionDeLlamada(null, response), + failure => devolucionDeLlamada(failure)); } catch (exception) { - callback(exception); + devolucionDeLlamada(exception); } }); } @@ -566,228 +563,231 @@ function requestType(name, handler) { {{index "Promise.resolve function"}} -`Promise.resolve` is used to convert the value returned by `handler` -to a promise if it isn't already. +`Promise.resolve` se usa para convertir el valor retornado por `manejador` +a una promesa si no es una ya. {{index "try keyword", "callback function"}} -Note that the call to `handler` had to be wrapped in a `try` block to -make sure any exception it raises directly is given to the callback. -This nicely illustrates the difficulty of properly handling errors -with raw callbacks—it is easy to forget to properly route -exceptions like that, and if you don't do it, failures won't get -reported to the right callback. Promises make this mostly automatic -and thus less error-prone. +Ten en cuenta que la llamada a `manejador` tenía que estar envuelta en un +bloque `try`, para asegurarse de que cualquier excepción que aparezca +directamente se le dé a la devolución de llamada. Esto ilustra muy bien la +dificultad de manejar adecuadamente los errores con devoluciones de llamada +crudas—es muy fácil olvidarse de encaminar correctamente excepciones como esa, +y si no lo haces, las fallas no se seran informadas a la devolución de +llamada correcta. Las promesas hacen esto casi automático, y por lo +tanto, son menos propensas a errores. -## Collections of promises +## Colecciones de promesas {{index "neighbors property", "ping request"}} -Each nest computer keeps an array of other nests within transmission -distance in its `neighbors` property. To check which of those are -currently reachable, you could write a function that tries to send a -`"ping"` request (a request that simply asks for a response) to each -of them and see which ones come back. +Cada computadora nido mantiene un array de otros nidos dentro de la distancia +de transmisión en su propiedad `vecinos`. Para verificar cuáles de +esos son actualmente accesibles, puede escribir una función que intente enviar +un solicitud `"ping"` (una solicitud que simplemente pregunta por una respuesta) +para cada de ellos, y ver cuáles regresan. {{index "Promise.all function"}} -When working with collections of promises running at the same time, -the `Promise.all` function can be useful. It returns a promise that -waits for all of the promises in the array to resolve and then -resolves to an array of the values that these promises produced (in -the same order as the original array). If any promise is rejected, the -result of `Promise.all` is itself rejected. +Al trabajar con colecciones de promesas que se ejecutan al mismo tiempo, +la función `Promise.all` puede ser útil. Esta retorna una promesa que +espera a que se resuelvan todas las promesas del array, y luego +resuelve un array de los valores que estas promesas produjeron (en +el mismo orden que en el array original). Si alguna promesa es rechazada, el +el resultado de `Promise.all` es en sí mismo rechazado. ```{includeCode: true} -requestType("ping", () => "pong"); +tipoSolicitud("ping", () => "pong"); -function availableNeighbors(nest) { - let requests = nest.neighbors.map(neighbor => { - return request(nest, neighbor, "ping") +function vecinosDisponibles(nido) { + let solicitudes = nido.vecinos.map(vecino => { + return request(nido, vecino, "ping") .then(() => true, () => false); }); - return Promise.all(requests).then(result => { - return nest.neighbors.filter((_, i) => result[i]); + return Promise.all(solicitudes).then(resultado => { + return nido.vecinos.filter((_, i) => resultado[i]); }); } ``` {{index "then method"}} -When a neighbor isn't available, we don't want the entire combined -promise to fail since then we still wouldn't know anything. So the -function that is mapped over the set of neighbors to turn them into -request promises attaches handlers that make successful requests -produce `true` and rejected ones produce `false`. +Cuando un vecino no este disponible, no queremos que todo la promesa combinada +falle, dado que entonces no sabríamos nada. Entonces la función +que es mappeada en el conjunto de vecinos para convertirlos en +promesas de solicitud vincula a los controladores que hacen las +solicitudes exitosas produzcan `true` y las rechazadas produzcan `false`. {{index "filter method", "map method", "some method"}} -In the handler for the combined promise, `filter` is used to remove -those elements from the `neighbors` array whose corresponding value is -false. This makes use of the fact that `filter` passes the array index -of the current element as a second argument to its filtering function -(`map`, `some`, and similar higher-order array methods do the same). +En el controlador de la promesa combinada, `filter` se usa para eliminar +esos elementos de la matriz `vecinos` cuyo valor correspondiente es +falso. Esto hace uso del hecho de que `filter` pasa el índice de matriz +del elemento actual como segundo argumento para su función de filtrado +(`map`,` some`, y métodos similares de orden superior de arrays hacen lo +mismo). -## Network flooding +## Inundación de red -The fact that nests can talk only to their neighbors greatly inhibits -the usefulness of this network. +El hecho de que los nidos solo pueden hablar con sus vecinos inhibe en gran +cantidad la utilidad de esta red. -For broadcasting information to the whole network, one solution is to -set up a type of request that is automatically forwarded to neighbors. -These neighbors then in turn forward it to their neighbors, until the -whole network has received the message. +Para transmitir información a toda la red, una solución es configurar un tipo +de solicitud que sea reenviada automáticamente a los vecinos. Estos vecinos +luego la envían a sus vecinos, hasta que toda la red ha recibido el mensaje. {{index "sendGossip function"}} ```{includeCode: true} -import {everywhere} from "./crow-tech"; +import {todosLados} from "./tecnologia-cuervo"; -everywhere(nest => { - nest.state.gossip = []; +todosLados(nido => { + nido.estado.chismorreo = []; }); -function sendGossip(nest, message, exceptFor = null) { - nest.state.gossip.push(message); - for (let neighbor of nest.neighbors) { - if (neighbor == exceptFor) continue; - request(nest, neighbor, "gossip", message); +function enviarChismorreo(nido, mensaje, exceptoPor = null) { + nido.estado.chismorreo.push(mensaje); + for (let vecino of nido.vecinos) { + if (vecino == exceptoPor) continue; + request(nido, vecino, "chismorreo", mensaje); } } -requestType("gossip", (nest, message, source) => { - if (nest.state.gossip.includes(message)) return; - console.log(`${nest.name} received gossip '${ - message}' from ${source}`); - sendGossip(nest, message, source); +requestType("chismorreo", (nido, mensaje, fuente) => { + if (nido.estado.chismorreo.includes(mensaje)) return; + console.log(`${nido.nombre} recibio chismorreo '${ + mensaje}' de ${fuente}`); + enviarChismorreo(nido, mensaje, fuente); }); ``` {{index "everywhere function", "gossip property"}} -To avoid sending the same message around the network forever, each -nest keeps an array of gossip strings that it has already seen. To -define this array, we use the `everywhere` function—which runs code on -every nest—to add a property to the nest's `state` object, which is -where we'll keep nest-local state. +Para evitar enviar el mismo mensaje a traves de la red por siempre, cada +nido mantiene un array de strings de chismorreos que ya ha visto. Para +definir este array, usaremos la función `todosLados`—que ejecuta +código en todos los nidos—para añadir una propiedad al objeto `estado` del nido, +que es donde mantendremos ((estado)) local del nido. -When a nest receives a duplicate gossip message, which is very likely -to happen with everybody blindly resending them, it ignores it. But -when it receives a new message, it excitedly tells all its neighbors -except for the one who sent it the message. +Cuando un nido recibe un mensaje de chisme duplicado, lo cual es muy probable +que suceda con todo el mundo reenviando estos a ciegas, lo ignora. Pero +cuando recibe un mensaje nuevo, emocionadamente le dice a todos sus vecinos +a excepción de quien le envió el mensaje. -This will cause a new piece of gossip to spread through the network -like an ink stain in water. Even when some connections aren't -currently working, if there is an alternative route to a given nest, -the gossip will reach it through there. +Esto provocará que una nueva pieza de chismes se propague a través de la red +como una mancha de tinta en agua. Incluso cuando algunas conexiones no estan +trabajando actualmente, si hay una ruta alternativa a un nido dado, +el chisme llegará hasta allí. {{index "flooding"}} -This style of network communication is called _flooding_—it floods the -network with a piece of information until all nodes have it. +Este estilo de comunicación de red se llama _inundamiento_-inunda la +red con una pieza de información hasta que todos los nodos la tengan. {{if interactive -We can call `sendGossip` to see a message flow through the village. +Podemos llamar a `enviarChismorreo` para ver un mensaje fluir a través del +pueblo. ``` -sendGossip(bigOak, "Kids with airgun in the park"); +enviarChismorreo(granRoble, "Niños con una pistola de aire en el parque"); ``` if}} -## Message routing +## Enrutamiento de mensajes {{index efficiency}} -If a given node wants to talk to a single other node, flooding is not -a very efficient approach. Especially when the network is big, that -would lead to a lot of useless data transfers. +Si un nodo determinado quiere hablar unicamente con otro nodo, la inundación no +es un enfoque muy eficiente. Especialmente cuando la red es grande, +daría lugar a una gran cantidad de transferencias de datos inútiles. {{index "routing"}} -An alternative approach is to set up a way for messages to hop from -node to node until they reach their destination. The difficulty with -that is it requires knowledge about the layout of the network. To -send a request in the direction of a faraway nest, it is necessary to -know which neighboring nest gets it closer to its destination. Sending -it in the wrong direction will not do much good. +Un enfoque alternativo es configurar una manera en que los mensajes salten de +nodo a nodo, hasta que lleguen a su destino. La dificultad con eso +es que requiere de conocimiento sobre el diseño de la red. Para +enviar una solicitud hacia la dirección de un nido lejano, es necesario +saber qué nido vecino lo acerca más a su destino. Enviar la solicitud +en la dirección equivocada no servirá de mucho. -Since each nest knows only about its direct neighbors, it doesn't have -the information it needs to compute a route. We must somehow spread -the information about these connections to all nests, preferably in a -way that allows it to change over time, when nests are abandoned or -new nests are built. +Dado que cada nido solo conoce a sus vecinos directos, no tiene +la información que necesita para calcular una ruta. De alguna manera debemos +extender la información acerca de estas conexiones a todos los nidos. +Preferiblemente en una manera que permita ser cambiada con el tiempo, cuando +los nidos son abandonados o nuevos nidos son construidos. {{index flooding}} -We can use flooding again, but instead of checking whether a given -message has already been received, we now check whether the new set of -neighbors for a given nest matches the current set we have for it. +Podemos usar la inundación de nuevo, pero en lugar de verificar si un +determinado mensaje ya ha sido recibido, ahora verificamos si el nuevo conjunto +de vecinos de un nido determinado coinciden con el conjunto actual que +tenemos para él. {{index "broadcastConnections function", "connections binding"}} ```{includeCode: true} -requestType("connections", (nest, {name, neighbors}, - source) => { - let connections = nest.state.connections; - if (JSON.stringify(connections.get(name)) == - JSON.stringify(neighbors)) return; - connections.set(name, neighbors); - broadcastConnections(nest, name, source); +tipoSolicitud("conexiones", (nido, {nombre, vecinos}, + fuente) => { + let conexiones = nido.estado.conexiones; + if (JSON.stringify(conexiones.get(nombre)) == + JSON.stringify(vecinos)) return; + conexiones.set(nombre, vecinos); + difundirConexiones(nido, nombre, fuente); }); -function broadcastConnections(nest, name, exceptFor = null) { - for (let neighbor of nest.neighbors) { - if (neighbor == exceptFor) continue; - request(nest, neighbor, "connections", { - name, - neighbors: nest.state.connections.get(name) +function difundirConexiones(nido, nombre, exceptoPor = null) { + for (let vecino of nido.vecinos) { + if (vecino == exceptoPor) continue; + solicitud(nido, vecino, "conexiones", { + nombre, + vecinos: nido.estado.conexiones.get(nombre) }); } } -everywhere(nest => { - nest.state.connections = new Map; - nest.state.connections.set(nest.name, nest.neighbors); - broadcastConnections(nest, nest.name); +todosLados(nido => { + nido.estado.conexiones = new Map; + nido.estado.conexiones.set(nido.nombre, nido.vecinos); + difundirConexiones(nido, nido.nombre); }); ``` {{index JSON, "== operator"}} -The comparison uses `JSON.stringify` because `==`, on objects or -arrays, will return true only when the two are the exact same value, -which is not what we need here. Comparing the JSON strings is a crude -but effective way to compare their content. +La comparación usa `JSON.stringify` porque `==`, en objetos o +arrays, solo retornara true cuando los dos tengan exactamente el mismo valor, +lo cual no es lo que necesitamos aquí. Comparar los strings JSON es una +cruda pero efectiva manera de comparar su contenido. -The nodes immediately start broadcasting their connections, which -should, unless some nests are completely unreachable, quickly give -every nest a map of the current network ((graph)). +Los nodos comienzan inmediatamente a transmitir sus conexiones, lo que +debería, a menos que algunos nidos sean completamente inalcanzables, dar +rápidamente cada nido un mapa del ((grafo)) de la red actual. {{index pathfinding}} -A thing you can do with graphs is find routes in them, as we saw in -[Chapter ?](robot). If we have a route toward a message's -destination, we know which direction to send it in. +Una cosa que puedes hacer con grafos es encontrar rutas en ellos, como vimos +en el [Capítulo 7](Robot). Si tenemos una ruta hacia el destino de un mensaje, +sabemos en qué dirección enviarlo. {{index "findRoute function"}} -This `findRoute` function, which greatly resembles the `findRoute` -from [Chapter ?](robot#findRoute), searches for a way to reach a given -node in the network. But instead of returning the whole route, it just -returns the next step. That next nest will itself, using its current -information about the network, decide where _it_ sends the message. +Esta función `encontrarRuta`, que se parece mucho a `encontrarRuta` +del [Capítulo 7](robot#findRoute), busca por una forma de llegar a un determinado +nodo en la red. Pero en lugar de devolver toda la ruta, simplemente +retorna el siguiente paso. Ese próximo nido en si mismo, usando su información +actual sobre la red, decididira _hacia_ dónde enviar el mensaje. ```{includeCode: true} -function findRoute(from, to, connections) { - let work = [{at: from, via: null}]; - for (let i = 0; i < work.length; i++) { - let {at, via} = work[i]; - for (let next of connections.get(at) || []) { - if (next == to) return via; - if (!work.some(w => w.at == next)) { - work.push({at: next, via: via || next}); +function encontrarRuta(desde, hasta, conexiones) { + let trabajo = [{donde: desde, via: null}]; + for (let i = 0; i < trabajo.length; i++) { + let {donde, via} = trabajo[i]; + for (let siguiente of conexiones.get(donde) || []) { + if (siguiente == hasta) return via; + if (!trabajo.some(w => w.donde == siguiente)) { + trabajo.push({donde: siguiente, via: via || siguiente}); } } } @@ -795,212 +795,209 @@ function findRoute(from, to, connections) { } ``` -Now we can build a function that can send long-distance messages. If -the message is addressed to a direct neighbor, it is delivered as -usual. If not, it is packaged in an object and sent to a neighbor that -is closer to the target, using the `"route"` request type, which will -cause that neighbor to repeat the same behavior. +Ahora podemos construir una función que pueda enviar mensajes de larga distancia. +Si el mensaje está dirigido a un vecino directo, se entrega normalmente. +Si no, se empaqueta en un objeto y se envía a un vecino que +este más cerca del objetivo, usando el tipo de solicitud `"ruta"`, que +hace que ese vecino repita el mismo comportamiento. {{index "routeRequest function"}} ```{includeCode: true} -function routeRequest(nest, target, type, content) { - if (nest.neighbors.includes(target)) { - return request(nest, target, type, content); +function solicitudRuta(nido, objetivo, tipo, contenido) { + if (nido.vecinos.includes(objetivo)) { + return solicitud(nido, objetivo, tipo, contenido); } else { - let via = findRoute(nest.name, target, - nest.state.connections); - if (!via) throw new Error(`No route to ${target}`); - return request(nest, via, "route", - {target, type, content}); + let via = encontrarRuta(nido.nombre, objetivo, + nido.estado.conexiones); + if (!via) throw new Error(`No hay rutas disponibles hacia ${objetivo}`); + return solicitud(nido, via, "ruta", + {objetivo, tipo, contenido}); } } -requestType("route", (nest, {target, type, content}) => { - return routeRequest(nest, target, type, content); +tipoSolicitud("ruta", (nido, {objetivo, tipo, contenido}) => { + return solicitudRuta(nido, objetivo, tipo, contenido); }); ``` {{if interactive -We can now send a message to the nest in the church tower, which is -four network hops removed. +Ahora podemos enviar un mensaje al nido en la torre de la iglesia, que esta +a cuatro saltos de red de distancia. ``` -routeRequest(bigOak, "Church Tower", "note", - "Incoming jackdaws!"); +solicitudRuta(granRoble, "Torre de la Iglesia", "nota", + "Cuidado con las Palomas!"); ``` if}} -{{index [network, abstraction], layering}} +{{index "[network, stack]"}} -We've constructed several layers of functionality on top of a -primitive communication system to make it convenient to use. -This is a nice (though simplified) model of how real computer networks -work. +Hemos construido varias ((capas)) de funcionalidad sobre un +sistema de comunicación primitivo para que sea conveniente de usarlo. +Este es un buen (aunque simplificado) modelo de cómo las redes de computadoras +reales trabajan. {{index error}} -A distinguishing property of computer networks is that they aren't -reliable—abstractions built on top of them can help, but you can't -abstract away network failure. So network programming is typically -very much about anticipating and dealing with failures. +Una propiedad distintiva de las redes de computadoras es que no son confiables—las +abstracciones construidas encima de ellas pueden ayudar, pero no se puede +abstraer la falla de una falla de red. Entonces la programación de redes es +típicamente mucho acerca de anticipar y lidiar con fallas. -## Async functions +## Funciones asíncronas -To store important information, ((crow))s are known to duplicate it -across nests. That way, when a hawk destroys a nest, the information -isn't lost. +Para almacenar información importante, se sabe que los ((cuervo))s la duplican +a través de los nidos. De esta forma, cuando un halcón destruye un nido, la +información no se pierde. -To retrieve a given piece of information that it doesn't have in its -own storage bulb, a nest computer might consult random other nests in -the network until it finds one that has it. +Para obtener una pieza de información dada que no este en su propia bulbo de +almacenamiento, una computadora nido puede consultar otros nidos al azar en +la red hasta que encuentre uno que la tenga. {{index "findInStorage function", "network function"}} ```{includeCode: true} -requestType("storage", (nest, name) => storage(nest, name)); +tipoSolicitud("almacenamiento", (nido, nombre) => almacenamiento(nido, nombre)); -function findInStorage(nest, name) { - return storage(nest, name).then(found => { - if (found != null) return found; - else return findInRemoteStorage(nest, name); +function encontrarEnAlmacenamiento(nido, nombre) { + return almacenamiento(nido, nombre).then(encontrado => { + if (encontrado != null) return encontrado; + else return encontrarEnAlmacenamientoRemoto(nido, nombre); }); } -function network(nest) { - return Array.from(nest.state.connections.keys()); +function red(nido) { + return Array.from(nido.estado.conexiones.keys()); } -function findInRemoteStorage(nest, name) { - let sources = network(nest).filter(n => n != nest.name); - function next() { - if (sources.length == 0) { - return Promise.reject(new Error("Not found")); +function encontrarEnAlmacenamientoRemoto(nido, nombre) { + let fuentes = red(nido).filter(n => n != nido.nombre); + function siguiente() { + if (fuentes.length == 0) { + return Promise.reject(new Error("No encontrado")); } else { - let source = sources[Math.floor(Math.random() * - sources.length)]; - sources = sources.filter(n => n != source); - return routeRequest(nest, source, "storage", name) - .then(value => value != null ? value : next(), - next); + let fuente = fuentes[Math.floor(Math.random() * + fuentes.length)]; + fuentes = fuentes.filter(n => n != fuente); + return solicitudRuta(nido, fuente, "almacenamiento", nombre) + .then(valor => valor != null ? valor : siguiente(), + siguiente); } } - return next(); + return siguiente(); } ``` {{index "Map class", "Object.keys function", "Array.from function"}} -Because `connections` is a `Map`, `Object.keys` doesn't work on it. It -has a `keys` _method_, but that returns an iterator rather than an -array. An iterator (or iterable value) can be converted to an array -with the `Array.from` function. +Como `conexiones` es un `Map`, `Object.keys` no funciona en él. Este +tiene un _metódo_ `keys`, pero que retorna un iterador en lugar de un +array. Un iterador (o valor iterable) se puede convertir a un array +con la función `Array.from`. {{index "Promise class", recursion}} -Even with promises this is some rather awkward code. Multiple -asynchronous actions are chained together in non-obvious ways. We -again need a recursive function (`next`) to model looping through the -nests. +Incluso con promesas, este es un código bastante incómodo. Múltiples +acciones asincrónicas están encadenadas juntas de maneras no-obvias. Nosotros +de nuevo necesitamos una función recursiva (`siguiente`) para modelar ciclos +a través de nidos. {{index "synchronous programming", "asynchronous programming"}} -And the thing the code actually does is completely linear—it always -waits for the previous action to complete before starting the next -one. In a synchronous programming model, it'd be simpler to express. +Y lo que el código realmente hace es completamente lineal—siempre +espera a que se complete la acción anterior antes de comenzar la siguiente. +En un modelo de programación sincrónica, sería más simple de expresar. {{index "async function", "await keyword"}} -The good news is that JavaScript allows you to write pseudo-synchronous -code to describe asynchronous computation. An `async` function is a -function that implicitly returns a -promise and that can, in its body, `await` other promises in a way -that _looks_ synchronous. +La buena noticia es que JavaScript te permite escribir código pseudo-sincrónico. +Una función `async` es una función que retorna implícitamente una +promesa y que puede, en su cuerpo, `await` ("esperar") otras promesas de una +manera que _se ve_ sincrónica. {{index "findInStorage function"}} -We can rewrite `findInStorage` like this: +Podemos reescribir `encontrarEnAlmacenamiento` de esta manera: ``` -async function findInStorage(nest, name) { - let local = await storage(nest, name); +async function encontrarEnAlmacenamiento(nido, nombre) { + let local = await almacenamiento(nido, nombre); if (local != null) return local; - let sources = network(nest).filter(n => n != nest.name); - while (sources.length > 0) { - let source = sources[Math.floor(Math.random() * - sources.length)]; - sources = sources.filter(n => n != source); + let fuentes = red(nido).filter(n => n != nido.nombre); + while (fuentes.length > 0) { + let fuente = fuentes[Math.floor(Math.random() * + fuentes.length)]; + fuentes = fuentes.filter(n => n != fuente); try { - let found = await routeRequest(nest, source, "storage", - name); - if (found != null) return found; + let encontrado = await solicitudRuta(nido, fuente, "almacenamiento", + nombre); + if (encontrado != null) return encontrado; } catch (_) {} } - throw new Error("Not found"); + throw new Error("No encontrado"); } ``` {{index "async function", "return keyword", "exception handling"}} -An `async` function is marked by the word `async` before the -`function` keyword. Methods can also be made `async` by writing -`async` before their name. When such a function or method is called, -it returns a promise. As soon as the body returns something, that -promise is resolved. If it throws an exception, the promise is -rejected. +Una función `async` está marcada por la palabra `async` antes de la +palabra clave `function`. Los métodos también pueden hacerse `async` +al escribir `async` antes de su nombre. Cuando se llame a dicha función o método, +este retorna una promesa. Tan pronto como el cuerpo retorne algo, esa +promesa es resuelta Si arroja una excepción, la promesa es rechazada. {{if interactive ```{startCode: true} -findInStorage(bigOak, "events on 2017-12-21") +encontrarEnAlmacenamiento(granRoble, "eventos del 2017-12-21") .then(console.log); ``` if}} -{{index "await keyword", ["control flow", asynchronous]}} +{{index "await keyword", "control flow"}} -Inside an `async` function, the word `await` can be put in front of an -expression to wait for a promise to resolve and only then continue -the execution of the function. +Dentro de una función `async`, la palabra `await` se puede poner delante de una +expresión para esperar a que se resuelva una promesa, y solo entonces continua +la ejecución de la función. -Such a function no longer, like a regular JavaScript function, runs -from start to completion in one go. Instead, it can be _frozen_ at any -point that has an `await`, and can be resumed at a later time. +Tal función ya no se ejecuta, como una función regular de JavaScript +de principio a fin de una sola vez. En su lugar, puede ser _congelada_ en +cualquier punto que tenga un `await`, y se reanuda en un momento posterior. -For non-trivial asynchronous code, this notation is usually more -convenient than directly using promises. Even if you need to do -something that doesn't fit the synchronous model, such as perform -multiple actions at the same time, it is easy to combine `await` with -the direct use of promises. +Para código asincrónico no-trivial, esta notación suele ser más +conveniente que usar promesas directamente. Incluso si necesitas hacer +algo que no se ajuste al modelo síncrono, como realizar +múltiples acciones al mismo tiempo, es fácil combinar `await` con el +uso directo de promesas. -## Generators +## Generadores {{index "async function"}} -This ability of functions to be paused and then resumed again is not -exclusive to `async` functions. JavaScript also has a feature called -_((generator))_ functions. These are similar, but without the -promises. +Esta capacidad de las funciones para pausar y luego reanudarse nuevamente no es +exclusiva para las funciones `async`. JavaScript también tiene una caracteristica +llamada funciones _((generador))_. Estss son similares, pero sin las +promesas. -When you define a function with `function*` (placing an asterisk after -the word `function`), it becomes a generator. When you call a -generator, it returns an ((iterator)), which we already saw in -[Chapter ?](object). +Cuando defines una función con `function*` (colocando un asterisco después de +la palabra `function`), se convierte en un generador. Cuando llamas un +generador, este retorna un ((iterador)), que ya vimos en el [Capítulo 6](objeto). ``` -function* powers(n) { - for (let current = n;; current *= n) { - yield current; +function* potenciacion(n) { + for (let actual = n;; actual *= n) { + yield actual; } } -for (let power of powers(3)) { - if (power > 50) break; - console.log(power); +for (let potencia of potenciacion(3)) { + if (potencia > 50) break; + console.log(potencia); } // → 3 // → 9 @@ -1009,78 +1006,75 @@ for (let power of powers(3)) { {{index "next method", "yield keyword"}} -Initially, when you call `powers`, the function is frozen at its -start. Every time you call `next` on the iterator, the function runs -until it hits a `yield` expression, which pauses it and causes the -yielded value to become the next value produced by the iterator. When -the function returns (the one in the example never does), the iterator -is done. +Inicialmente, cuando llamas a `potenciacion`, la función se congela en su +comienzo. Cada vez que llames `next` en el iterador, la función se ejecuta +hasta que encuentre una expresión `yield` ("arrojar"), que la pausa y causa que el +valor arrojado se convierta en el siguiente valor producido por el iterador. +Cuando la función retorne (la del ejemplo nunca lo hace), el iterador +está completo. -Writing iterators is often much easier when you use generator -functions. The iterator for the `Group` class (from the exercise in -[Chapter ?](object#group_iterator)) can be written with this -generator: +Escribir iteradores es a menudo mucho más fácil cuando usas funciones +generadoras. El iterador para la clase grupal (del ejercicio en el +[Capítulo 6](Objeto#group_iterator)) se puede escribir con este +generador: {{index "Group class"}} ``` -Group.prototype[Symbol.iterator] = function*() { - for (let i = 0; i < this.members.length; i++) { - yield this.members[i]; +Conjunto.prototype[Symbol.iterator] = function*() { + for (let i = 0; i < this.miembros.length; i++) { + yield this.miembros[i]; } }; ``` ```{hidden: true, includeCode: true} -class Group { - constructor() { this.members = []; } - add(m) { this.members.add(m); } +class Conjunto { + constructor() { this.miembros = []; } + añadir(m) { this.miembros.añadir(m); } } ``` -{{index [state, in iterator]}} - -There's no longer a need to create an object to hold the iteration -state—generators automatically save their local state every time -they yield. +Ya no es necesario crear un objeto para mantener el estado de la iteración—los +generadores guardan automáticamente su estado local cada vez ellos arrojen. -Such `yield` expressions may occur only directly in the generator -function itself and not in an inner function you define inside of it. -The state a generator saves, when yielding, is only its _local_ -environment and the position where it yielded. +Dichas expresiones `yield` solo pueden ocurrir directamente en la +función generadora en sí y no en una función interna que definas dentro de ella. +El estado que ahorra un generador, cuando arroja, es solo su entorno _local_ +y la posición en la que fue arrojada. {{index "await keyword"}} -An `async` function is a special type of generator. It produces a -promise when called, which is resolved when it returns (finishes) and -rejected when it throws an exception. Whenever it yields (awaits) a -promise, the result of that promise (value or thrown exception) is the -result of the `await` expression. +Una función `async` es un tipo especial de generador. Produce una +promesa cuando se llama, que se resuelve cuando vuelve (termina) y +rechaza cuando arroja una excepción. Cuando cede (espera) por una +promesa, el resultado de esa promesa (valor o excepción lanzada) es el +resultado de la expresión `await`. -## The event loop +## El ciclo de evento {{index "asynchronous programming", scheduling, "event loop", timeline}} -Asynchronous programs are executed piece by piece. Each piece may -start some actions and schedule code to be executed when the action -finishes or fails. In between these pieces, the program sits idle, -waiting for the next action. +Los programas asincrónicos son ejecutados pieza por pieza. Cada pieza puede +iniciar algunas acciones y programar código para que se ejecute cuando la +acción termine o falle. Entre estas piezas, el programa permanece inactivo, +esperando por la siguiente acción. {{index "setTimeout function"}} -So callbacks are not directly called by the code that scheduled them. -If I call `setTimeout` from within a function, that function will have -returned by the time the callback function is called. And when the -callback returns, control does not go back to the function that -scheduled it. +Por lo tanto, las devoluciones de llamada no son llamadas directamente por el +código que las programó. Si llamo a `setTimeout` desde adentro de una función, +esa función habra retornado para el momento en que se llame a la función de +devolución de llamada. Y cuando la devolución de llamada retorne, el +control no volvera a la función que la programo. {{index "Promise class", "catch keyword", "exception handling"}} -Asynchronous behavior happens on its own empty function ((call -stack)). This is one of the reasons that, without promises, managing -exceptions across asynchronous code is hard. Since each callback -starts with a mostly empty stack, your `catch` handlers won't be on -the stack when they throw an exception. +El comportamiento asincrónico ocurre en su propia función de ((llamada de +pila)) vacía. Esta es una de las razones por las cuales, sin promesas, la +gestión de excepciones en el código asincrónico es dificil. Como cada +devolución de llamada comienza con una pila en su mayoría vacía, tus +manejadores `catch` no estarán en la pila cuando lanzen una excepción. ``` try { @@ -1088,212 +1082,212 @@ try { throw new Error("Woosh"); }, 20); } catch (_) { - // This will not run - console.log("Caught!"); + // Esto no se va a ejecutar + console.log("Atrapado!"); } ``` {{index thread, queue}} -No matter how closely together events—such as timeouts or incoming -requests—happen, a JavaScript environment will run only one program at -a time. You can think of this as it running a big loop _around_ your -program, called the _event loop_. When there's nothing to be done, -that loop is stopped. But as events come in, they are added to a queue, -and their code is executed one after the other. Because no two things -run at the same time, slow-running code might delay the handling of -other events. +No importa que tan cerca los eventos—como tiempos de espera o solicitudes +entrantes—sucedan, un entorno de JavaScript solo ejecutará un programa a +la vez. Puedes pensar en esto como un gran ciclo _alrededor_ de tu +programa, llamado _ciclo de evento_. Cuando no hay nada que hacer, +ese bucle está detenido. Pero a medida que los eventos entran, se agregan a una +cola, y su código se ejecuta uno después del otro. Porque no hay dos cosas +que se ejecuten al mismo tiempo, código de ejecución lenta puede retrasar +el manejo de otros eventos. -This example sets a timeout but then dallies until after the -timeout's intended point of time, causing the timeout to be late. +Este ejemplo establece un tiempo de espera, pero luego se retrasa hasta después +del tiempo de espera previsto, lo que hace que el tiempo de espera este tarde. ``` -let start = Date.now(); +let comienzo = Date.now(); setTimeout(() => { - console.log("Timeout ran at", Date.now() - start); + console.log("Tiempo de espera corrio al ", Date.now() - comienzo); }, 20); -while (Date.now() < start + 50) {} -console.log("Wasted time until", Date.now() - start); -// → Wasted time until 50 -// → Timeout ran at 55 +while (Date.now() < comienzo + 50) {} +console.log("Se desperdicio tiempo hasta el ", Date.now() - comienzo); +// → Se desperdicio tiempo hasta el 50 +// → Tiempo de espera corrio al 55 ``` {{index "resolving (a promise)", "rejecting (a promise)", "Promise class"}} -Promises always resolve or reject as a new event. Even if a promise is -already resolved, waiting for it will cause your callback to run after -the current script finishes, rather than right away. +Las promesas siempre se resuelven o rechazan como un nuevo evento. Incluso si +una promesa ya ha sido resuelta, esperar por ella hará que la devolución de +llamada se ejecute después de que el script actual termine, en lugar de hacerlo +inmediatamente. ``` -Promise.resolve("Done").then(console.log); -console.log("Me first!"); -// → Me first! -// → Done +Promise.resolve("Listo").then(console.log); +console.log("Yo primero!"); +// → Yo primero! +// → Listo ``` -In later chapters we'll see various other types of events that run on -the event loop. +En capítulos posteriores, veremos otros tipos de eventos que se ejecutan en +el ciclo de eventos. -## Asynchronous bugs +## Errores asincrónicos -{{index "asynchronous programming", [state, transitions]}} +{{index "asynchronous programming"}} -When your program runs synchronously, in a single go, there are no -state changes happening except those that the program itself -makes. For asynchronous programs this is different—they may have -_gaps_ in their execution during which other code can run. +Cuando tu programa se ejecuta de forma síncrona, de una sola vez, no hay +cambios de ((estado)) sucediendo aparte de aquellos que el mismo programa +realiza. Para los programas asíncronos, esto es diferente—estos pueden tener +_brechas_ en su ejecución durante las cuales se podria ejecutar otro código. -Let's look at an example. One of the hobbies of our crows is to count -the number of chicks that hatch throughout the village every year. -Nests store this count in their storage bulbs. The following code tries to -enumerate the counts from all the nests for a given year: +Veamos un ejemplo. Uno de los pasatiempos de nuestros cuervos es contar +la cantidad de polluelos que nacen en el pueblo cada año. +Los nidos guardan este recuento en sus bulbos de almacenamiento. El siguiente +código intenta enumerar los recuentos de todos los nidos para un año determinado. {{index "anyStorage function", "chicks function"}} ```{includeCode: true} -function anyStorage(nest, source, name) { - if (source == nest.name) return storage(nest, name); - else return routeRequest(nest, source, "storage", name); +function cualquierAlmacenamiento(nido, fuente, nombre) { + if (fuente == nido.nombre) return almacenamiento(nido, nombre); + else return solicitudRuta(nido, fuente, "almacenamiento", nombre); } -async function chicks(nest, year) { - let list = ""; - await Promise.all(network(nest).map(async name => { - list += `${name}: ${ - await anyStorage(nest, name, `chicks in ${year}`) +async function polluelos(nido, años) { + let lista = ""; + await Promise.all(red(nido).map(async nombre => { + lista += `${nombre}: ${ + await cualquierAlmacenamiento(nido, nombre, `polluelos en ${años}`) }\n`; })); - return list; + return lista; } ``` {{index "async function"}} -The `async name =>` part shows that ((arrow function))s can also be -made `async` by putting the word `async` in front of them. +La parte `async nombre =>` muestra que las ((funciones de flecha)) también pueden +ser `async` al poner la palabra `async` delante de ellas. {{index "Promise.all function"}} -The code doesn't immediately look suspicious...it maps the `async` -arrow function over the set of nests, creating an array of promises, -and then uses `Promise.all` to wait for all of these before returning -the list they build up. +El código no parece sospechoso de inmediato... mapea la función de flecha +`async` sobre el conjunto de nidos, creando una serie de promesas, +y luego usa `Promise.all` para esperar a todos estas antes de retornar +la lista que estas construyen. -But it is seriously broken. It'll always return only a single line of -output, listing the nest that was slowest to respond. +Pero está seriamente roto. Siempre devolverá solo una línea de +salida, enumerando al nido que fue más lento en responder. {{if interactive ``` -chicks(bigOak, 2017).then(console.log); +polluelos(granRoble, 2017).then(console.log); ``` if}} -Can you work out why? +Puedes averiguar por qué? {{index "+= operator"}} -The problem lies in the `+=` operator, which takes the _current_ value -of `list` at the time where the statement starts executing and then, -when the `await` finishes, sets the `list` binding to be that value -plus the added string. +El problema radica en el operador `+=`, que toma el valor _actual_ +de `lista` en el momento en que la instrucción comienza a ejecutarse, y luego, +cuando el `await` termina, establece que la vinculaciòn `lista` sea ese valor +más el string agregado. {{index "await keyword"}} -But between the time where the statement starts executing and the time -where it finishes there's an asynchronous gap. The `map` expression -runs before anything has been added to the list, so each of the `+=` -operators starts from an empty string and ends up, when its storage -retrieval finishes, setting `list` to a single-line list—the result of -adding its line to the empty string. +Pero entre el momento en el que la declaración comienza a ejecutarse y el momento +donde termina hay una brecha asincrónica. La expresión `map` se ejecuta antes +de que se haya agregado algo a la lista, por lo que cada uno de los operadores +`+=` comienza desde un string vacío y termina cuando su recuperación de +almacenamiento finaliza, estableciendo `lista` como una lista de una sola +línea—el resultado de agregar su línea al string vacío. {{index "side effect"}} -This could have easily been avoided by returning the lines from the -mapped promises and calling `join` on the result of `Promise.all`, -instead of building up the list by changing a binding. As usual, -computing new values is less error-prone than changing existing -values. +Esto podría haberse evitado fácilmente retornando las líneas de las +promesas mapeadas y llamando a `join` en el resultado de `Promise.all`, +en lugar de construir la lista cambiando una vinculación. Como siempre, +calcular nuevos valores es menos propenso a errores que cambiar +valores existentes. {{index "chicks function"}} ``` -async function chicks(nest, year) { - let lines = network(nest).map(async name => { - return name + ": " + - await anyStorage(nest, name, `chicks in ${year}`); +async function polluelos(nido, año) { + let lineas = red(nido).map(async nombre => { + return nombre + ": " + + await cualquierAlmacenamiento(nido, nombre, `polluelos en ${año}`); }); - return (await Promise.all(lines)).join("\n"); + return (await Promise.all(lineas)).join("\n"); } ``` -Mistakes like this are easy to make, especially when using `await`, -and you should be aware of where the gaps in your code occur. An -advantage of JavaScript's _explicit_ asynchronicity (whether through -callbacks, promises, or `await`) is that spotting these gaps is -relatively easy. +Errores como este son fáciles de hacer, especialmente cuando se usa `await`, +y debes tener en cuenta dónde se producen las brechas en tu código. Una +ventaja de la asincronicidad _explicita_ de JavaScript (ya sea a través de +devoluciones de llamada, promesas, o `await`) es que detectar estas brechas es +relativamente fácil. -## Summary +## Resumen -Asynchronous programming makes it possible to express waiting for -long-running actions without freezing the program during these -actions. JavaScript environments typically implement this style of -programming using callbacks, functions that are called when the -actions complete. An event loop schedules such callbacks to be called -when appropriate, one after the other, so that their execution does -not overlap. +La programación asincrónica permite expresar la espera de +acciones de larga duración sin congelar el programa durante estas +acciones. Los entornos de JavaScript suelen implementar este estilo de +programación usando devoluciones de llamada, funciones que son llaman cuando las +acciones son completadas. Un ciclo de eventos planifica que dichas devoluciones +de llamadas sean llamadas cuando sea apropiado, una después de la otra, +para que sus ejecuciones no se superpongan. -Programming asynchronously is made easier by promises, objects that -represent actions that might complete in the future, and `async` -functions, which allow you to write an asynchronous program as if it -were synchronous. +La programación asíncrona se hace más fácil mediante promesas, objetos que +representar acciones que podrían completarse en el futuro, y funciones `async`, +que te permiten escribir un programa asíncrono como si fuera sincrónico. -## Exercises +## Ejercicios -### Tracking the scalpel +### Siguiendo el bisturí {{index "scalpel (exercise)"}} -The village crows own an old scalpel that they occasionally use on -special missions—say, to cut through screen doors or packaging. To be -able to quickly track it down, every time the scalpel is moved to -another nest, an entry is added to the storage of both the nest that -had it and the nest that took it, under the name `"scalpel"`, with its -new location as the value. +Los cuervos del pueblo poseen un viejo bisturí que ocasionalmente usan en +misiones especiales—por ejemplo, para cortar puertas de malla o embalar cosas. +Para ser capaces de rastrearlo rápidamente, cada vez que se mueve el bisturí +a otro nido, una entrada se agrega al almacenamiento tanto del nido que +lo tenía como al nido que lo tomó, bajo el nombre `"bisturí"`, con su +nueva ubicación como su valor. -This means that finding the scalpel is a matter of following the -breadcrumb trail of storage entries, until you find a nest where that -points at the nest itself. +Esto significa que encontrar el bisturí es una cuestión de seguir la +ruta de navegación de las entradas de almacenamiento, hasta que encuentres un +nido que apunte a el nido en si mismo. {{index "anyStorage function", "async function"}} -Write an `async` function `locateScalpel` that does this, starting at -the nest on which it runs. You can use the `anyStorage` function -defined earlier to access storage in arbitrary nests. The scalpel has -been going around long enough that you may assume that every nest has -a `"scalpel"` entry in its data storage. +Escribe una función `async`, `localizarBisturi` que haga esto, comenzando en +el nido en el que se ejecute. Puede usar la función `cualquierAlmacenamiento` +definida anteriormente para acceder al almacenamiento en nidos arbitrarios. +El bisturí ha estado dando vueltas el tiempo suficiente como para que puedas +suponer que cada nido tiene una entrada `bisturí` en su almacenamiento de datos. -Next, write the same function again without using `async` and `await`. +Luego, vuelve a escribir la misma función sin usar `async` y `await`. {{index "exception handling"}} -Do request failures properly show up as rejections of the returned -promise in both versions? How? +Las fallas de solicitud se muestran correctamente como rechazos de la +promesa devuelta en ambas versiones? Cómo? {{if interactive ```{test: no} -async function locateScalpel(nest) { - // Your code here. +async function localizarBisturi(nido) { + // Tu codigo aqui. } -function locateScalpel2(nest) { - // Your code here. +function localizarBisturi2(nido) { + // Tu codigo aqui. } -locateScalpel(bigOak).then(console.log); -// → Butcher Shop +localizarBisturi(granRoble).then(console.log); +// → Tienda del Carnicero ``` if}} @@ -1302,66 +1296,66 @@ if}} {{index "scalpel (exercise)"}} -This can be done with a single loop that searches through the nests, -moving forward to the next when it finds a value that doesn't match -the current nest's name and returning the name when it finds a -matching value. In the `async` function, a regular `for` or `while` -loop can be used. +Esto se puede realizar con un solo ciclo que busca a través de los nidos, +avanzando hacia el siguiente cuando encuentre un valor que no coincida +con el nombre del nido actual, y retornando el nombre cuando esta encuentra un +valor que coincida. En la función `async`, un ciclo regular `for` o `while` +puede ser utilizado. {{index recursion}} -To do the same in a plain function, you will have to build your loop -using a recursive function. The easiest way to do this is to have that -function return a promise by calling `then` on the promise that -retrieves the storage value. Depending on whether that value matches -the name of the current nest, the handler returns that value or a -further promise created by calling the loop function again. +Para hacer lo mismo con una función simple, tendrás que construir tu ciclo +usando una función recursiva. La manera más fácil de hacer esto es hacer +que esa función retorne una promesa al llamar a `then` en la promesa que +recupera el valor de almacenamiento. Dependiendo de si ese valor coincide +con el nombre del nido actual, el controlador devuelve ese valor o una +promesa adicional creada llamando a la función de ciclo nuevamente. -Don't forget to start the loop by calling the recursive function once -from the main function. +No olvides iniciar el ciclo llamando a la función recursiva una vez +desde la función principal. {{index "exception handling"}} -In the `async` function, rejected promises are converted to exceptions -by `await`. When an `async` function throws an exception, its promise -is rejected. So that works. +En la función `async`, las promesas rechazadas se convierten en excepciones +por `await` Cuando una función `async` arroja una excepción, su promesa +es rechazada. Entonces eso funciona. -If you implemented the non-`async` function as outlined earlier, the way -`then` works also automatically causes a failure to end up in the -returned promise. If a request fails, the handler passed to `then` -isn't called, and the promise it returns is rejected with the same -reason. +Si implementaste la función no-`async` como se describe anteriormente, la forma +en que `then` funciona también provoca automáticamente que una falla termine +en la promesa devuelta. Si una solicitud falla, el manejador pasado a `then` +no se llama, y ​​la promesa que devuelve se rechaza con la misma +razón. hint}} -### Building Promise.all +### Construyendo Promise.all {{index "Promise class", "Promise.all function", "building Promise.all (exercise)"}} -Given an array of ((promise))s, `Promise.all` returns a promise that -waits for all of the promises in the array to finish. It then -succeeds, yielding an array of result values. If a promise -in the array fails, the promise returned by `all` fails too, with the -failure reason from the failing promise. +Dado un array de ((promesa))s, `Promise.all` retorna una promesa que +espera a que finalicen todas las promesas del array. Entonces +tiene éxito, produciendo un array de valores de resultados. Si una promesa +en el array falla, la promesa retornada por `all` también falla, con la +razón de la falla proveniente de la promesa fallida. -Implement something like this yourself as a regular function -called `Promise_all`. +Implemente algo como esto tu mismo como una función regular +llamada `Promise_all`. -Remember that after a promise has succeeded or failed, it can't -succeed or fail again, and further calls to the functions that resolve -it are ignored. This can simplify the way you handle failure of your -promise. +Recuerda que una vez que una promesa ha tenido éxito o ha fallado, no puede +tener éxito o fallar de nuevo, y llamadas subsecuentes a las funciones que +resuelven son ignoradas. Esto puede simplificar la forma en que manejas la +falla de tu promesa. {{if interactive ```{test: no} -function Promise_all(promises) { +function Promise_all(promesa) { return new Promise((resolve, reject) => { - // Your code here. + // Tu codigo aqui. }); } -// Test code. +// Codigo de Prueba. Promise_all([]).then(array => { console.log("This should be []:", array); }); @@ -1390,25 +1384,25 @@ if}} {{index "Promise.all function", "Promise class", "then method", "building Promise.all (exercise)"}} -The function passed to the `Promise` constructor will have to call -`then` on each of the promises in the given array. When one of them -succeeds, two things need to happen. The resulting value needs to be -stored in the correct position of a result array, and we must check -whether this was the last pending ((promise)) and finish our own -promise if it was. +La función pasada al constructor `Promise` tendrá que llamar +`then` en cada una de las promesas del array dado. Cuando una de ellas +tenga éxito, dos cosas deben suceder. El valor resultante debe ser +almacenado en la posición correcta de un array de resultados, y debemos +verificar si esta fue la última ((promesa)) pendiente y terminar nuestra +promesa si asi fue. {{index "counter variable"}} -The latter can be done with a counter that is initialized to the -length of the input array and from which we subtract 1 every time a -promise succeeds. When it reaches 0, we are done. Make sure you take -into account the situation where the input array is empty (and thus no -promise will ever resolve). - -Handling failure requires some thought but turns out to be extremely -simple. Just pass the `reject` function of the wrapping promise to -each of the promises in the array as a `catch` handler or as a second -argument to `then` so that a failure in one of them triggers the -rejection of the whole wrapper promise. +Esto último se puede hacer con un contador que se inicializa con la +longitud del array de entrada y del que restamos 1 cada vez que una +promesa tenga éxito. Cuando llega a 0, hemos terminado. Asegúrate de tener +en cuenta la situación en la que el array de entrada este vacío (y +por lo tanto ninguna promesa nunca se resolverá). + +El manejo de la falla requiere pensar un poco, pero resulta ser extremadamente +sencillo. Solo pasa la función `reject` de la promesa de envoltura a +cada una de las promesas en el array como manejador `catch` o como segundo +argumento a `then` para que una falla en una de ellos desencadene el +rechazo de la promesa de envoltura completa. hint}} diff --git a/12_language.md b/12_language.md index 4e4413398..80d0ae3e6 100644 --- a/12_language.md +++ b/12_language.md @@ -1,307 +1,305 @@ {{meta {load_files: ["code/chapter/12_language.js"], zip: "node/html"}}} -# Project: A Programming Language +# Proyecto: Un Lenguaje de Programación {{quote {author: "Hal Abelson and Gerald Sussman", title: "Structure and Interpretation of Computer Programs", chapter: true} -The evaluator, which determines the meaning of expressions in a -programming language, is just another program. +El evaluador, que determina el significado de las expresiones en un +lenguaje de programación, es solo otro programa. quote}} {{index "Abelson, Hal", "Sussman, Gerald", SICP, "project chapter"}} -{{figure {url: "img/chapter_picture_12.jpg", alt: "Picture of an egg with smaller eggs inside", chapter: "framed"}}} +Construir tu propio ((lenguaje de programación)) es sorprendentemente fácil +(siempre y cuando no apuntes demasiado alto) y muy esclarecedor. -Building your own ((programming language)) is surprisingly easy (as -long as you do not aim too high) and very enlightening. +Lo principal que quiero mostrar en este capítulo es que no hay +((magia)) involucrada en la construcción de tu propio lenguaje. A menudo +he sentido que algunos inventos humanos eran tan inmensamente inteligentes +y complicados que nunca podría llegar a entenderlos. Pero con un poco de lectura +y experimentación, a menudo resultan ser bastante mundanos. -The main thing I want to show in this chapter is that there is no -((magic)) involved in building your own language. I've often felt that -some human inventions were so immensely clever and complicated that -I'd never be able to understand them. But with a little reading and -experimenting, they often turn out to be quite mundane. - -{{index "Egg language", [abstraction, "in Egg"]}} +{{index "Egg language"}} -We will build a programming language called Egg. It will be a tiny, -simple language—but one that is powerful enough to express any -computation you can think of. It will allow simple ((abstraction)) -based on ((function))s. +Construiremos un lenguaje de programación llamado Egg. Será un lenguaje pequeño +y simple—pero lo suficientemente poderoso como para expresar cualquier +computación que puedes pensar. Permitirá una ((abstracción)) simple +basada en ((funcion))es. {{id parsing}} -## Parsing +## Análisis -{{index parsing, validation, [syntax, "of Egg"]}} +{{index parsing, validation}} -The most immediately visible part of a programming language is its -_syntax_, or notation. A _parser_ is a program that reads a piece -of text and produces a data structure that reflects the structure of -the program contained in that text. If the text does not form a valid -program, the parser should point out the error. +La parte más visible de un lenguaje de programación es su +_((sintaxis))_, o notación. Un _analizador_ es un programa que lee una pieza +de texto y produce una estructura de datos que refleja la estructura del +programa contenido en ese texto. Si el texto no forma un programa válido, +el analizador debe señalar el error. {{index "special form", [function, application]}} -Our language will have a simple and uniform syntax. Everything in Egg -is an ((expression)). An expression can be the name of a binding, a -number, a string, or an _application_. Applications are used for -function calls but also for constructs such as `if` or `while`. +Nuestro lenguaje tendrá una sintaxis simple y uniforme. Todo en Egg +es una ((expresión)). Una expresión puede ser el nombre de una vinculación (binding), un +número, una cadena de texto o una _aplicación_. Las aplicaciones son usadas para +llamadas de función pero también para constructos como `if` o `while`. -{{index "double-quote character", parsing, [escaping, "in strings"], [whitespace, syntax]}} +{{index "double-quote character", parsing, [escaping, "in strings"]}} -To keep the parser simple, strings in Egg do not support anything like -backslash escapes. A string is simply a sequence of characters that -are not double quotes, wrapped in double quotes. A number is a -sequence of digits. Binding names can consist of any character that is -not whitespace and that does not have a special meaning in the syntax. +Para mantener el analizador simple, las cadenas en Egg no soportarán nada parecido +a escapes de barra invertida. Una cadena es simplemente una secuencia de +caracteres que no sean comillas dobles, envueltas en comillas dobles. Un número +es un secuencia de dígitos. Los nombres de vinculaciones pueden consistir en +cualquier carácter no que sea un ((espacio en blanco)) y eso no tiene un +significado especial en la sintaxis. -{{index "comma character", [parentheses, arguments]}} +{{index "comma character"}} -Applications are written the way they are in JavaScript, by putting -parentheses after an expression and having any number of -((argument))s between those parentheses, separated by commas. +Las aplicaciones se escriben tal y como están en JavaScript, poniendo +((paréntesis)) después de una expresión y teniendo cualquier cantidad de +((argumento))s entre esos paréntesis, separados por comas. ```{lang: null} -do(define(x, 10), - if(>(x, 5), - print("large"), - print("small"))) +hacer(definir(x, 10), + si(>(x, 5), + imprimir("grande"), + imprimir("pequeño"))) ``` -{{index block, [syntax, "of Egg"]}} +{{index block}} -The ((uniformity)) of the ((Egg language)) means that things that are -((operator))s in JavaScript (such as `>`) are normal bindings in this -language, applied just like other ((function))s. And since the -syntax has no concept of a block, we need a `do` construct to -represent doing multiple things in sequence. +La ((uniformidad)) del ((lenguaje Egg)) significa que las cosas que son +((operador))es en JavaScript (como `>`) son vinculaciones normales en este +lenguaje, aplicadas como cualquier ((función)). Y dado que la +((sintaxis)) no tiene un concepto de bloque, necesitamos una construcción +`hacer` para representar el hecho de realizar múltiples cosas en secuencia. -{{index "type property", parsing, ["data structure", tree]}} +{{index "type property", parsing}} -The data structure that the parser will use to describe a program -consists of ((expression)) objects, each of which has a `type` -property indicating the kind of expression it is and other properties -to describe its content. +La ((estructura de datos)) que el analizador usará para describir un programa +consta de objetos de ((expresión)), cada uno de los cuales tiene una +propiedad `tipo` que indica el tipo de expresión que este es y otras propiedades +que describen su contenido. {{index identifier}} -Expressions of type `"value"` represent literal strings or numbers. -Their `value` property contains the string or number value that they -represent. Expressions of type `"word"` are used for identifiers -(names). Such objects have a `name` property that holds the -identifier's name as a string. Finally, `"apply"` expressions -represent applications. They have an `operator` property that refers -to the expression that is being applied, as well as an `args` property that -holds an array of argument expressions. +Las expresiones de tipo `"valor"` representan strings o números literales. +Su propiedad `valor` contiene el string o valor numérico que estos +representan. Las expresiones de tipo `"palabra"` se usan para identificadores +(nombres). Dichos objetos tienen una propiedad `nombre` que contienen el +nombre del identificador como un string. Finalmente, las expresiones `"aplicar"` +representan aplicaciones. Tienen una propiedad `operador` que se refiere +a la expresión que está siendo aplicada, y una propiedad `argumentos` que +contiene un array de expresiones de argumentos. -The `>(x, 5)` part of the previous program would be represented like this: +La parte `>(x, 5)` del programa anterior se representaría de esta manera: ```{lang: "application/json"} { - type: "apply", - operator: {type: "word", name: ">"}, - args: [ - {type: "word", name: "x"}, - {type: "value", value: 5} + tipo: "aplicar", + operador: {tipo: "palabra", nombre: ">"}, + argumentos: [ + {tipo: "palabra", nombre: "x"}, + {tipo: "valor", valor: 5} ] } ``` -{{indexsee "abstract syntax tree", "syntax tree", ["data structure", tree]}} +{{indexsee "abstract syntax tree", "syntax tree"}} -Such a data structure is called a _((syntax tree))_. If you -imagine the objects as dots and the links between them as lines -between those dots, it has a ((tree))like shape. The fact that -expressions contain other expressions, which in turn might contain -more expressions, is similar to the way tree branches split and split -again. +Tal ((estructura de datos)) se llama _((árbol de sintaxis))_. Si tu +imaginas los objetos como puntos y los enlaces entre ellos como líneas +entre esos puntos, tiene una forma similar a un ((árbol)). El hecho de que +las expresiones contienen otras expresiones, que a su vez pueden contener +más expresiones, es similar a la forma en que las ramas de los árboles se +dividen y dividen una y otra vez. {{figure {url: "img/syntax_tree.svg", alt: "The structure of a syntax tree",width: "5cm"}}} {{index parsing}} -Contrast this to the parser we wrote for the configuration file format -in [Chapter ?](regexp#ini), which had a simple structure: it split the -input into lines and handled those lines one at a time. There were -only a few simple forms that a line was allowed to have. +Compara esto con el analizador que escribimos para el formato de archivo de +configuración en el [Capítulo 9](regexp#ini), que tenía una estructura simple: +dividía la entrada en líneas y manejaba esas líneas una a la vez. Habían +solo unas pocas formas simples que se le permitía tener a una línea. {{index recursion, [nesting, "of expressions"]}} -Here we must find a different approach. Expressions are not separated -into lines, and they have a recursive structure. Application -expressions _contain_ other expressions. +Aquí debemos encontrar un enfoque diferente. Las expresiones no están +separadas en líneas, y tienen una estructura recursiva. Las +expresiones de aplicaciones _contienen_ otras expresiones. {{index elegance}} -Fortunately, this problem can be solved very well by writing a parser -function that is recursive in a way that reflects the recursive nature -of the language. +Afortunadamente, este problema se puede resolver muy bien escribiendo una +función analizadora que es recursiva en una manera que refleje +la naturaleza recursiva del lenguaje. {{index "parseExpression function", "syntax tree"}} -We define a function `parseExpression`, which takes a string as input -and returns an object containing the data structure for the expression -at the start of the string, along with the part of the string left -after parsing this expression. When parsing subexpressions (the -argument to an application, for example), this function can be called -again, yielding the argument expression as well as the text that -remains. This text may in turn contain more arguments or may be the -closing parenthesis that ends the list of arguments. +Definimos una función `analizarExpresion`, que toma un string como entrada +y retorna un objeto que contiene la estructura de datos para la expresión +al comienzo del string, junto con la parte del string que queda +después de analizar esta expresión Al analizar subexpresiones (el +argumento para una aplicación, por ejemplo), esta función puede ser llamada +de nuevo, produciendo la expresión del argumento, así como al texto que +permanece. A su vez, este texto puede contener más argumentos o puede ser el +paréntesis de cierre que finaliza la lista de argumentos. -This is the first part of the parser: +Esta es la primera parte del analizador: ```{includeCode: true} -function parseExpression(program) { - program = skipSpace(program); - let match, expr; - if (match = /^"([^"]*)"/.exec(program)) { - expr = {type: "value", value: match[1]}; - } else if (match = /^\d+\b/.exec(program)) { - expr = {type: "value", value: Number(match[0])}; - } else if (match = /^[^\s(),#"]+/.exec(program)) { - expr = {type: "word", name: match[0]}; +function analizarExpresion(programa) { + programa = saltarEspacio(programa); + let emparejamiento, expresion; + if (emparejamiento = /^"([^"]*)"/.exec(programa)) { + expresion = {tipo: "valor", valor: emparejamiento[1]}; + } else if (emparejamiento = /^\d+\b/.exec(programa)) { + expresion = {tipo: "valor", valor: Number(emparejamiento[0])}; + } else if (emparejamiento = /^[^\s(),"]+/.exec(programa)) { + expresion = {tipo: "palabra", nombre: emparejamiento[0]}; } else { - throw new SyntaxError("Unexpected syntax: " + program); + throw new SyntaxError("Sintaxis inesperada: " + programa); } - return parseApply(expr, program.slice(match[0].length)); + return aplicarAnalisis(expresion, programa.slice(emparejamiento[0].length)); } -function skipSpace(string) { - let first = string.search(/\S/); - if (first == -1) return ""; - return string.slice(first); +function saltarEspacio(string) { + let primero = string.search(/\S/); + if (primero == -1) return ""; + return string.slice(primero); } ``` -{{index "skipSpace function", [whitespace, syntax]}} +{{index "skipSpace function"}} -Because Egg, like JavaScript, allows any amount of whitespace -between its elements, we have to repeatedly cut the whitespace off the -start of the program string. That is what the `skipSpace` function -helps with. +Dado que Egg, al igual que JavaScript, permite cualquier cantidad de ((espacios +en blanco)) entre sus elementos, tenemos que remover repetidamente los espacios +en blanco del inicio del string del programa. Para esto nos ayuda la función +`saltarEspacio`. {{index "literal expression", "SyntaxError type"}} -After skipping any leading space, `parseExpression` uses three -((regular expression))s to spot the three atomic elements that Egg -supports: strings, numbers, and words. The parser constructs a -different kind of data structure depending on which one matches. If -the input does not match one of these three forms, it is not a valid -expression, and the parser throws an error. We use `SyntaxError` -instead of `Error` as the exception constructor, which is another standard -error type, because it is a little more specific—it is also the error -type thrown when an attempt is made to run an invalid JavaScript -program. +Después de saltar cualquier espacio en blanco, `analizarExpresion` usa tres +((expresiones regular))es para detectar los tres elementos atómicos que Egg +soporta: strings, números y palabras. El analizador construye un +tipo diferente de estructura de datos dependiendo de cuál coincida. Si +la entrada no coincide con ninguna de estas tres formas, la expresión no +es válida, y el analizador arroja un error. Usamos `SyntaxError` +en lugar de `Error` como constructor de excepción, el cual es otro +tipo de error estándar, dado que es un poco más específico—también es el +tipo de error lanzado cuando se intenta ejecutar un programa de +JavaScript no válido. {{index "parseApply function"}} -We then cut off the part that was matched from the program string and -pass that, along with the object for the expression, to `parseApply`, -which checks whether the expression is an application. If so, it -parses a parenthesized list of arguments. +Luego cortamos la parte que coincidió del string del programa y +pasamos eso, junto con el objeto para la expresión, a `aplicarAnalisis`, +el cual verifica si la expresión es una aplicación. Si es así, +analiza una lista de los argumentos entre paréntesis. ```{includeCode: true} -function parseApply(expr, program) { - program = skipSpace(program); - if (program[0] != "(") { - return {expr: expr, rest: program}; +function aplicarAnalisis(expresion, programa) { + programa = saltarEspacio(programa); + if (programa[0] != "(") { + return {expresion: expresion, resto: programa}; } - program = skipSpace(program.slice(1)); - expr = {type: "apply", operator: expr, args: []}; - while (program[0] != ")") { - let arg = parseExpression(program); - expr.args.push(arg.expr); - program = skipSpace(arg.rest); - if (program[0] == ",") { - program = skipSpace(program.slice(1)); - } else if (program[0] != ")") { - throw new SyntaxError("Expected ',' or ')'"); + programa = saltarEspacio(programa.slice(1)); + expresion = {tipo: "aplicar", operador: expresion, argumentos: []}; + while (programa[0] != ")") { + let argumento = analizarExpresion(programa); + expresion.argumentos.push(argumento.expresion); + programa = saltarEspacio(argumento.resto); + if (programa[0] == ",") { + programa = saltarEspacio(programa.slice(1)); + } else if (programa[0] != ")") { + throw new SyntaxError("Experaba ',' o ')'"); } } - return parseApply(expr, program.slice(1)); + return aplicarAnalisis(expresion, programa.slice(1)); } ``` {{index parsing}} -If the next character in the program is not an opening parenthesis, -this is not an application, and `parseApply` returns the expression it -was given. +Si el siguiente carácter en el programa no es un paréntesis de apertura, +esto no es una aplicación, y `aplicarAnalisis` retorna la expresión que +se le dio. {{index recursion}} -Otherwise, it skips the opening parenthesis and creates the ((syntax -tree)) object for this application expression. It then recursively -calls `parseExpression` to parse each argument until a closing -parenthesis is found. The recursion is indirect, through `parseApply` -and `parseExpression` calling each other. +De lo contrario, salta el paréntesis de apertura y crea el objeto de +((árbol)) de sintaxis para esta expresión de aplicación. Entonces, recursivamente +llama a `analizarExpresion` para analizar cada argumento hasta que se encuentre +el paréntesis de cierre. La recursión es indirecta, a través de `aplicarAnalisis` +y `analizarExpresion` llamando una a la otra. -Because an application expression can itself be applied (such as in -`multiplier(2)(1)`), `parseApply` must, after it has parsed an -application, call itself again to check whether another pair of -parentheses follows. +Dado que una expresión de aplicación puede ser aplicada a sí misma (como en +`multiplicador(2)(1)`), `aplicarAnalisis` debe, después de haber analizado una +aplicación, llamarse asi misma de nuevo para verificar si otro par de +paréntesis sigue a continuación. {{index "syntax tree", "Egg language", "parse function"}} -This is all we need to parse Egg. We wrap it in a convenient `parse` -function that verifies that it has reached the end of the input string -after parsing the expression (an Egg program is a single expression), -and that gives us the program's data structure. +Esto es todo lo que necesitamos para analizar Egg. Envolvemos esto en una +conveniente función `analizar` que verifica que ha llegado al final del string +de entrada después de analizar la expresión (un programa Egg es una sola +expresión), y eso nos da la estructura de datos del programa. ```{includeCode: strip_log, test: join} -function parse(program) { - let {expr, rest} = parseExpression(program); - if (skipSpace(rest).length > 0) { - throw new SyntaxError("Unexpected text after program"); +function analizar(programa) { + let {expresion, resto} = analizarExpresion(programa); + if (saltarEspacio(resto).length > 0) { + throw new SyntaxError("Texto inesperado despues de programa"); } - return expr; + return expresion; } -console.log(parse("+(a, 10)")); -// → {type: "apply", -// operator: {type: "word", name: "+"}, -// args: [{type: "word", name: "a"}, -// {type: "value", value: 10}]} +console.log(analizar("+(a, 10)")); +// → {tipo: "aplicar", +// operador: {tipo: "palabra", nombre: "+"}, +// argumentos: [{tipo: "palabra", nombre: "a"}, +// {tipo: "valor", valor: 10}]} ``` {{index "error message"}} -It works! It doesn't give us very helpful information when it fails -and doesn't store the line and column on which each expression starts, -which might be helpful when reporting errors later, but it's good -enough for our purposes. +¡Funciona! No nos da información muy útil cuando falla +y no almacena la línea y la columna en que comienza cada expresión, +lo que podría ser útil al informar errores más tarde, pero es lo +suficientemente bueno para nuestros propósitos. -## The evaluator +## El evaluador -{{index "evaluate function", evaluation, interpretation, "syntax tree", "Egg language"}} +{{index "evaluar función", evaluación, interpretación, "árbol sintáctico", "lenguaje de Egg"}} -What can we do with the syntax tree for a program? Run it, of course! -And that is what the evaluator does. You give it a syntax tree and a -scope object that associates names with values, and it will evaluate -the expression that the tree represents and return the value that this -produces. +¿Qué poddemos hacer son el árrbol sintáctico de un programa? ¡Correrlo, por supuesto! +Y eso es lo que el evaluador hace. Le das un árbol sintáctico y un objeto de ámbito +que asocie nombres con valores, y evaluará la expresión que el árbol representa y regresará +el valor que el árbol produce. ```{includeCode: true} const specialForms = Object.create(null); -function evaluate(expr, scope) { - if (expr.type == "value") { - return expr.value; - } else if (expr.type == "word") { - if (expr.name in scope) { - return scope[expr.name]; +function evaluate(expresion, scope) { + if (expresion.type == "value") { + return expresion.value; + } else if (expresion.type == "word") { + if (expresion.name in scope) { + return scope[expresion.name]; } else { throw new ReferenceError( - `Undefined binding: ${expr.name}`); + `Undefined binding: ${expresion.name}`); } - } else if (expr.type == "apply") { - let {operator, args} = expr; + } else if (expresion.type == "apply") { + let {operator, args} = expresion; if (operator.type == "word" && operator.name in specialForms) { - return specialForms[operator.name](expr.args, scope); + return specialForms[operator.name](expresion.args, scope); } else { let op = evaluate(operator, scope); if (typeof op == "function") { @@ -314,47 +312,48 @@ function evaluate(expr, scope) { } ``` -{{index "literal expression", scope}} +{{index "expresión literal", ámbito}} + +El evaluador tiene código para cada uno de los tipos de ((exprresión)). Un +expresión literal de un valor produce este valor. (Por ejemplo, la expresión `100` evalúa +al número 100.) Para un vínculo de valor, debemos verificar si está definido en el ámbito, +y si lo está, traer el valor vinculado. -The evaluator has code for each of the ((expression)) types. A literal -value expression produces its value. (For example, the expression -`100` just evaluates to the number 100.) For a binding, we must check -whether it is actually defined in the scope and, if it is, fetch the -binding's value. +{{index [función, aplicación]}} -{{index [function, application]}} +La apliación implica más cosas. Si son una ((forma especial)), como +`if`, no evaluamos nada y pasamos las expresiones argumento, junto con el +ámbito, a la función que maneja esta forma. Si es una llamada normal, evaluamos el operador, +verificamos que sea una función, y la llamamos con los argumentos evaluados. -Applications are more involved. If they are a ((special form)), like -`if`, we do not evaluate anything and pass the argument expressions, -along with the scope, to the function that handles this form. If it is -a normal call, we evaluate the operator, verify that it is a function, -and call it with the evaluated arguments. +Usamos valores función planos de JavaScript para representar los valores función de Egg. +Regresaremos a esto [más tardde](language#egg_fun), cuando forma especial llamada +`fun` está definida. -We use plain JavaScript function values to represent Egg's function -values. We will come back to this [later](language#egg_fun), when the -special form called `fun` is defined. -{{index readability, "evaluate function", recursion, parsing}} +{{index legibilidad, "evaluar función", recursión, parseo}} -The recursive structure of `evaluate` resembles the similar structure -of the parser, and both mirror the structure of the language itself. -It would also be possible to integrate the parser with the evaluator -and evaluate during parsing, but splitting them up this way makes the -program clearer. +La estructura recursiva de `evaluate` se parece a la estructura del parser, y +ambos reflejan la estructura del lenguaje mismo. También sería posible +integrar el parser con el evaluador y evaluar durante el parseo, +pero separarlos de esta manera hace el programa más claro. -{{index "Egg language", interpretation}} -This is really all that is needed to interpret Egg. It is that simple. -But without defining a few special forms and adding some useful values -to the ((environment)), you can't do much with this language yet. +{{index "lenguaje Egg", interpretación}} -## Special forms +Esto es todo lo que se necesita para interpretar Egg. Es así de simple. +Pero si no definimos unas cuantas formas especiales y agregamos algunos +valores útiles para el ((entorno)), todavía no podemos hacer mucho con +el lenguaje. -{{index "special form", "specialForms object"}} +## Formas especiales + +{{index "formas especiales", "objeto specialForms"}} + +El objeto `specialForms` es usado para definir una sintaxis especial en +Egg. Asocia palabras con funciones que evalúan estas formas. Ahora mismo está +vacío. Agreguemos `if`. -The `specialForms` object is used to define special syntax in Egg. It -associates words with functions that evaluate such forms. It is -currently empty. Let's add `if`. ```{includeCode: true} specialForms.if = (args, scope) => { @@ -368,30 +367,30 @@ specialForms.if = (args, scope) => { }; ``` -{{index "conditional execution", "ternary operator", "?: operator", "conditional operator"}} +{{index "ejecución condicional", "operador ternario", "operador ?:", "operador condicional"}} + +El constructo `if` de Egg espera exactamente tres arrgumentos. Evaluará el primero, +y si el resultado no es `false`, evaluará el segundo. En otro caso, el +tercero será evaluado. Esta forma `if` es más parecidda al operaddor +ternario `?:` que al `if` if de JavaScript. Es una expresión, no una +sentencia, y produce un valor: el resultado del segundo o tercer argumento. -Egg's `if` construct expects exactly three arguments. It will evaluate -the first, and if the result isn't the value `false`, it will evaluate -the second. Otherwise, the third gets evaluated. This `if` form is -more similar to JavaScript's ternary `?:` operator than to -JavaScript's `if`. It is an expression, not a statement, and it -produces a value, namely, the result of the second or third argument. +{{index Booleano}} -{{index Boolean}} +Egg también se diferencia de JavaScript en como maneja el valor de la +condición para `if`. No tratará cosas como cero o la cadena vacía como +falso, solamente el valor exacto `false`. -Egg also differs from JavaScript in how it handles the condition value -to `if`. It will not treat things like zero or the empty string as -false, only the precise value `false`. +{{index "evaluación en corto circuito"}} -{{index "short-circuit evaluation"}} +La razón por la que necesitamos representar `if` como una forma especial, +en vez de como una función regular, es que todos los argumentos de las +funciones son evaluados antes de que la función sea llamada, mientras +que el `if` debe evaluar solo el segundo _o_ el tercer argumento, +dependiendo del valor del primero. -The reason we need to represent `if` as a special form, rather than a -regular function, is that all arguments to functions are evaluated -before the function is called, whereas `if` should evaluate only -_either_ its second or its third argument, depending on the value of -the first. +La forma `while` es similar. -The `while` form is similar. ```{includeCode: true} specialForms.while = (args, scope) => { @@ -408,9 +407,10 @@ specialForms.while = (args, scope) => { }; ``` -Another basic building block is `do`, which executes all its arguments -from top to bottom. Its value is the value produced by the last -argument. +Otro bloque de construcción principal es `do`, que ejecuta todos sus argumentos +de arriba hacia abajo. Su valos es el valorr producido por el último +argumento. + ```{includeCode: true} specialForms.do = (args, scope) => { @@ -422,14 +422,15 @@ specialForms.do = (args, scope) => { }; ``` -{{index ["= operator", "in Egg"], [binding, "in Egg"]}} +{{index "operador ="}} + +Para ser capaces de crear ((asignaciones)) y darle nuevos valores, +además necesitamos crear una forma llamada `define`. Espera una palabra +como prime argumento y una expresión producciendo un valor para asignar +a esa palabra como segundo argumento. Ya que `define`, como todo, +es una expresión, debe regresar un valor. Haremos que regrese el valor que +fue asignado (justo como el operador `=` de JavaScript). -To be able to create bindings and give them new values, we also -create a form called `define`. It expects a word as its first argument -and an expression producing the value to assign to that word as its -second argument. Since `define`, like everything, is an expression, it -must return a value. We'll make it return the value that was assigned -(just like JavaScript's `=` operator). ```{includeCode: true} specialForms.define = (args, scope) => { @@ -442,19 +443,18 @@ specialForms.define = (args, scope) => { }; ``` -## The environment +## El entorno -{{index "Egg language", "evaluate function", [binding, "in Egg"]}} +{{index "lenguaje Egg", "evaluar función"}} -The ((scope)) accepted by `evaluate` is an object with properties -whose names correspond to binding names and whose values correspond to -the values those bindings are bound to. Let's define an object to -represent the ((global scope)). +El ((ámbito)) aceptado por `evaluate` es un objeto con propiedades +de las que sus nombres corresponden a los nombres de las vinculaciones en el ámbito +y sus valores corresnponden a los valores de esos vínculos. Ahora definamos un objeto +para representar el ((ámbito global)). -To be able to use the `if` construct we just defined, we must have -access to ((Boolean)) values. Since there are only two Boolean values, -we do not need special syntax for them. We simply bind two names to -the values `true` and `false` and use them. +Para poder usar el constructo `if` que acabamos de definir, necesitamos acceder +a valores ((Booleano))s. Como solo hay dos valores Booleanos, no necesitamos una sintaxis +especial. Simplemente asociaremos dos nombres a los valores `true` y `false` y usaremos estos nombres. ```{includeCode: true} const topScope = Object.create(null); @@ -463,7 +463,7 @@ topScope.true = true; topScope.false = false; ``` -We can now evaluate a simple expression that negates a Boolean value. +Podemos evaluar una expresión simple que niegue un Booleano. ``` let prog = parse(`if(true, false, true)`); @@ -471,13 +471,13 @@ console.log(evaluate(prog, topScope)); // → false ``` -{{index arithmetic, "Function constructor"}} +{{index aritmética, "constructor de Función"}} + +Para proveer la ((aritmética)) y ((operador))es de ((comparación)), +necesitamos además añadir algunas funciones al ((ámbito)). Para mantener +el código peequeño, usaremos `Function` para sintetizar varias +funciones de operador en un ciclo, en vez de definirlas individualmente. -To supply basic ((arithmetic)) and ((comparison)) ((operator))s, we -will also add some function values to the ((scope)). In the interest -of keeping the code short, we'll use `Function` to synthesize a bunch -of operator functions in a loop, instead of defining them -individually. ```{includeCode: true} for (let op of ["+", "-", "*", "/", "==", "<", ">"]) { @@ -485,8 +485,9 @@ for (let op of ["+", "-", "*", "/", "==", "<", ">"]) { } ``` -A way to ((output)) values is also useful, so we'll wrap -`console.log` in a function and call it `print`. +Tener una manera de ((devolver)) valores tambbién es muy útil, +así que envolveremos `console.log` en una función y la llamaremos +`print`. ```{includeCode: true} topScope.print = value => { @@ -495,11 +496,12 @@ topScope.print = value => { }; ``` -{{index parsing, "run function"}} +{{index parseo, "función run"}} + +Esto nos da suficientes herramientas elementales para escribir +programas simples. La siguiente función provee una forma conveniente +de leer un programa y correrlo en un ámbito nuevo. -That gives us enough elementary tools to write simple programs. The -following function provides a convenient way to parse a program and -run it in a fresh scope: ```{includeCode: true} function run(program) { @@ -507,11 +509,11 @@ function run(program) { } ``` -{{index "Object.create function", prototype}} +{{index "función Object.create", prototipo}} -We'll use object prototype chains to represent nested scopes so that -the program can add bindings to its local scope without changing the -top-level scope. +Usareomos las cadenas de prototipos de los objetos para representar +ámbitos anidados, para que el programa pueda añadir asignaciones a su +ámbito local sin cambiar el ámbito superior. ``` run(` @@ -525,25 +527,27 @@ do(define(total, 0), // → 55 ``` -{{index "summing example", "Egg language"}} +{{index "ejemplo de suma", "lenguaje Egg"}} + +Este es el programa que hemos visto varias veces antes, que calcula la suma +de los números del 1 a 10, expresado en Egg. Es claramente más +feo que su equivalente en JavaScript, peror no está tan mal para +un lenguaje implementado en menos de 150 ((líneas de código)). -This is the program we've seen several times before, which computes -the sum of the numbers 1 to 10, expressed in Egg. It is clearly uglier -than the equivalent JavaScript program—but not bad for a language -implemented in less than 150 ((lines of code)). {{id egg_fun}} -## Functions +## Funciones + +{{index función, "lenguaje Egg"}} -{{index function, "Egg language"}} +Un lenguaje de programación sin funciones es un lenguaje de +programación pobre, en efecto. -A programming language without functions is a poor programming -language indeed. +Afortunadamente, no es difícil agregar un constructo `fun`, +que trate su último argumento como el cuerpo de la función y use +todos los argumentos antes de ese como los parámetros de la función. -Fortunately, it isn't hard to add a `fun` construct, which treats its -last argument as the function's body and uses all arguments before -that as the names of the function's parameters. ```{includeCode: true} specialForms.fun = (args, scope) => { @@ -571,80 +575,83 @@ specialForms.fun = (args, scope) => { }; ``` -{{index "local scope"}} +{{index "ámbito local"}} -Functions in Egg get their own local scope. The function produced by -the `fun` form creates this local scope and adds the argument bindings -to it. It then evaluates the function body in this scope and returns -the result. +Las funciones en Egg tienen su propio ámbito local. La función producida +por la forma `fun` crea este entorno local y agrega las asociaciones de +argumentos a este. Entonces evalúa el cuerpo de la función en este ámbito +y regresa el resultado. ```{startCode: true} run(` do(define(plusOne, fun(a, +(a, 1))), - print(plusOne(10))) + imprimir(plusOne(10))) `); // → 11 run(` do(define(pow, fun(base, exp, - if(==(exp, 0), + si(==(exp, 0), 1, *(base, pow(base, -(exp, 1)))))), - print(pow(2, 10))) + imprimir(pow(2, 10))) `); // → 1024 ``` -## Compilation +## Compilación -{{index interpretation, compilation}} +{{index interpretación, compilación}} -What we have built is an interpreter. During evaluation, it acts -directly on the representation of the program produced by the parser. +Lo que hemos construido es un intérprete. Durante la evaluación, actúa +directamente en la representación del programa producido por el +parseador. -{{index efficiency, performance, [binding, definition], [memory, speed]}} -_Compilation_ is the process of adding another step between the -parsing and the running of a program, which transforms the program -into something that can be evaluated more efficiently by doing as much -work as possible in advance. For example, in well-designed languages -it is obvious, for each use of a binding, which binding is being -referred to, without actually running the program. This can be used to -avoid looking up the binding by name every time it is accessed, -instead directly fetching it from some predetermined memory -location. +{{index eficiencia, rendimiento}} -Traditionally, ((compilation)) involves converting the program to -((machine code)), the raw format that a computer's processor can -execute. But any process that converts a program to a different -representation can be thought of as compilation. +La _Compilación_ es el proceso de añadir otro paso entre la lectura +del programa y su ejecución, que transforma el programa en algo que +pueda ser evaluado más eficientemente, haciendo la mayor cantidad +de trabajo posible por adelantado. Por ejemplo, en lenguajes bien +diseñados es obvio, para cada uso de una ((asignación)), a qué asignación +te estás refiriendo, sin correr el programa. Esto puede ser usado para +evitar buscar la asociación por nombbre cada vez que se usa, y, en vez de eso, +traer directamente el valor de una locación predeterminada de +((memoria)). -{{index simplicity, "Function constructor", transpilation}} +Tradicionalmente, la ((compilación)) implica convertir el programa +en ((código máquina)), el formato crudo que el procesador de la computadora +puede ejecutar. Pero puedes pensar en compilación como cualquier proceso +que convierta un programa en una representación diferente. -It would be possible to write an alternative ((evaluation)) strategy -for Egg, one that first converts the program to a JavaScript program, -uses `Function` to invoke the JavaScript compiler on it, and then runs -the result. When done right, this would make Egg run very fast while -still being quite simple to implement. +{{index simplicidad, "función constructor", transpilación}} -If you are interested in this topic and willing to spend some time on -it, I encourage you to try to implement such a compiler as an -exercise. +Sería posible escribir una estrategia de ((evaluación)) alternativa +para Egg, una que primero convierrta el programa a uno de JavaScript, +usa `Function` para invocar el compilador de JavaSctipt, y después correr +el resultado. Cuando se hace correctamente, esto haría que Egg +corrra muy rápiddo mientras todavía sigue siendo simple implementarlo. -## Cheating +Si estás interesado en este tema y dispuesto a gastar algo de tiempo, +te animo a que trates de implementar el compilador como ejercicio. -{{index "Egg language"}} +## Haciendo trampa + +{{index "lenguaje Egg"}} + +Cuando definimos `id` y `while`, probablemente te diste cuenta de que son +más o menos envoltorios triviales alrededor de los `if` y `while` de +JavaScript. Similarmente, los valores en Egg son solo valores de +JavaScript comunes. -When we defined `if` and `while`, you probably noticed that they were -more or less trivial wrappers around JavaScript's own `if` and -`while`. Similarly, the values in Egg are just regular old JavaScript -values. +Si comparas la implementación de Egg, construida soobre JavaScript, +con la cantidad de trabajo y complejidad requerida para +construir un lenguaje de programación directamente en la funcionalidad +pura y cruda provista por una máquina, la diferencia es grande. Sin embargo, +con suerte este ejemplo te dio un vistazo de la forma en +que un ((lenguaje de programación)) funciona. -If you compare the implementation of Egg, built on top of JavaScript, -with the amount of work and complexity required to build a programming -language directly on the raw functionality provided by a machine, the -difference is huge. Regardless, this example ideally gave you an -impression of the way ((programming language))s work. And when it comes to getting something done, cheating is more effective than doing everything yourself. Though the toy language in @@ -692,9 +699,9 @@ described in its domain, and nothing else. ### Arrays -{{index "Egg language", "arrays in egg (exercise)", [array, "in Egg"]}} +{{index "Egg language", "arrays in egg (exercise)"}} -Add support for arrays to Egg by adding the following three +Add support for ((array))s to Egg by adding the following three functions to the top scope: `array(...values)` to construct an array containing the argument values, `length(array)` to get an array's length, and `element(array, n)` to fetch the n^th^ element from an @@ -820,14 +827,15 @@ console.log(parse("a # one\n # two\n()")); // operator: {type: "word", name: "a"}, // args: []} ``` + if}} {{hint -{{index "comments in egg (exercise)", [whitespace, syntax]}} +{{index comment, "comments in egg (exercise)"}} Make sure your solution handles multiple comments in a row, with -potentially whitespace between or after them. +potentially ((whitespace)) between or after them. A ((regular expression)) is probably the easiest way to solve this. Write something that matches "whitespace or a comment, zero or more @@ -841,7 +849,7 @@ hint}} {{index [binding, definition], assignment, "fixing scope (exercise)"}} -Currently, the only way to assign a binding a value is `define`. +Currently, the only way to assign a ((binding)) a value is `definir`. This construct acts as a way both to define new bindings and to give existing ones a new value. @@ -854,7 +862,7 @@ always found it an awkward way to handle ((scope)). {{index "ReferenceError type"}} -Add a special form `set`, similar to `define`, which gives a binding a +Add a special form `set`, similar to `definir`, which gives a binding a new value, updating the binding in an outer scope if it doesn't already exist in the inner scope. If the binding is not defined at all, throw a `ReferenceError` (another standard error type). @@ -880,23 +888,24 @@ specialForms.set = (args, scope) => { }; run(` -do(define(x, 4), - define(setx, fun(val, set(x, val))), +hacer(definir(x, 4), + definir(setx, fun(val, set(x, val))), setx(50), - print(x)) + imprimir(x)) `); // → 50 run(`set(quux, true)`); // → Some kind of ReferenceError ``` + if}} {{hint -{{index [binding, "compilation of"], assignment, "getPrototypeOf function", "hasOwnProperty method", "fixing scope (exercise)"}} +{{index [binding, definition], assignment, "getPrototypeOf function", "hasOwnProperty method", "fixing scope (exercise)"}} You will have to loop through one ((scope)) at a time, using -`Object.getPrototypeOf` to go to the next outer scope. For each scope, +`Object.getPrototypeOf` to go the next outer scope. For each scope, use `hasOwnProperty` to find out whether the binding, indicated by the `name` property of the first argument to `set`, exists in that scope. If it does, set it to the result of evaluating the second argument to diff --git a/13_browser.md b/13_browser.md index 3ff233770..665193a02 100644 --- a/13_browser.md +++ b/13_browser.md @@ -1,11 +1,11 @@ -# JavaScript and the Browser +# JavaScript y el Navegador {{quote {author: "Tim Berners-Lee", title: "The World Wide Web: A very short personal history", chapter: true} -The dream behind the Web is of a common information space in which we -communicate by sharing information. Its universality is essential: the -fact that a hypertext link can point to anything, be it personal, -local or global, be it draft or highly polished. +El sueño detrás de la Web es el de un espacio común de información +en el cual nos comunicamos compartiendo información. +Su universalidad es esencial: el echo de que un enlace pueda apuntar a cualquier cosa, +ya sea personal, local o global, ya sea un borrador o este muy pulido. quote}} @@ -13,164 +13,167 @@ quote}} {{figure {url: "img/chapter_picture_13.jpg", alt: "Picture of a telephone switchboard", chapter: "framed"}}} -The next chapters of this book will talk about web browsers. Without -web ((browser))s, there would be no JavaScript. Or even if there were, -no one would ever have paid any attention to it. +Los próximos capítulos del libro hablarán del navegador web. Sin +((navegador))es web, no existiría JavaScript. Incluso, si existieran, +nadie le habría puesto atención. {{index decentralization, compatibility}} -Web technology has been decentralized from the start, not just -technically but also in the way it evolved. Various browser vendors -have added new functionality in ad hoc and sometimes poorly thought-out -ways, which then, sometimes, ended up being adopted by others—and -finally set down as in ((standards)). - -This is both a blessing and a curse. On the one hand, it is empowering -to not have a central party control a system but have it be improved -by various parties working in loose ((collaboration)) (or occasionally -open hostility). On the other hand, the haphazard way in which the Web -was developed means that the resulting system is not exactly a shining -example of internal ((consistency)). Some parts of it are downright -confusing and poorly conceived. - -## Networks and the Internet - -Computer ((network))s have been around since the 1950s. If you put -cables between two or more computers and allow them to send data back -and forth through these cables, you can do all kinds of wonderful -things. - -And if connecting two machines in the same building allows us to do -wonderful things, connecting machines all over the planet should be -even better. The technology to start implementing this vision was -developed in the 1980s, and the resulting network is called the -_((Internet))_. It has lived up to its promise. - -A computer can use this network to shoot bits at another computer. For -any effective ((communication)) to arise out of this bit-shooting, the -computers on both ends must know what the bits are supposed to -represent. The meaning of any given sequence of bits depends entirely -on the kind of thing that it is trying to express and on the -((encoding)) mechanism used. +La tecnología web ha estado descentralizada desde el principio, +no sólo técnicamente, también en la forma en que ha evolucionado. +Distintos proveedores de nagevadores han agregado nueva funcionalidad +adhoc y algunas veces ha sido de una forma deficiente, y entonces, +termina siendo adoptada por otros-y finalmente es aceptada como +((estándar)). + +Esta es a la vez una bendición y una maldición. Por un lado, empodera el no tener +un control central, sino que es mejorado por distintas partes trabajando +en ((colaboración)) (o a veces hostilidad abierta). Por otro lado, la +forma aleatoria en la que la web fue desarrollada significa que el sistema +resultante no es exactamente un ejemplo de ((consistencia)) interna. Algunas +partes son francamente confusas y pobremente concebidas. + +## Redes e Internet + +Las ((red))es de computadoras han existido de los 50s. Si pones cables +entre dos o más computadoras y les permites enviar y recibir información +a través de esos cables, puedes hacer todo tipo de cosas increíbles. + +Y si conectar dos computadoras en el mismo edificio nos permite hacer +cosas increíbles, conectar computadoras alrededor del mundo debería ser +incluso mejor. La tecnología para iniciar la implementación de esta visión +fue desarrollada en los 80s, y la red resultante es llamada _((Internet))_. +Ha cumplido su promesa. + +Una computadora puede usar esta red para mandar bits a otra computadora. Para +cualquier ((comunicación)) efectiva, las computadoras en ambos lados deben +saber qué se supone que representan los bits. El significado de cualquier +secuencia de bits depende enteramente del tipo de cosas que se está tratando +de expresar y del mecanismo de ((codificación)) utilizado. {{index [network, protocol]}} -A _network ((protocol))_ describes a style of communication over a -((network)). There are protocols for sending email, for fetching email, -for sharing files, and even for controlling computers that happen to be -infected by malicious software. +Un _((protocolo)) de red_ describe un estilo de comunicación sobre una ((red)). +Hay protocolos para mandar correos electrónicos, para recibir correos electrónicos, +para compartir archivos, e incluso para controlar computadoras que resultan estar +infectadas por software malicioso. {{indexsee "Hypertext Transfer Protocol", HTTP}} -For example, the _Hypertext Transfer Protocol_ (((HTTP))) is -a protocol for retrieving named ((resource))s (chunks of information, -such as web pages or pictures). It specifies that the side making the -request should start with a line like this, naming the resource and -the version of the protocol that it is trying to use: +Por ejemplo, el Protocolo de Transferencia de Hipertexto +(((HTPP del inglés _Hypertext Transfer Protocol_))) es un protocolo +para obtener ((recurso))s nombrados (fragmentos de información, como páginas web +o imágenes). Especifica que el lado que hace la solicitud debe iniciar con +una linea como esta, indicando el recurso y la versión del protocolo +que se intenta usar: ```{lang: "text/plain"} GET /index.html HTTP/1.1 ``` -There are a lot more rules about the way the requester can include more -information in the ((request)) and the way the other side, which -returns the resource, packages up its content. We'll look at HTTP in a -little more detail in [Chapter ?](http). +Existen otras reglas acerca de cómo el solicitante puede incluir más +información en la ((solicitud)) y la forma en qué el otro lado, que +devuelve el recurso, empaca el contenido. Examinaremos HTTP en más detalle +en el capítulo [Chapter ?](http). {{index layering, stream, ordering}} -Most protocols are built on top of other protocols. HTTP treats the -network as a streamlike device into which you can put bits and have -them arrive at the correct destination in the correct order. As we saw -in [Chapter ?](async), ensuring those things is already a rather -difficult problem. +La mayoría de los protocolos están construidos sobre otros protocolos. HTTP +utiliza la red como un dispositivo en el que pones bits y llegan a su destino +correcto en el orden correcto. Como vimos en el capítulo [Chapter ?](async), +asegurar eso ya es un problema muy complicado. {{index TCP}} {{indexsee "Transmission Control Protocol", TCP}} -The _Transmission Control Protocol_ (TCP) is a ((protocol)) that -addresses this problem. All Internet-connected devices "speak" it, and -most communication on the ((Internet)) is built on top of it. +El _Protocolo de Control de Transmisión_ (TCP del inglés Transmission +Control Protocol) es un ((protocolo)) que se encarga de este problema. +Todos los dispositivos conectados a internet lo "hablan", y la mayor +parte de la comunicación en ((internet)) está construida encima de él. {{index "listening (TCP)"}} -A TCP ((connection)) works as follows: one computer must be waiting, -or _listening_, for other computers to start talking to it. To be able -to listen for different kinds of communication at the same time on a -single machine, each listener has a number (called a _((port))_) -associated with it. Most ((protocol))s specify which port should be -used by default. For example, when we want to send an email using the -((SMTP)) protocol, the machine through which we send it is expected to -be listening on port 25. - -Another computer can then establish a ((connection)) by connecting to -the target machine using the correct port number. If the target -machine can be reached and is listening on that port, the connection -is successfully created. The listening computer is called the -_((server))_, and the connecting computer is called the _((client))_. +Una ((conexión)) TCP funciona así: un computadora debe estar esperando, +o _escuchando_, a que otras computadoras inicien a hablarle. Para poder +escuchar diferentes tipos comunicaciones a la vez en una sola computadora, +cada oyente tiene un número (llamado ((_puerto_))) asociado a él. La +mayoria de los ((protocolo))s especifican qué puerto deben usar por +defecto. Por ejemplo, cuando queremos enviar un correo electrónico +utilizando el protocolo ((SMTP)), la máquina a través de la cual la +enviamos esté escuchando en el puerto 25. + +Otra computadora pue establecer una ((conexion)) conectandose a la +ocmputadora destino usando el puerto correcto. Si la computadora destino +puede ser alcanzada y está escuchando en ee puerto, la conexión se +crea de forma exitosa. La computadora oyente es llamada el _((servidor))_, +y la computadora que se conecta es llamado el _((cliente))_. {{index [abtraction, "of the network"]}} -Such a connection acts as a two-way ((pipe)) through which bits can -flow—the machines on both ends can put data into it. Once the bits are -successfully transmitted, they can be read out again by the machine on -the other side. This is a convenient model. You could say that ((TCP)) -provides an abstraction of the network. +Esa conexión actua como un una tubería de dos direcciones por la cual +los bits pueden fluir, las computadoras en ambos extremos pueden poner +información en esa tubería. Una vez que los bits son transmitidos de forma +exitosa, pueden ser leidos nuevamente por la computadora en el otro lado. +Este es un modelo conveniente. Podrías decir que ((TCP)) provee una +abstracción de la red. {{id web}} -## The Web +## La Web -The _((World Wide Web))_ (not to be confused with the ((Internet)) as -a whole) is a set of ((protocol))s and formats that allow us to visit -web pages in a browser. The "Web" part in the name refers to the fact -that such pages can easily link to each other, thus connecting into a -huge ((mesh)) that users can move through. +La ((Red Mundial)) (en inglés World Wide Web, no confundir con ((Internet))) +es un conjunto de ((protocolo))s y formatos que nos permiten visitar +páginas web en un navegador. La parte "Web" del nombre se refiere al +hecho de que esas páginas pueden enlazarse fácilmente unas con otras, +conectandose a una gran ((red)) a entre la cual los usuarios pueden +moverse. -To become part of the Web, all you need to do is connect a machine to -the ((Internet)) and have it listen on port 80 with the ((HTTP)) -protocol so that other computers can ask it for documents. +Para llegar a ser parte de la Web, todo lo que tienes que hacer es +conectar una computadora a ((Internet)) y ponerla a escuchar en el +puerto 80 con el protocolo ((HTTP)) para que otras computadoras puedan +pedirle documentos. {{index URL}} {{indexsee "Uniform Resource Locator", URL}} -Each ((document)) on the Web is named by a _Uniform Resource Locator_ -(URL), which looks something like this: +Cada ((documento)) en la Web es nombrado por un _Localizador Uniforme +de Recursos_ (URL del inglés Uniform Resource Locator), y se ve así: ```{lang: null} http://eloquentjavascript.net/13_browser.html | | | | - protocol server path + protocolo servidor ruta ``` {{index HTTPS}} -The first part tells us that this URL uses the HTTP ((protocol)) (as -opposed to, for example, encrypted HTTP, which would be _https://_). -Then comes the part that identifies which ((server)) we are requesting -the document from. Last is a path string that identifies the specific -document (or _((resource))_) we are interested in. - -Machines connected to the Internet get an _((IP address))_, which is a -number that can be used to send messages to that machine, and looks -something like `149.210.142.219` or `2001:4860:4860::8888`. But lists -of more or less random numbers are hard to remember and awkward to -type, so you can instead register a _((domain)) name_ for a specific -address or set of addresses. I registered _eloquentjavascript.net_ to -point at the IP address of a machine I control and can thus use that -domain name to serve web pages. +La primera sección nos dice que ésta URL utiliza el ((protocolo)) HTTP +(La versión ecriptada sería _https://_). Posteriormente sigue la sección +que identifica a qué ((servidor)) estamos solicitado el documento. La +última sección es una cadena de ruta que identifica el documento especifico +(o _((recurso))_) que nos interesa. + +Las computadoras conectadas a internet obtienen una _((dirección IP))_, que +es un número que puede utilizado para enviar mensajes a esa computadora, ese +número se ve así `149.210.142.219` o así `2001:4860:4860::8888`. Pero +números casi aleatorios son dificiles de recordar y es raro para escribirlos, +así que en lugar de eso puedes registrar un _nombre de ((dominio))_ para +un una dirección específica o un conjunto de direcciones. Yo registré +_eloquentjavascript.net_ para apuntar a una dirección IP de una computadora +que controlo y así puedo usar ese nombre de dominio para entregar páginas +web. {{index browser}} -If you type this URL into your browser's ((address bar)), the browser -will try to retrieve and display the ((document)) at that URL. First, -your browser has to find out what address _eloquentjavascript.net_ -refers to. Then, using the ((HTTP)) protocol, it will make a -connection to the server at that address and ask for the resource -_/13_browser.html_. If all goes well, the server sends back a -document, which your browser then displays on your screen. +Si escribes esa URL en la ((barra de dirección)) del navegador, el navegador +intentará obtener y mostrar el document en esa URL. Primero, el navegador +tiene que encontrar la dirección a la que _eloquentjavascript.net_ se +refiere. Entonces, utilizando el protocolo ((HTTP)), hará una conexión +al servidor en esa dirección y solicitará el recurso _/13_browser.html_. +Si todo sale bien, el servidor envía de vuelta un documento, que el +navegador muestra en la pantalla. ## HTML @@ -178,32 +181,32 @@ document, which your browser then displays on your screen. {{indexsee "Hypertext Markup Language", HTML}} -HTML, which stands for _Hypertext Markup Language_, is the document -format used for web pages. An HTML document contains ((text)), as well -as _((tag))s_ that give structure to the text, describing things such -as links, paragraphs, and headings. +HTML, que significa _Lenguaje de Marcado de Hipertexto_ (en inglés +Hypertext Markup Language), es el formato de documento utilizado +para páginas web. Un documento HTML contiene ((texto)), así como +((etiqueta))s que dan estructura al texto, describiendo elementos +como enlaces, párrafos y encabezados. -A short HTML document might look like this: +Un pequeño documento HTML puede verse así: ```{lang: "text/html"} - - My home page + Mi página de inicio -

My home page

-

Hello, I am Marijn and this is my home page.

-

I also wrote a book! Read it - here.

+

Mi página de inicio

+

Hola, mi nombre es Marijn y esta es mi página de inicio.

+

También escribí un libro! Léelo + aquí.

``` {{if book -This is what such a document would look like in the browser: +Así es como el documento se vería en el navegador: {{figure {url: "img/home-page.png", alt: "My home page",width: "6.3cm"}}} @@ -211,113 +214,117 @@ if}} {{index [HTML, notation]}} -The tags, wrapped in ((angle brackets)) (`<` and `>`, the symbols for -_less than_ and _greater than_), provide information about the -((structure)) of the document. The other ((text)) is just plain text. +Las etiquetas, encerradas entre ((paréntesis angulares)) (`<` y `>`, +los símbolos para _Menor qué_ y _Mayor qué_), proveen información acerca +de la ((estructura)) del documento. El otro ((texto)) es sólo texto +plano. {{index doctype, version}} -The document starts with ``, which tells the browser to -interpret the page as _modern_ HTML, as opposed to various dialects -that were in use in the past. +El documento inicia con ``, eso le indica al navegador +que interprete la página como HTML _moderno_, no como distintos lenguajes +que fueron utilizados en el pasado. {{index "head (HTML tag)", "body (HTML tag)", "title (HTML tag)", "h1 (HTML tag)", "p (HTML tag)"}} -HTML documents have a head and a body. The head contains information -_about_ the document, and the body contains the document itself. In -this case, the head declares that the title of this document is "My -home page" and that it uses the UTF-8 encoding, which is a way to -encode Unicode text as binary data. The document's body contains a -heading (`

`, meaning "heading 1"—`

` to `

` produce -subheadings) and two ((paragraph))s (`

`). +Los documentos HTML tienen un encabezado y un cuerpo. El encabezado +contiene información _acerca del_ documento, y el cuerpo contiene +el documento en sí. En este caso, el encabezado declara que el título +del documento es "Mi página de inicio" y que está utilizando la +codificación UTF-8, que es una forma de codificar texto Unicode como +información binaria. El cuerpo del documento contiene un encabezado +(`

`, que significa "encabezado 1", `

` a `

` produce +sub-encabezados) y dos ((párrafo))s (`

`). {{index "href attribute", "a (HTML tag)"}} -Tags come in several forms. An ((element)), such as the body, a -paragraph, or a link, is started by an _((opening tag))_ like `

` -and ended by a _((closing tag))_ like `

`. Some opening tags, such -as the one for the ((link)) (``), contain extra information in the -form of `name="value"` pairs. These are called _((attribute))s_. In -this case, the destination of the link is indicated with -`href="http://eloquentjavascript.net"`, where `href` stands for -"hypertext reference". +Las etiquetas vienen en distintas formas. Algunos ((elemento))s, como el cuerpo, +un párrafo, o un enlace, se inicia por una _((etiqueta de apertura))_ como +`

` y finaliza con una _((etiqueta de cierre))_ como `

`. Algunas +etiquetas de apertura, como la utilizada para los enlaces, (`
`), +contienen información extra en forma de pares `name="value"`. Estos +se llaman _((atributos))_. En este caso, el destino del enlace +está indicado con `href="http://eloquentjavascript.net"`, donde +`href` significa "referencia de hipertexto". {{index "src attribute", "self-closing tag", "img (HTML tag)"}} -Some kinds of ((tag))s do not enclose anything and thus do not need to -be closed. The metadata tag `` is an example of -this. +Algunos tipos de ((etiqueta))s no encierran nada, y por lo tanto no +necesitan ser cerradas. La etiqueta meta `` +es un ejemplo de esto. {{index [escaping, "in HTML"]}} -To be able to include ((angle brackets)) in the text of a document, -even though they have a special meaning in HTML, yet another form of -special notation has to be introduced. A plain opening angle bracket -is written as `<` ("less than"), and a closing bracket is written -as `>` ("greater than"). In HTML, an ampersand (`&`) character -followed by a name or character code and a semicolon (`;`) is called an _((entity))_ -and will be replaced by the character it encodes. +Para poder incluir paréntesis angulares en el texto de un documento, +aunque tengan un significado especial en HTML, otra forma de notación +especial tiene que ser introducida. Un paréntesis angular de apertura +es escrito como `<` (menor qué del inglés less than), y un +paréntesis angular de cierre es escrito como `>` (mayor qué del +inglés greater than). En HTML, un ampersand (`&`) seguido por un nombre +o código de caracter, y un punto y coma (`;`) es llamado una _((entidad))_ +y será reemplazada por el caracter que codifica. {{index ["backslash character", "in strings"], "ampersand character", "double-quote character"}} -This is analogous to the way backslashes are used in JavaScript -strings. Since this mechanism gives ampersand characters a special -meaning, too, they need to be escaped as `&`. Inside attribute -values, which are wrapped in double quotes, `"` can be used to -insert an actual quote character. +Esto es análogo a la forma en que las diagonales invertidas son +utilizadas en las cadenas en JavaScript. Ya que este mecanismo le +da a los carácteres ampersand un significado especial, también, +tienen que ser escapadas como `&`. Dentro de los valores de los +atributos, que son encerrados en comillas dobles, `"` pueden ser +utilizados para insertar un carácter de comillas. {{index "error tolerance", parsing}} -HTML is parsed in a remarkably error-tolerant way. When tags that -should be there are missing, the browser reconstructs them. The way in -which this is done has been standardized, and you can rely on all -modern browsers to do it in the same way. +HTML es analizado en una forma tolerante a errores. Cuando las etiquetas +que deberían estar ahí no están, el navegador las reconstruye. La forma +en que esto se realiza está estandarizado, y puedes confiar en que +todos los navegadores modernos lo realizarán de la misma manera. -The following document will be treated just like the one shown previously: +El siguiente documento será tratado identicamente al mostrado anteriormente: ```{lang: "text/html"} -My home page +Mi página de inicios -

My home page

-

Hello, I am Marijn and this is my home page. -

I also wrote a book! Read it - here. +

Mi página de inicio

+

Hola, mi nombre es Marijn y esta es mi página de inicio. +

También escribí un libro! Léelo + aquí. ``` {{index "title (HTML tag)", "head (HTML tag)", "body (HTML tag)", "html (HTML tag)"}} -The ``, ``, and `` tags are gone completely. The -browser knows that `` and `` belong in the head and that -`<h1>` means the body has started. Furthermore, I am no longer -explicitly closing the paragraphs since opening a new paragraph or -ending the document will close them implicitly. The quotes around the -attribute values are also gone. +Las etiquetas `<html>`, `<head>` y `<body>` fueron removidas completamente. +El navegador sabe que `<meta>` y `<title>` pertenecen al encabezado y +`<h1>` significa que el cuerpo del documento ha iniciado. Además, +los párrafos no se están cerrando explícitamente ya que el abrir un +nuevo párrafo o finalizar el documento los cierra implicitamente. +Las comillas alrededor de los valores de los atributos tampoco están. -This book will usually omit the `<html>`, `<head>`, and `<body>` tags -from examples to keep them short and free of clutter. But I _will_ -close tags and include quotes around attributes. +Este libro generalmente omitirá las etiquetas `<html>`, `<head>` y `<body>` +de los ejemplos para mantenerlos cortos. Pero cerraré etiquetas e incluiré +comillas alrededor de los atributos. {{index browser}} -I will also usually omit the ((doctype)) and `charset` declaration. -This is not to be taken as an encouragement to drop these from HTML -documents. Browsers will often do ridiculous things when you forget -them. You should consider the doctype and the `charset` metadata -to be implicitly present in examples, even when they are not actually shown -in the text. +Usualmente omitiré las declaraciones ((doctype)) y `charset`. Eso +no significa que tu tienes que hacer lo mismo. Los navegadores +puede llegar a hacer cosas ridiculas cuando las omites. Siempre +considera que las declaraciones ((doctype)) y `charset` están ahí +implicitamente en los ejemplos, incluso cuando no se muestran en el +texto. {{id script_tag}} -## HTML and JavaScript +## HTML y JavaScript {{index [JavaScript, "in HTML"], "script (HTML tag)"}} -In the context of this book, the most important HTML tag is -`<script>`. This tag allows us to include a piece of JavaScript in a -document. +En el contexto de este libro, la etiqueta HTML más importante es +`<script>`. Esta etiqueta nos permite incluir un fragmento de JavaScript +en un documento. ```{lang: "text/html"} <h1>Testing alert</h1> @@ -326,133 +333,131 @@ document. {{index "alert function", timeline}} -Such a script will run as soon as its `<script>` tag is encountered -while the browser reads the HTML. This page will pop up a dialog when -opened—the `alert` function resembles `prompt`, in that it pops up a -little window, but only shows a message without asking for input. +Ese comando será ejecutado tan pronto como su etiqueta `<script>` es +encontrada mientras el navegador lee el HTML. Esta página mostrará un +diálogo en cuanto sea abierta, la función `alert` se parece a `prompt` +en que muestra una pequeña ventana, pero sólo muestra un mensaje sin +esperar datos de entrada. {{index "src attribute"}} -Including large programs directly in HTML documents is often -impractical. The `<script>` tag can be given an `src` attribute to fetch a script file (a text file containing a JavaScript -program) from a URL. +Incluir grandes programas directamente en un documento HTML +es muchas veces impráctico. A la etiqueta `<script>` se le puede proveer +un atributo `src` para obtener un archivo de script (un archivo de texto +que contiene un fragmento de códido JavaScript) de una URL. ```{lang: "text/html"} <h1>Testing alert</h1> <script src="code/hello.js"></script> ``` -The _code/hello.js_ file included here contains the same -program—`alert("hello!")`. When an HTML page references other URLs as -part of itself—for example, an image file or a script—web browsers -will retrieve them immediately and include them in the page. +El archivo _code/hello.js_ incluido aquí contiene el mismo programa, +`alert("hello!")`. Cuando una página HTML hace referencia a otras URLs como +parte de si misma, por ejemplo, un archivo de imagen o un script, los navegadores +los recuperarán inmediatamente y los incluirán en la página. {{index "script (HTML tag)", "closing tag"}} -A script tag must always be closed with `</script>`, even if it refers -to a script file and doesn't contain any code. If you forget this, the -rest of the page will be interpreted as part of the script. +Una etiqueta de script siempre debe cerrarse con `</script>`, incluso si se +refiere a un archivo de script y no contiene código en su interior. Si olvidas +cerrarla, el resto de la página será interpretada como parte del script. {{index "relative path", dependency}} -You can load ((ES modules)) (see [Chapter ?](modules#es)) in the -browser by giving your script tag a `type="module"` attribute. Such -modules can depend on other modules by using ((URL))s relative to -themselves as module names in `import` declarations. +Puedes cargar ((módulos ES)) (ver [Chapter ?](modules#es)) en el navegador +si le provees a tu etiqueta script un atributo `type="module"`. Esos módulos +pueden depender de otros módulos usando ((URL))s relativas a si mismos como +nombres de módulos en declaraciones `import`. {{index "button (HTML tag)", "onclick attribute"}} -Some attributes can also contain a JavaScript program. The `<button>` -tag shown next (which shows up as a button) has an `onclick` -attribute. The attribute's value will be run whenever the button is -clicked. +Algunos atributos también pueden contener un programa JavaScript. La etiqueta +`<button>` mostrada a continuación (que muestra un botón) tiene un atributo +`onclick`. El valor del atributo será ejecutado siempre que el botón sea presionado. ```{lang: "text/html"} -<button onclick="alert('Boom!');">DO NOT PRESS</button> +<button onclick="alert('Boom!');">NO PRESIONAR</button> ``` {{index "single-quote character", [escaping, "in HTML"]}} -Note that I had to use single quotes for the string in the `onclick` -attribute because double quotes are already used to quote the whole -attribute. I could also have used `"`. +Nota que tuve que usar comillas simples para la cadena en el atributo `onclick` +porque las comillas dobles ya están siendo utilizadas para encerrar el atributo +completo. También pude haber usado `"`. -## In the sandbox +## En la caja de arena {{index "malicious script", "World Wide Web", browser, website, security}} -Running programs downloaded from the ((Internet)) is potentially -dangerous. You do not know much about the people behind most sites you -visit, and they do not necessarily mean well. Running programs by -people who do not mean well is how you get your computer infected by -((virus))es, your data stolen, and your accounts hacked. +Ejecutar programas descargados de ((Internet)) es pontencialmente peligroso. +No conoces mucho de las personas detrás de la mayoría de los sitios que visitas, +y no necesariamente quieren hacer el bien. Ejecutar programas de gente que no +quiere hacer el bien es como tu computadora se infecta de ((virus)), te roban +la información y compromenten tus cuentas. -Yet the attraction of the Web is that you can browse it without -necessarily ((trust))ing all the pages you visit. This is why browsers -severely limit the things a JavaScript program may do: it can't look -at the files on your computer or modify anything not related to the -web page it was embedded in. +Aunque la atracción de la Web es que puedes navegar sin necesariamente ((confiar)) +en todas las páginas que visitas. Ésa es la razón por la cual los navegadores +limitan en gran medida las cosas que un programa JavaScript puede hacer: no puede +revisar los archivos en tu computadora o modificar nada relacionado a la página web +en la que se inscrustó. {{index isolation}} -Isolating a programming environment in this way is called -_((sandbox))ing_, the idea being that the program is harmlessly -playing in a sandbox. But you should imagine this particular kind of -sandbox as having a cage of thick steel bars over it so that the -programs playing in it can't actually get out. +Aislar un entorno de programación en ésta forma es llamado _((sandbox))ing_, +la idea es que el programa pueda ejecutarse sin daños en una caja de arena. +Pero deberías imaginar que este tipo particular de caja de arena es como tener +los programas detrás de barras de acero y que no pueden salir. -The hard part of sandboxing is allowing the programs enough room to be -useful yet at the same time restricting them from doing anything -dangerous. Lots of useful functionality, such as communicating with -other servers or reading the content of the copy-paste ((clipboard)), -can also be used to do problematic, ((privacy))-invading things. +la parte complicada del sandboxing es permitirle al programa suficientes permisos +para ser útil y a la vez restringirlo de hacer cualquier cosa peligrosa. Mucha +funcionalidad, como comunicarse con otros servidores o leer el contenido del +portapapeles puede ser problematico, son cosas que invaden la ((privacidad)). {{index leak, exploit, security}} -Every now and then, someone comes up with a new way to circumvent the -limitations of a ((browser)) and do something harmful, ranging from -leaking minor private information to taking over the whole machine -that the browser runs on. The browser developers respond by fixing the -hole, and all is well again—until the next problem is discovered, and -hopefully publicized, rather than secretly exploited by some -government agency or ((mafia)). +De vez en cuando, alguien tiene una nueva forma de evitar las limitaciones de un +((navegador)) y hacer cosas dañinas, desde exponer información privada sin importancia +hasta tomar el control de toda la máquina sobre la que corre el navegador. Los +desarrolladores del navegador responden arreglando ese problema, y todo está bien +nuevamente, hasta que el próximo problema es descubierto, y tal vez se haga +público, en lugar de ser explotado de forma secreta por una agencia del gobierno +o ((mafia)). -## Compatibility and the browser wars +## Compatibilidad y la guerra de los navegadores {{index Microsoft, "World Wide Web"}} -In the early stages of the Web, a browser called ((Mosaic)) dominated -the market. After a few years, the balance shifted to -((Netscape)), which was then, in turn, largely supplanted by -Microsoft's ((Internet Explorer)). At any point where a single -((browser)) was dominant, that browser's vendor would feel entitled to -unilaterally invent new features for the Web. Since most users used -the most popular browser, ((website))s would simply start using those -features—never mind the other browsers. - -This was the dark age of ((compatibility)), often called the -_((browser wars))_. Web developers were left with not one unified Web -but two or three incompatible platforms. To make things worse, the -browsers in use around 2003 were all full of ((bug))s, and of course -the bugs were different for each ((browser)). Life was hard for people -writing web pages. +En las etapas tempranas de la Web, un navegador llamado ((Mosaic)) dominó el mercado. +Después de unos cuantos años, la balanza se inclinó hacia ((Netscape)), el cual fue +suplantado después por el browser de Microsoft ((Internet Explorer)). En cualquier punto +en el que un único ((navegador)) fuera dominante, el fabricante de ese navegador se +sentía con derecho de inventar nuevas caracteristicas unilateralmente para la Web. +Ya que la mayoria de los usuarios usaban el navegador más popular, los ((sitios web)) +simplemente empezarían a usar esas caracteristicas sin importarles los otros navegadores. + +Ésta fue la edad oscura de la ((compatibilidad)), algunas veces llamada +_((guerra de navegadores))_. Los desarrolladores Web tenían que lidiar con +dos o tres plataformas incompatibles. Para hacer las cosas peor, los navegadores +usados alrededor del 2003 estaban llenos de ((defectos)), y por supuesto, +los defectos eran distintos para cada ((navegador)). La vida era dura para los +que escribian páginas Web. {{index Apple, "Internet Explorer", Mozilla}} -Mozilla ((Firefox)), a not-for-profit offshoot of ((Netscape)), -challenged Internet Explorer's position in the late 2000s. Because -((Microsoft)) was not particularly interested in staying competitive -at the time, Firefox took a lot of market share away from it. Around -the same time, ((Google)) introduced its ((Chrome)) browser, and -Apple's ((Safari)) browser gained popularity, leading to a situation -where there were four major players, rather than one. +Mozilla ((Firefox)), una derivación sin fines de lucro de ((Netscape)), retó +la posición de Internet Explorer a finales de los 2000. Ya que ((Microsoft)) +no estaba interesado en mantenerse competitivo en ese momento, Firefox tomó +una gran parte del mercado de Internet Explorer. Más o menos al mismo tiempo, +((Google)) introdujo su navegador ((Chrome)), el navegador ((Safari)) de Apple +también ganó popularidad, llevando a una situación en la que existieron cuatro +grandes competidores, en lugar de uno. {{index compatibility}} -The new players had a more serious attitude toward ((standards)) and -better ((engineering)) practices, giving us less incompatibility and -fewer ((bug))s. Microsoft, seeing its market share crumble, came -around and adopted these attitudes in its Edge browser, which replaces -Internet Explorer. If you are starting to learn web development today, -consider yourself lucky. The latest versions of the major browsers -behave quite uniformly and have relatively few bugs. +Los nuevos competidores tuvieron una actitud más seria hacia los ((estándar))es y +mejores prácticas de ((ingeniería)), dándonos menos incompatibilidad y menos +defectos. Microsoft, al vez que su parte del mercado caerse, le dio la vuelta y +adoptó esas actitudes en su navegador Edge, el cual reemplaza a Internet Explorer. +Si tú estás empezando a aprender desarrollo web, considérate afortunado. Las +últimas versiones de los navegadores se comportan uniformemente bien y tienen +relativamente menos defectos. \ No newline at end of file diff --git a/14_dom.md b/14_dom.md index 0b058553a..4dc381c97 100644 --- a/14_dom.md +++ b/14_dom.md @@ -1,273 +1,290 @@ -# The Document Object Model +# El Modelo de Objeto del Documento -{{quote {author: "Friedrich Nietzsche", title: "Beyond Good and Evil", chapter: true} +{{quote {author: "Friedrich Nietzsche", title: "Más allá del bien y del mal", chapter: true} -Too bad! Same old story! Once you've finished building your house you -notice you've accidentally learned something that you really should -have known—before you started. +¡Tanto peor! ¡Otra vez la vieja historia! Cuando uno ha acabado de +construir su casa advierte que, mientras la construía, ha aprendido, sin darse +cuenta, algo que tendría que haber sabido absolutamente antes de comenzar +a construir. quote}} -{{figure {url: "img/chapter_picture_14.jpg", alt: "Picture of a tree with letters and scripts hanging from its branches", chapter: "framed"}}} +{{figure {url: "img/chapter_picture_14.jpg", alt: "Foto de un árbol con letras y _scripts_ colgando de sus ramas", chapter: "framed"}}} {{index drawing, parsing}} -When you open a web page in your browser, the browser retrieves the -page's ((HTML)) text and parses it, much like the way our parser from -[Chapter ?](language#parsing) parsed programs. The browser builds up a -model of the document's ((structure)) and uses this model to draw the -page on the screen. +Cuando abres una página web en tu navegador, el navegador obtiene el +texto de la página ((HTML)) y lo analiza, de una manera bastante +similar a la manera en que nuestro analizador del [Capítulo ?](language#parsing) +analizaba los programas. El navegador construye un modelo de la +((estructura)) del documento y utiliza este modelo para dibujar la +página en la pantalla. + {{index "live data structure"}} -This representation of the ((document)) is one of the toys that a -JavaScript program has available in its ((sandbox)). It is a ((data -structure)) that you can read or modify. It acts as a _live_ data -structure: when it's modified, the page on the screen is updated to -reflect the changes. +Esta representación del ((documento)) es uno de los juguetes que un +programa de JavaScript tiene disponible en su ((caja de arena)). Es +una ((estructura de datos)) que puedes leer o modificar. Y actúa como +una estructura en _tiempo real_: cuando se modifica, la página en la +pantalla es actualizada para reflejar los cambios. -## Document structure +## La estructura del documento {{index [HTML, structure]}} -You can imagine an HTML document as a nested set of ((box))es. -Tags such as `<body>` and `</body>` enclose other ((tag))s, which in -turn contain other tags or ((text)). Here's the example document from -the [previous chapter](browser): +Te puedes imaginar a un documento HTML como un conjunto anidado de +((caja))s. Las etiquetas como `<body>` y `</body>` encierran otras +((etiqueta))s, que a su vez, contienen otras etiquetas o ((texto)). +Este es el documento de ejemplo del +[capítulo anterior](browser): + ```{lang: "text/html", sandbox: "homepage"} <!doctype html> <html> <head> - <title>My home page + Mi página de inicio -

My home page

-

Hello, I am Marijn and this is my home page.

-

I also wrote a book! Read it - here.

+

Mi página de inicio

+

Hola, mi nombre es Marijn y esta es mi página de inicio.

+

También escribí un libro! Léelo + aquí.

``` -This page has the following structure: +Esta página tiene la siguiente estructura: {{figure {url: "img/html-boxes.svg", alt: "HTML document as nested boxes", width: "7cm"}}} {{indexsee "Document Object Model", DOM}} -The data structure the browser uses to represent the document follows -this shape. For each box, there is an object, which we can -interact with to find out things such as what HTML tag it represents -and which boxes and text it contains. This representation is called -the _Document Object Model_, or ((DOM)) for short. +La estructura de datos que el navegador utiliza para representar el +documento sigue esta figura. Para cada caja, hay un objeto, con el que +podemos interactuar para descubrir cosas como que etiqueta de HTML +representa y que cajas y texto contiene. Esta representación es llamada +_Modelo de Objeto del Documento_ o ((DOM)) _(por sus siglas en inglés +"Document Object Model")_. {{index "documentElement property", "head property", "body property", "html (HTML tag)", "body (HTML tag)", "head (HTML tag)"}} -The global binding `document` gives us access to these objects. Its -`documentElement` property refers to the object representing the -`` tag. Since every HTML document has a head and a body, it also -has `head` and `body` properties, pointing at those elements. +El objeto de enlace global `document` nos da acceso a esos objetos. Su +propiedad `documentElement` hace referencia al objeto que representa +a la etiqueta ``. Dado que cada documento HTML tiene una cabecera +y un cuerpo, también tiene propiedades `head` y `body` que apuntan a +esos elementos. -## Trees +## Árboles {{index [nesting, "of objects"]}} -Think back to the ((syntax tree))s from [Chapter ?](language#parsing) -for a moment. Their structures are strikingly similar to the structure -of a browser's document. Each _((node))_ may refer to other nodes, -_children_, which in turn may have their own children. This shape is -typical of nested structures where elements can contain subelements -that are similar to themselves. +Pensemos en los ((árbol))es ((sintáctico))s del [Capítulo ?](language#parsing) +por un momento. Sus estructuras son sorprendentemente similares a la +estructura de un documento del navegador. Cada _((nodo))_ puede +referirse a otros nodos _hijos_ que, a su vez, pueden tener hijos +propios. Esta forma es típica de las estructuras anidadas donde los +elementos pueden contener sub elementos que son similares a ellos +mismos. {{index "documentElement property", [DOM, tree]}} -We call a data structure a _((tree))_ when it has a branching -structure, has no ((cycle))s (a node may not contain itself, directly -or indirectly), and has a single, well-defined _((root))_. In the case -of the DOM, `document.documentElement` serves as the root. +Le damos el nombre de _((árbol))_ a una estructura de datos cuando +tiene una estructura de ramificación, no tiene ((ciclo))s (un nodo +no puede contenerse a sí mismo, directa o indirectamente) y tiene +una única _((raíz))_ bien definida. En el caso del _DOM_, +`document.documentElement` hace la función de raíz. {{index sorting, ["data structure", "tree"], "syntax tree"}} -Trees come up a lot in computer science. In addition to representing -recursive structures such as HTML documents or programs, they are -often used to maintain sorted ((set))s of data because elements can -usually be found or inserted more efficiently in a tree than in a flat -array. +Los árboles aparecen constantemente en las ciencias de la computación +_(computer sience)_. Además de representar estructuras recursivas como +los documentos HTML o programas, también son comúnmente usados para +mantener ((conjunto))s ordenados de datos debido a que los elementos +generalmente pueden ser encontrados o agregados más eficientemente en +un árbol que en un arreglo plano. {{index "leaf node", "Egg language"}} -A typical tree has different kinds of ((node))s. The syntax tree for -[the Egg language](language) had identifiers, values, and application -nodes. Application nodes may have children, whereas identifiers and -values are _leaves_, or nodes without children. +Un árbol típico tiene diferentes tipos de ((nodo))s. El árbol sintáctico +del [lenguaje _Egg_](language) tenía identificadores, valores y nodos +de aplicación. Los nodos de aplicación pueden tener hijos, mientras +que los identificadores y valores son _hojas_, o nodos sin hijos. {{index "body property", [HTML, structure]}} -The same goes for the DOM. Nodes for _((element))s_, which represent -HTML tags, determine the structure of the document. These can have -((child node))s. An example of such a node is `document.body`. Some of -these children can be ((leaf node))s, such as pieces of ((text)) or -((comment)) nodes. +Lo mismo sucede para el DOM, los nodos para los _((elemento))s_, +los cuales representan etiquetas HTML, determinan la estructura del +documento. Estos pueden tener ((nodos hijos)). Un ejemplo de estos +nodos es `document.body`. Algunos de estos hijos pueden ser +((nodos hoja)), como los fragmentos de ((texto)) o los nodos +((comentario)). {{index "text node", element, "ELEMENT_NODE code", "COMMENT_NODE code", "TEXT_NODE code", "nodeType property"}} -Each DOM node object has a `nodeType` property, which contains a code -(number) that identifies the type of node. Elements have code 1, which -is also defined as the constant property `Node.ELEMENT_NODE`. Text -nodes, representing a section of text in the document, get code 3 -(`Node.TEXT_NODE`). Comments have code 8 +Cada objeto _nodo DOM_ tiene una propiedad `nodeType`, la cual +contiene un código (numérico) que identifica el tipo de nodo. Los +_Elementos_ tienen el código 1, que también es definido por la +propiedad constante `Node.ELEMENT_NODE`. Los nodos de texto, representando +una sección de texto en el documento, obtienen el código 3 +(`Node.TEXT_NODE`). Los comentarios obtienen el código 8 (`Node.COMMENT_NODE`). -Another way to visualize our document ((tree)) is as follows: +Otra forma de visualizar nuestro ((árbol)) de documento es la +siguiente: {{figure {url: "img/html-tree.svg", alt: "HTML document as a tree",width: "8cm"}}} -The leaves are text nodes, and the arrows indicate parent-child -relationships between nodes. +Las hojas son nodos de texto, y las flechas nos indican las relaciones +padre-hijo entre los nodos. {{id standard}} -## The standard +## El estándar {{index "programming language", [interface, design], [DOM, interface]}} -Using cryptic numeric codes to represent node types is not a very -JavaScript-like thing to do. Later in this chapter, we'll see that -other parts of the DOM interface also feel cumbersome and alien. -The reason for this is that the DOM wasn't designed for just -JavaScript. Rather, it tries to be a language-neutral interface -that can be used in other systems as well—not just for HTML but also -for ((XML)), which is a generic ((data format)) with an HTML-like -syntax. +Usar códigos numéricos crípticos para representar a los tipos de +nodos no es algo que se parezca al estilo de JavaScript para hacer +las cosas. Más adelante en este capítulo, veremos cómo otras partes +de la interfaz DOM también se sienten engorrosas y alienígenas. +La razón de esto es que DOM no fue diseñado solamente para JavaScript. +Más bien, intenta ser una interfaz _independiente del lenguaje_ que +puede ser usada en otros sistemas, no solamente para HTML pero +también para ((XML)), que es un ((formato de datos)) genérico con +una sintaxis similar a la de HTML. {{index consistency, integration}} -This is unfortunate. Standards are often useful. But in this case, the -advantage (cross-language consistency) isn't all that compelling. -Having an interface that is properly integrated with the language you -are using will save you more time than having a familiar interface -across languages. +Esto es desafortunado. Usualmente los estándares son bastante útiles. +Pero en este caso, la ventaja (consistencia entre lenguajes) no es tan +conveniente. Tener una interfaz que está propiamente integrada con el +lenguaje que estás utilizando te ahorrará más tiempo que tener una +interfaz familiar en distintos lenguajes. {{index "array-like object", "NodeList type"}} -As an example of this poor integration, consider the `childNodes` -property that element nodes in the DOM have. This property holds an -array-like object, with a `length` property and properties labeled by -numbers to access the child nodes. But it is an instance of the -`NodeList` type, not a real array, so it does not have methods such as -`slice` and `map`. +A manera de ejemplo de esta pobre integración, considera la propiedad +`childNodes` que los nodos elemento en el DOM tienen. Esta propiedad +almacena un objeto parecido a un arreglo, con una propiedad `length` +y propiedades etiquetadas por números para acceder a los nodos hijos. +Pero es una instancia de tipo `NodeList`, no un arreglo real, por +lo que no tiene métodos como `slice` o `map`. {{index [interface, design], [DOM, construction], "side effect"}} -Then there are issues that are simply poor design. For example, there -is no way to create a new node and immediately add children or -((attribute))s to it. Instead, you have to first create it and then add -the children and attributes one by one, using side effects. Code that -interacts heavily with the DOM tends to get long, repetitive, and -ugly. +Luego, hay problemas que son simplemente un pobre diseño. Por ejemplo, +no hay una manera de crear un nuevo nodo e inmediatamente agregar +hijos o ((attributo))s. En vez de eso, tienes que crearlo primero +y luego agregar los hijos y atributos uno por uno, usando efectos +secundarios. El código que interactúa mucho con el DOM tiende a ser +largo, repetitivo y feo. {{index library}} -But these flaws aren't fatal. Since JavaScript allows us to create our -own ((abstraction))s, it is possible to design improved ways to -express the operations you are performing. Many libraries intended for -browser programming come with such tools. +Pero estos defectos no son fatales. Dado que JavaScript nos permite +crear nuestra propias ((abstraccion))es, es posible diseñar formas +mejoradas para expresar las operaciones que estás realizando. Muchas +bibliotecas destinadas a la programación del navegador vienen con +esas herramientas. -## Moving through the tree +## Moviéndose a través del árbol {{index pointer}} -DOM nodes contain a wealth of ((link))s to other nearby nodes. The -following diagram illustrates these: +Los nodos del DOM contienen una amplia cantidad de ((enlace))s a +otros nodos cercanos. El siguiente diagrama los ilustra: {{figure {url: "img/html-links.svg", alt: "Links between DOM nodes",width: "6cm"}}} {{index "child node", "parentNode property", "childNodes property"}} -Although the diagram shows only one link of each type, every node has -a `parentNode` property that points to the node it is part of, if any. -Likewise, every element node (node type 1) has a `childNodes` property -that points to an ((array-like object)) holding its children. +A pesar de que el diagrama muestra un solo enlace por cada tipo, cada +nodo tiene una propiedad `parentNode` que apunta al nodo al que +pertenece, si es que hay alguno. Igualmente, cada nodo _elemento_ +(nodo tipo 1) tiene una propiedad `childNodes` que apunta a un objeto +similar a un arreglo que almacena a sus hijos. {{index "firstChild property", "lastChild property", "previousSibling property", "nextSibling property"}} -In theory, you could move anywhere in the tree using just these parent -and child links. But JavaScript also gives you access to a number of -additional convenience links. The `firstChild` and `lastChild` -properties point to the first and last child elements or have the -value `null` for nodes without children. Similarly, `previousSibling` -and `nextSibling` point to adjacent nodes, which are nodes with the -same parent that appear immediately before or after the node itself. -For a first child, `previousSibling` will be null, and for a last -child, `nextSibling` will be null. +En teoría, te deberías poder mover donde quieras en el árbol +utilizando únicamente estos enlaces entre padre e hijo. Pero JavaScript +también te otorga acceso a un número de enlaces adicionales convenientes. +Las propiedades `firstChild` y `lastChild` apuntan al primer y último +elementos hijo, o tiene el valor `null` para nodos sin hijos. De +manera similar, las propiedades `previousSibling` y `nextSibling` +apuntan a los nodos adyacentes, los cuales, son nodos con el mismo +padre que aparecen inmediatamente antes o después del nodo. Para el +primer hijo `previousSibling` será `null` y para el último hijo, +`nextSibling` será `null`. {{index "children property", "text node", element}} -There's also the `children` property, which is like `childNodes` but -contains only element (type 1) children, not other types of -child nodes. This can be useful when you aren't interested in text -nodes. +También existe una propiedad `children`, que es parecida a `childNodes` +pero contiene únicamente hijos de tipo _elemento_ (tipo 1), excluyendo +otros tipos de nodos. Esto puede ser útil cuando no estás interesando +en nodos de tipo texto. {{index "talksAbout function", recursion, [nesting, "of objects"]}} -When dealing with a nested data structure like this one, recursive -functions are often useful. The following function scans a document -for ((text node))s containing a given string and returns `true` when -it has found one: +Cuando estás tratando con estructuras de datos anidadas como esta, +las funciones recursivas son generalmente útiles. La siguiente función +escanea un documento por ((nodos de texto)) que contengan una cadena +dada y regresan `true` en caso de que encuentren una: {{id talksAbout}} ```{sandbox: "homepage"} -function talksAbout(node, string) { - if (node.nodeType == Node.ELEMENT_NODE) { - for (let i = 0; i < node.childNodes.length; i++) { - if (talksAbout(node.childNodes[i], string)) { +function hablaSobre(nodo, cadena) { + if (nodo.nodeType == Node.ELEMENT_NODE) { + for (let i = 0; i < nodo.childNodes.length; i++) { + if (hablaSobre(nodo.childNodes[i], cadena)) { return true; } } return false; - } else if (node.nodeType == Node.TEXT_NODE) { - return node.nodeValue.indexOf(string) > -1; + } else if (nodo.nodeType == Node.TEXT_NODE) { + return nodo.nodeValue.indexOf(cadena) > -1; } } -console.log(talksAbout(document.body, "book")); +console.log(hablaSobre(document.body, "libro")); // → true ``` {{index "childNodes property", "array-like object", "Array.from function"}} -Because `childNodes` is not a real array, we cannot loop over it with -`for`/`of` and have to run over the index range using a regular `for` -loop or use `Array.from`. +Debido a que `childNodes` no es un arreglo real, no podemos iterar +sobre el usando `for`/`of` por lo que tenemos que iterar sobre el rango +del índice usando un `for` regular o usando `Array.from`. {{index "nodeValue property"}} -The `nodeValue` property of a text node holds the string of text that -it represents. +La propiedad `nodeValue` de un nodo de texto almacena la cadena de +texto que representa. -## Finding elements +## Buscar elementos {{index [DOM, querying], "body property", "hard-coding", [whitespace, "in HTML"]}} -Navigating these ((link))s among parents, children, and siblings is -often useful. But if we want to find a specific node in the document, -reaching it by starting at `document.body` and following a fixed path -of properties is a bad idea. Doing so bakes assumptions into our -program about the precise structure of the document—a structure you -might want to change later. Another complicating factor is that text -nodes are created even for the whitespace between nodes. The -example document's `` tag does not have just three children (`

` -and two `

` elements) but actually has seven: those three, plus the -spaces before, after, and between them. +Navegar por estos ((enlace))s entre padres, hijos y hermanos suele +ser útil. Pero si queremos encontrar un nodo específico en el documento, +alcanzarlo comenzando en `document.body` y siguiendo un camino fijo de +propiedades es una mala idea. Hacerlo genera suposiciones en nuestro +programa sobre la estructura precisa del documento que tal vez quieras +cambiar después. Otro factor complicado es que los nodos de texto son +creados incluso para los espacios en blanco entre nodos. La etiqueta +`` en el documento de ejemplo no tiene solamente tres hijos +(`

` y dos elementos `

`), en realidad tiene siete: esos tres, +más los espacios posteriores y anteriores entre ellos. {{index "search problem", "href attribute", "getElementsByTagName method"}} -So if we want to get the `href` attribute of the link in that -document, we don't want to say something like "Get the second child of -the sixth child of the document body". It'd be better if we could say -"Get the first link in the document". And we can. +Por lo que si queremos obtener el atributo `href` del enlace en ese +documento, no queremos decir algo como "Obten el segundo hijo del +sexto hijo del elemento _body_ del documento". Sería mejor si pudiéramos +decir "Obten el primer enlace en el documento". Y de hecho podemos. ```{sandbox: "homepage"} let link = document.body.getElementsByTagName("a")[0]; @@ -276,99 +293,101 @@ console.log(link.href); {{index "child node"}} -All element nodes have a `getElementsByTagName` method, which collects -all elements with the given tag name that are descendants (direct or -indirect children) of that node and returns them as an ((array-like -object)). +Todos los nodos _elemento_ tienen un método `getElementsByTagName`, el +cual recolecta a todos los elementos con un nombre de etiqueta dado que +son descendientes (hijos directos o indirectos) de ese nodo y los +regresa como un objeto parecido a un arreglo. {{index "id attribute", "getElementById method"}} -To find a specific _single_ node, you can give it an `id` attribute -and use `document.getElementById` instead. +Para encontrar un _único_ nodo en específico, puedes otorgarle un +atributo `id` y usar `document.getElementById`. ```{lang: "text/html"} -

My ostrich Gertrude:

-

+

Mi avestruz Gertrudiz:

+

``` {{index "getElementsByClassName method", "class attribute"}} -A third, similar method is `getElementsByClassName`, which, like -`getElementsByTagName`, searches through the contents of an element -node and retrieves all elements that have the given string in their -`class` attribute. +Un tercer método similar es `getElementsByClassName`, el cual, de +manera similar a `getElementsByTagName` busca a través de los +contenidos de un nodo elemento y obtiene todos los elementos que +tienen una cadena dada en su attributo `class`. -## Changing the document +## Actualizar el documento {{index "side effect", "removeChild method", "appendChild method", "insertBefore method", [DOM, construction], [DOM, modification]}} -Almost everything about the DOM data structure can be changed. The -shape of the document tree can be modified by changing parent-child -relationships. Nodes have a `remove` method to remove them from their -current parent node. To add a child node to an element node, we can -use `appendChild`, which puts it at the end of the list of children, -or `insertBefore`, which inserts the node given as the first argument -before the node given as the second argument. +Prácticamente todo sobre la estructura de datos DOM puede ser cambiado. +La forma del árbol de documento puede ser modificada cambiando las +relaciones padre-hijo. Los nodos tienen un método `remove` para ser +removidos de su nodo padre actual. Para agregar un nodo hijo a un +nodo elemento, podemos usar `appendChild`, que lo pondrá al final de +la lista de hijos, o `insertBefore`, que insertará el nodo en el +primer argumento antes del nodo en el segundo argumento. ```{lang: "text/html"} -

One

-

Two

-

Three

+

Uno

+

Dos

+

Tres

``` -A node can exist in the document in only one place. Thus, inserting -paragraph _Three_ in front of paragraph _One_ will first remove it -from the end of the document and then insert it at the front, -resulting in _Three_/_One_/_Two_. All operations that insert a node -somewhere will, as a ((side effect)), cause it to be removed from its -current position (if it has one). +Un nodo puede existir en el documento solamente en un lugar. En +consecuencia, insertar el párrafo _Tres_ enfrente del párrafo _Uno_ +primero lo removerá del final del documento y luego lo insertará +en la parte delantera, resultando en _Tres_/_Uno_/_Dos_. Todas las operaciones +que insertan un nodo en alguna parte causarán, a modo de +((efecto secundario)), que el nodo sea removido de su posición actual +(si es que tiene una). {{index "insertBefore method", "replaceChild method"}} -The `replaceChild` method is used to replace a child node with another -one. It takes as arguments two nodes: a new node and the node to be -replaced. The replaced node must be a child of the element the method -is called on. Note that both `replaceChild` and `insertBefore` expect -the _new_ node as their first argument. +El método `replaceChild` es usado para reemplazar a un nodo hijo con +otro. Toma dos nodos como argumentos: un nuevo nodo y el nodo que será +reemplazado. El nodo reemplazado debe ser un nodo hijo del elemento +desde donde se está llamando el método. Nótese que tanto `replaceChild` +como `insertBefore` esperan que el _nuevo_ nodo sea el primer argumento. -## Creating nodes +## Crear nodos {{index "alt attribute", "img (HTML tag)"}} -Say we want to write a script that replaces all ((image))s (`` -tags) in the document with the text held in their `alt` attributes, -which specifies an alternative textual representation of the image. +Digamos que queremos escribir un _script_ que reemplace todas las +((imagen))es (etiquetas ``) en el documento con el texto contenido +en sus atributos `alt`, los cuales especifican una representación +textual alternativa de la imagen. {{index "createTextNode method"}} -This involves not only removing the images but adding a new text node -to replace them. Text nodes are created with the -`document.createTextNode` method. +Esto no solamente involucra remover las imágenes, si no que también +involucra agregar un nuevo nodo texto que las reemplace. Los nodos texto +son creados con el método `document.createTextNode`. ```{lang: "text/html"} -

The Cat in the - Hat.

+

El Gato en el + Sombrero.

-

+

``` {{if book -This is what the resulting document looks like: +Así es como se ve el documento resultante: {{figure {url: "img/blockquote.png", alt: "A blockquote with attribution",width: "8cm"}}} if}} -## Attributes +## Atributos {{index "href attribute", [DOM, attributes]}} -Some element ((attribute))s, such as `href` for links, can be accessed -through a property of the same name on the element's ((DOM)) -object. This is the case for most commonly used standard attributes. +Los ((atributo))s de algunos elementos, como `href` para los enlaces, +pueden ser accedidos a través de una propiedad con el mismo nombre en +el objeto ((DOM)) del elemento. Este es el caso para los atributos +estándar más comúnmente utilizados. {{index "data attribute", "getAttribute method", "setAttribute method", attribute}} -But HTML allows you to set any attribute you want on nodes. This can -be useful because it allows you to store extra information in a -document. If you make up your own attribute names, though, such -attributes will not be present as properties on the element's node. -Instead, you have to use the `getAttribute` and `setAttribute` methods -to work with them. +Pero HTML te permite establecer cualquier atributo que quieras en los +nodos. Esto puede ser útil debido a que te permite almacenar información +extra en un documento. Sin embargo, si creas tus propios nombres de +atributo, dichos atributos no estarán presentes como propiedades en el +nodo del elemento. En vez de eso, tendrás que utilizar los métodos +`getAttribute` y `setAttribute` para poder trabajar con ellos. ```{lang: "text/html"} -

The launch code is 00000000.

-

I have two feet.

+

El código de lanzamiento es: 00000000.

+

Yo tengo dos pies.

``` -It is recommended to prefix the names of such made-up attributes with -`data-` to ensure they do not conflict with any other attributes. +Se recomienda anteponer los nombres de dichos atributos inventados con +`data-` para asegurarse de que no conflictúan con ningún otro atributo. {{index "getAttribute method", "setAttribute method", "className property", "class attribute"}} -There is a commonly used attribute, `class`, which is a ((keyword)) in -the JavaScript language. For historical reasons—some old JavaScript -implementations could not handle property names that matched -keywords—the property used to access this attribute is called -`className`. You can also access it under its real name, `"class"`, by -using the `getAttribute` and `setAttribute` methods. +Existe un atributo comúnmente usado, `class`, que es una ((palabra clave)) +en el lenguaje JavaScript. Por motivos históricos, algunas +implementaciones antiguas de JavaScript podrían no manejar nombres de +propiedades que coincidan con las palabras clave, la propiedad utilizada +para acceder a este atributo tiene por nombre `className`. También +puedes acceder a ella bajo su nombre real, `"class"`, utilizando los +métodos `getAttribute` y `setAttribute`. ## Layout {{index layout, "block element", "inline element", "p (HTML tag)", "h1 (HTML tag)", "a (HTML tag)", "strong (HTML tag)"}} -You may have noticed that different types of elements are laid out -differently. Some, such as paragraphs (`

`) or headings (`

`), -take up the whole width of the document and are rendered on separate -lines. These are called _block_ elements. Others, such as links -(``) or the `` element, are rendered on the same line with -their surrounding text. Such elements are called _inline_ elements. +Tal vez hayas notado que diferentes tipos de elementos se exponen +de manera distinta. Algunos, como en el caso de los párrafos +(`

`) o encabezados (`

`), ocupan todo el ancho del documento +y se renderizan en líneas separadas. A estos se les conoce como +elementos _block_ (o bloque). Otros, como los enlaces (``) o el +elemento ``, se renderizan en la misma línea con su texto +circundante. Dichos elementos se les conoce como elementos +_inline_ (o en línea). {{index drawing}} -For any given document, browsers are able to compute a layout, which -gives each element a size and position based on its type and content. -This layout is then used to actually draw the document. +Para cualquier documento dado, los navegadores son capaces de calcular +una estructura _(layout)_, que le da a cada elemento un tamaño y una +posición basada en el tipo y el contenido. Luego, esta estructura se +utiliza para trazar el documento. {{index "border (CSS)", "offsetWidth property", "offsetHeight property", "clientWidth property", "clientHeight property", dimensions}} -The size and position of an element can be accessed from JavaScript. -The `offsetWidth` and `offsetHeight` properties give you the space the -element takes up in _((pixel))s_. A pixel is the basic unit of -measurement in the browser. It traditionally corresponds to the -smallest dot that the screen can draw, but on modern displays, which -can draw _very_ small dots, that may no longer be the case, and a -browser pixel may span multiple display dots. +Se puede acceder al tamaño y la posición de un elemento desde +JavaScript. Las propiedades `offsetWidth` y `offsetHeight` te dan el +espacio que el elemento utiliza en ((pixel))es. Un píxel es la unidad +básica de las medidas del navegador. Tradicionalmente correspondía al +punto más pequeño que la pantalla podía trazar, pero en los monitores +modernos, que pueden trazar puntos _muy_ pequeños, este puede no ser +más el caso, por lo que un píxel del navegador puede abarcar varios +puntos en la pantalla. -Similarly, `clientWidth` and `clientHeight` give you the size of the -space _inside_ the element, ignoring border width. +De manera similar, `clientWidth` y `clientHeight` te dan el tamaño +del espacio _dentro_ del elemento, ignorando la anchura del borde. ```{lang: "text/html"}

- I'm boxed in + Estoy dentro de una caja

``` {{if book -Giving a paragraph a border causes a rectangle to be drawn around it. +Darle un borde a un párrafo hace que se dibuje un rectángulo a su alrededor. {{figure {url: "img/boxed-in.png", alt: "A paragraph with a border",width: "8cm"}}} @@ -548,88 +575,90 @@ if}} {{id boundingRect}} -The most effective way to find the precise position of an element on -the screen is the `getBoundingClientRect` method. It returns an object -with `top`, `bottom`, `left`, and `right` properties, indicating the -pixel positions of the sides of the element relative to the top left -of the screen. If you want them relative to the whole document, you -must add the current scroll position, which you can find in the -`pageXOffset` and `pageYOffset` bindings. +La manera más efectiva de encontrar la posición precisa de un elemento +en la pantalla es el método `getBoundingClientRect`. Este devuelve un +objeto con las propiedades `top`, `bottom`, `left`, y `right`, indicando +las posiciones de pixeles de los lados el elemento en relación con la +parte superior izquierda de la pantalla. Si los quieres en relación a +todo el documento, deberás agregar la posición actual del scroll, la +cual puedes obtener en los _bindings_ `pageXOffset` y `pageYOffset`. {{index "offsetHeight property", "getBoundingClientRect method", drawing, laziness, performance, efficiency}} -Laying out a document can be quite a lot of work. In the interest of -speed, browser engines do not immediately re-layout a document every -time you change it but wait as long as they can. When a JavaScript -program that changed the document finishes running, the browser will -have to compute a new layout to draw the changed document to -the screen. When a program _asks_ for the position or size of -something by reading properties such as `offsetHeight` or calling -`getBoundingClientRect`, providing correct information also requires -computing a ((layout)). +Estructurar un documento puede requerir mucho trabajo. En los +intereses de velocidad, los motores de los navegadores no +reestructuran inmediatamente un documento cada vez que lo cambias, +en cambio, se espera lo más que se pueda. Cuando un programa de +JavaScript que modifica el documento termina de ejecutarse, el +navegador tendrá que calcular una nueva estructura para trazar el +documento actualizado en la pantalla. Cuando un programa _solicita_ +la posición o el tamaño de algo, leyendo propiedades como `offsetHeight` +o llamando a `getBoundingClientRect`, proveer la información correcta +también requiere que se calcule una nueva estructura. {{index "side effect", optimization, benchmark}} -A program that repeatedly alternates between reading DOM layout -information and changing the DOM forces a lot of layout computations -to happen and will consequently run very slowly. The following code is -an example of this. It contains two different programs that build up a -line of _X_ characters 2,000 pixels wide and measures the time each -one takes. +A un programa que alterna repetidamente entre leer la información de la +estructura DOM y cambiar el DOM, fuerza a que haya bastantes cálculos +de estructura, y por consecuencia se ejecutará lentamente. El siguiente +código es un ejemplo de esto. Contiene dos programas diferentes que +construyen una línea de _X_ caracteres con 2,000 pixeles de ancho y +que mide el tiempo que toma cada uno. ```{lang: "text/html", test: nonumbers} -

-

+

+

``` -## Styling +## Estilización {{index "block element", "inline element", style, "strong (HTML tag)", "a (HTML tag)", underline}} -We have seen that different HTML elements are drawn differently. Some -are displayed as blocks, others inline. Some add styling—`` -makes its content ((bold)), and `
` makes it blue and underlines it. +Hemos visto que diferentes elementos HTML se trazan de manera diferente. +Algunos son desplegados como bloques, otros en línea. Algunos agregan +estilos, por ejemplo `` hace que su contenido esté en +((negritas)) y `` lo hace azul y lo subraya. {{index "img (HTML tag)", "default behavior", "style attribute"}} -The way an `` tag shows an image or an `` tag causes a link to -be followed when it is clicked is strongly tied to the element type. -But we can change the styling associated with an element, such -as the text color or underline. Here is an example that uses -the `style` property: +La forma en la que una etiqueta `` muestra una imagen o una +etiqueta `` hace que un enlace sea seguido cuando se hace click en +el, está fuertemente atado al tipo del elemento. Pero podemos cambiar los +estilos asociados a un elemento, tales como el color o si está subrayado. +Este es un ejemplo que utiliza la propiedad `style`: ```{lang: "text/html"} -

Normal link

-

Green link

+

Enlace normal

+

Enlace verde

``` {{if book -The second link will be green instead of the default link color. +El segundo enlace será verde en vez del color por defecto. {{figure {url: "img/colored-links.png", alt: "A normal and a green link",width: "2.2cm"}}} @@ -637,31 +666,32 @@ if}} {{index "border (CSS)", "color (CSS)", CSS, "colon character"}} -A style attribute may contain one or more _((declaration))s_, which -are a property (such as `color`) followed by a colon and a value (such -as `green`). When there is more than one declaration, they must be -separated by ((semicolon))s, as in `"color: red; border: none"`. +Un atributo _style_ puede llegar a contener una o más declaraciones, que +consisten en una propiedad (como `color`) seguido del símbolo de dos puntos +y un valor (como `green`). Cuando hay más de una declaración, estas deben +ser separadas por ((punto y coma)), como en `"color: red; border: none"`. {{index "display (CSS)", layout}} -A lot of aspects of the document can be influenced by -styling. For example, the `display` property controls whether an -element is displayed as a block or an inline element. +Muchos de los aspectos del documento pueden ser influenciados por la +estilización. Por ejemplo, la propiedad `display` controla si un +elemento es desplegado como un bloque o como un elemento en línea. ```{lang: "text/html"} -This text is displayed inline, -as a block, and -not at all. +El texto es desplegado en línea, +como bloque, y +no se despliega. ``` {{index "hidden element"}} -The `block` tag will end up on its own line since ((block element))s -are not displayed inline with the text around them. The last tag is -not displayed at all—`display: none` prevents an element from showing -up on the screen. This is a way to hide elements. It is often -preferable to removing them from the document entirely because it -makes it easy to reveal them again later. +La etiqueta `block` terminará en su propia línea dado que los elementos +_((bloque))_ no son desplegados en línea con el texto que los rodea. +La última etiqueta no se despliega—`display: none` previene que el +elemento sea mostrado en la pantalla. Esta es una manera de ocultar +elementos. A menudo esto es preferido sobre removerlos completamente +del documento debido a que hace más fácil mostrarlos nuevamente en el +futuro. {{if book @@ -671,42 +701,46 @@ if}} {{index "color (CSS)", "style attribute"}} -JavaScript code can directly manipulate the style of an element -through the element's `style` property. This property holds an object -that has properties for all possible style properties. The values of -these properties are strings, which we can write to in order to change -a particular aspect of the element's style. +El código de JavaScript puede manipular directamente el estilo de un +elemento a través de la propiedad `style` del elemento. Esta propiedad +almacena un objeto que tiene propiedades por todas las posibles +propiedades de estilo. Los valores de esas propiedades son cadenas, que +podemos escribir para cambiar un aspecto en particular del estilo del +elemento. ```{lang: "text/html"} -

- Nice text +

+ Texto mejorado

``` {{index "camel case", capitalization, "hyphen character", "font-family (CSS)"}} -Some style property names contain hyphens, such as `font-family`. -Because such property names are awkward to work with in JavaScript -(you'd have to say `style["font-family"]`), the property names in the -`style` object for such properties have their hyphens removed and the -letters after them capitalized (`style.fontFamily`). +Algunos nombres de propiedades pueden contener guiones, como es el caso +de `font-family`. Dado que estos nombres de propiedades son incómodos +para trabajar con ellos en JavaScript (tendrías que decir +`style["font-family"]`), los nombres de propiedades en el objeto `style` +para tales propiedades no tendrán guiones y las letra después del guion +estará en mayúsculas (`style.fontFamily`). -## Cascading styles +## Estilos en Cascada {{index "rule (CSS)", "style (HTML tag)"}} {{indexsee "Cascading Style Sheets", CSS}} {{indexsee "style sheet", CSS}} -The styling system for HTML is called ((CSS)), for _Cascading Style -Sheets_. A _style sheet_ is a set of rules for how to style -elements in a document. It can be given inside a ` -

Now strong text is italic and gray.

+

Ahora el texto en negritas esta en italicas y es gris.

``` {{index "rule (CSS)", "font-weight (CSS)", overlay}} -The _((cascading))_ in the name refers to the fact that multiple such -rules are combined to produce the final style for an element. In the -example, the default styling for `` tags, which gives them -`font-weight: bold`, is overlaid by the rule in the ` ``` -### Mouse motion +### Movimiento del mouse {{index "mousemove event"}} -Every time the mouse pointer moves, a `"mousemove"` event is fired. -This event can be used to track the position of the mouse. A common -situation in which this is useful is when implementing some form of -mouse-((dragging)) functionality. +Cada vez que el puntero del mouse se mueve, un evento `"mousemove"` es lanzado. +Este evento puede ser utilizado para rastrear la posición del mouse. Una +situación común en la cual es útil es cuando se implementa una funcionalidad de +mouse-((arrastrar)). {{index "draggable bar example"}} -As an example, the following program displays a bar and sets up event -handlers so that dragging to the left or right on this bar makes it -narrower or wider: +Como un ejemplo, el siguiente programa muestra una barra y configura los +manejadores de eventos para que cuando se arrastre hacia la izquierda o a la +derecha en esta barra la haga más estrecha o más ancha: ```{lang: "text/html", startCode: true} -

Drag the bar to change its width:

+

Dibuja la barra para cambiar su anchura:

@@ -489,201 +489,200 @@ narrower or wider: {{if book -The resulting page looks like this: +La página resultante se ve así: -{{figure {url: "img/drag-bar.png", alt: "A draggable bar",width: "5.3cm"}}} +{{figure {url: "img/drag-bar.png", alt: "Una barra arrastrable", width: +"5.3cm"}}} if}} {{index "mouseup event", "mousemove event"}} -Note that the `"mousemove"` handler is registered on the whole -((window)). Even if the mouse goes outside of the bar during resizing, -as long as the button is held we still want to update its size. +Tener en cuenta que el manejador `"mousemove"` es registrado sobre toda la +((ventana)). Incluso si el mouse se sale de la barra durante el cambio de +tamaño, siempre que se mantenga presionado el botón, su tamaño se actualizará. {{index "buttons property", "button property", "bitfield"}} -We must stop resizing the bar when the mouse button is released. For -that, we can use the `buttons` property (note the plural), which tells -us about the buttons that are currently held down. When this is zero, -no buttons are down. When buttons are held, its value is the sum of -the codes for those buttons—the left button has code 1, the right -button 2, and the middle one 4. That way, you can check whether a given -button is pressed by taking the remainder of the value of `buttons` -and its code. +Se debe dejar de cambiar el tamaño de la barra cuando se suelta el botón del +mouse. Para eso, podemos usar la propiedad `buttons` (nótese el plural), que +informa sobre los botones que se mantienen presionados actualmente. Cuando este +es cero, no hay botones presionados. Cuando los botones se mantienen +presionados, su valor es la suma de los códigos de esos botones—el botón +izquierdo tiene el código 1, el botón derecho 2 y el central 4. De esta manera, +puedes verificar si un botón dado es presionado tomando el resto del valor de +`buttons` y su código. -Note that the order of these codes is different from the one used by -`button`, where the middle button came before the right one. As -mentioned, consistency isn't really a strong point of the browser's -programming interface. +Tener en cuenta que el orden de los códigos es diferente del utilizado por +`button`, donde el botón central se encuentra que el derecho. Como se mencionó, +la coherencia no es realmente un punto fuerte de la interfaz de programación del +navegador. -### Touch events +### Eventos de toques {{index touch, "mousedown event", "mouseup event", "click event"}} -The style of graphical browser that we use was designed with mouse -interfaces in mind, at a time where touchscreens were rare. To -make the Web "work" on early touchscreen phones, browsers for those -devices pretended, to a certain extent, that touch events were mouse -events. If you tap your screen, you'll get `"mousedown"`, `"mouseup"`, -and `"click"` events. +El estilo del navegador gráfico que usamos fue diseñado con la interfaz de mouse +en mente, en un tiempo en el cual las pantallas táctiles eran raras. Para hacer +que la Web "funcionara" en los primeros teléfonos con pantalla táctil, los +navegadores de esos dispositivos pretendían, hasta cierto punto, que los eventos +táctiles fueran eventos del mouse. Si se toca la pantalla, se obtendrán los +eventos `"mousedown"`, `"mouseup"` y `"click"`. -But this illusion isn't very robust. A touchscreen works differently -from a mouse: it doesn't have multiple buttons, you can't track the -finger when it isn't on the screen (to simulate `"mousemove"`), and it -allows multiple fingers to be on the screen at the same time. +Pero esta ilusión no es muy robusta. Una pantalla táctil funciona de manera +diferente a un mouse: no tiene multiples botones, no puedes rastrear el dedo +cuando no está en la pantalla (para simular `"mousemove"`) y permite que +multiples dedos estén en la pantalla al mismo tiempo. -Mouse events cover touch interaction only in straightforward cases—if -you add a `"click"` handler to a button, touch users will still be -able to use it. But something like the resizeable bar in the previous -example does not work on a touchscreen. +Los eventos del mouse cubren las interacciones solamente en casos sencillos—si +se agrega un manejador `"click"` a un butón, los usuarios táctiles aún podrán +usarlo. Pero algo como la barra redimensionable del ejemplo anterior no funciona +en una pantalla táctil. {{index "touchstart event", "touchmove event", "touchend event"}} -There are specific event types fired by touch interaction. When a -finger starts touching the screen, you get a `"touchstart"` event. -When it is moved while touching, `"touchmove"` events fire. -Finally, when it stops touching the screen, you'll see a `"touchend"` -event. +Hay tipos de eventos específicos lanzados por la interacción táctil. Cuando un +dedo inicia tocando la pantalla, se obtiene el evento `"touchstart"`. Cuando +este es movido mientras se toca, se lanza el evento `"touchmove"`. Finalmente, +cuando se deja de tocar la pantalla, se lanzará un evento `"touchend"`. {{index "touches property", "clientX property", "clientY property", "pageX property", "pageY property"}} -Because many touchscreens can detect multiple fingers at the same -time, these events don't have a single set of coordinates associated -with them. Rather, their ((event object))s have a `touches` property, -which holds an ((array-like object)) of points, each of which has its -own `clientX`, `clientY`, `pageX`, and `pageY` properties. +Debido a que muchas pantallas táctiles pueden detectar multiples dedos al mismo +tiempo, estos eventos no tienen un solo conjunto de coordenadas asociados a +ellos. Más bien, sus ((objetos de evento)) tienen una propiedad `touches`, el +cual contiene un ((objeto tipo matriz)) de puntos, cada uno de ellos tiene sus +propias propiedades `clientX`, `clientY`, `pageX` y `pageY`. -You could do something like this to show red circles around every -touching finger: +Se puede hacer algo como esto para mostrar circulos rojos alrededor de cada que +toca: ```{lang: "text/html"} -

Touch this page

+

Toca esta página

``` {{index "preventDefault method"}} -You'll often want to call `preventDefault` in touch event handlers to -override the browser's default behavior (which may include scrolling -the page on swiping) and to prevent the mouse events from being fired, -for which you may _also_ have a handler. +A menudo se querrá llamar a `preventDefault` en un manejador de eventos táctiles +para sobreescribir el comportamiento por defecto del navegador (que puede +incluir desplazarse por la paǵina al deslizar el dedo) y para evitar que los +eventos del mouse se lancen, para lo cual se puede tener _también_ un manejador. -## Scroll events +## Eventos de desplazamiento {{index scrolling, "scroll event", "event handling"}} -Whenever an element is scrolled, a `"scroll"` event is fired on it. -This has various uses, such as knowing what the user is currently -looking at (for disabling off-screen ((animation))s or sending ((spy)) -reports to your evil headquarters) or showing some indication of -progress (by highlighting part of a table of contents or showing a -page number). +Siempre que un elemento es desplazado, un evento `"scroll"` es lanzado. Esto +tiene varios usos, como son saber qué está mirando el usuario actualmente (para +deshabilitar la ((animación)) fuera de pantalla o enviar informes ((espías)) a +su sede maligna) o mostrar alguna indicación de progreso (resaltando parte de +una tabla de contenido o mostrando un número de página). -The following example draws a ((progress bar)) above the document and -updates it to fill up as you scroll down: +El siguiente ejemplo dibuja una ((barra de progreso)) sobre el documento y lo +actualiza para que se llene a medida que se desplza hacia abajo: ```{lang: "text/html"} -
+
``` {{index "unit (CSS)", scrolling, "position (CSS)", "fixed positioning", "absolute positioning", percentage, "repeat method"}} -Giving an element a `position` of `fixed` acts much like an `absolute` -position but also prevents it from scrolling along with the rest of -the document. The effect is to make our progress bar stay at the top. -Its width is changed to indicate the current progress. We use `%`, -rather than `px`, as a unit when setting the width so that the element -is sized relative to the page width. +Darle a un elemento una `position` de `fixed` actua como una posición `absolute` +pero también evita que se desplace junto con el resto del documento. El efecto +es hacer que la barra de progreso permanezca en la parte superior. Su ancho +cambia para indicar el progreso actual. Se usa `%`, en lugar de `px`, como una +unidad cuando se configura el ancho para que el tamaño del elemento sea relativo +al ancho de la página. {{index "innerHeight property", "innerWidth property", "pageYOffset property"}} -The global `innerHeight` binding gives us the height of the window, -which we have to subtract from the total scrollable height—you can't -keep scrolling when you hit the bottom of the document. There's also -an `innerWidth` for the window width. By dividing `pageYOffset`, the -current scroll position, by the maximum scroll position and -multiplying by 100, we get the percentage for the progress bar. +La vincualación global `innerHeight` proporciona la altura de la ventana, que se +tiene que restar de la altura total desplazable—no se puede seguir desplazando +cuando se llega al final del documento. También hay un `innerWidth` para el +ancho de la ventana. Al dividir `pageYOffset`, la posición de desplazamiento +actual, por la posición de desplazamiento máximo y mulplicado por 100, se +obtiene el porcentaje de la barra de progreso. {{index "preventDefault method"}} -Calling `preventDefault` on a scroll event does not prevent the -scrolling from happening. In fact, the event handler is called only -_after_ the scrolling takes place. +Al llamar `preventDefault` en un evento de desplazamiento no evita que el +desplazamiento se produzca. De hecho, el manejador de eventos es llamado +unicamente _después_ de que se realiza el desplazamiento. -## Focus events +## Eventos de foco {{index "event handling", "focus event", "blur event"}} -When an element gains ((focus)), the browser fires a `"focus"` event -on it. When it loses focus, the element gets a `"blur"` event. +Cuando un elemento gana el ((foco)), el navegador lanza un evento de `"focus"` +en él. Cuando este pierde el foco, el elemento obtiene un evento `"blur"`. {{index "event propagation"}} -Unlike the events discussed earlier, these two events do not -propagate. A handler on a parent element is not notified when a child -element gains or loses focus. +A diferencia de los eventos discutidos con anterioridad, estos dos eventos no se +propagan. Un manejador en un elemento padre no es notificado cuando un elemento +hijo gana o pierde el foco. {{index "input (HTML tag)", "help text example"}} -The following example displays help text for the ((text field)) that -currently has focus: +El siguiente ejemplo muestra un texto de ayuda para ((campo de texto)) que +actualmente tiene el foco: ```{lang: "text/html"} -

Name:

-

Age:

-

+

Nombre:

+

Edad:

+

@@ -691,278 +690,281 @@ currently has focus: {{if book -This screenshot shows the help text for the age field. +Esta captura de pantalla muestra el texto de ayuda para el campo edad. -{{figure {url: "img/help-field.png", alt: "Providing help when a field is focused",width: "4.4cm"}}} +{{figure {url: "img/help-field.png", alt: "Brindar ayuda cuando un campo está +enfocado", width: "4.4cm"}}} if}} {{index "focus event", "blur event"}} -The ((window)) object will receive `"focus"` and `"blur"` events when -the user moves from or to the browser tab or window in which the -document is shown. +El objeto ((window)) recibirá eventos de `"focus"` y `"blur"` cuando el usuario +se mueva desde o hacia la pestaña o ventana del navegador en la que se muestra +el documento. -## Load event +## Evento de carga {{index "script (HTML tag)", "load event"}} -When a page finishes loading, the `"load"` event fires on the window -and the document body objects. This is often used to schedule -((initialization)) actions that require the whole ((document)) to have -been built. Remember that the content of ` ``` {{index "sloppy programming"}} -Giving an undefined value to `clearTimeout` or calling it on a timeout -that has already fired has no effect. Thus, we don't have to be -careful about when to call it, and we simply do so for every event. +Dar un valor indefinido a `clearTimeout` o llamándolo en un tiempo de espera que +ya ha se ha lanzado no tiene ningún efecto. Por lo tanto, no debemos tener +cuidado sobre cuándo llamarlo, y simplemente se hace para cada evento. {{index "mousemove event"}} -We can use a slightly different pattern if we want to space responses -so that they're separated by at least a certain length of ((time)) but -want to fire them _during_ a series of events, not just afterward. For -example, we might want to respond to `"mousemove"` events by showing -the current coordinates of the mouse but only every 250 milliseconds. +Podemos utilizar un patrón ligeramente diferente si se quiere espaciar las +respuestas de modo que estén separadas por al menos una cierta longitud de +((tiempo)) pero se quiere lanzar _durante_ una serie de eventos, no solo +después. Por ejemplo, se podría querer responder a los eventos `"mousemove"` +mostrando las coordenadas actuales del mourse pero solo cada 250 milisegundos. ```{lang: "text/html"} ``` -## Summary +## Resumen -Event handlers make it possible to detect and react to events -happening in our web page. The `addEventListener` method is used to -register such a handler. +Los manejadores de eventos hacen posible detectar y reaccionar a eventos que +suceden en nuestra página web. El método `addEventListener` es usado para +registrar un manejador de eventos. -Each event has a type (`"keydown"`, `"focus"`, and so on) that -identifies it. Most events are called on a specific DOM element and -then _propagate_ to that element's ancestors, allowing handlers -associated with those elements to handle them. +Cada evento tiene un tipo (`"keydown"`, `"focus"`, etc.) que lo identifica. La +mayoría de eventos son llamados en un elemento DOM específico y luego se +_propagan_ a los ancentros de ese elemento, lo que permite que los manejadores +asociados con esos elementos los manejen. -When an event handler is called, it is passed an event object with -additional information about the event. This object also has methods -that allow us to stop further propagation (`stopPropagation`) and -prevent the browser's default handling of the event -(`preventDefault`). +Cuando un manejador de evento es llamado, se le pasa un objeto evento con +información adicional acerca del evento. Este objeto también tiene métodos que +permiten detener una mayor propagación (`stopPropagation`) y evitar que el +navegador maneje el evento por defecto (`preventDefault`). -Pressing a key fires `"keydown"` and `"keyup"` events. Pressing a -mouse button fires `"mousedown"`, `"mouseup"`, and `"click"` events. -Moving the mouse fires `"mousemove"` events. Touchscreen interaction -will result in `"touchstart"`, `"touchmove"`, and `"touchend"` events. +Al presiosar una tecla se lanza los eventos `"keydown"` y `"keyup"`. Al +presionar un botón del mouse se lanzan los eventos `"mousedown"`, `"mouseup"` y +`"click"`. Al mover el mouse se lanzan los eventos `"mousemove"`. Las +interacción de la pantalla táctil darán como resultado eventos `"touchstart"`, +`"touchmove"` y `"touchend"`. -Scrolling can be detected with the `"scroll"` event, and focus changes -can be detected with the `"focus"` and `"blur"` events. When the -document finishes loading, a `"load"` event fires on the window. +El desplazamiento puede ser detectado con el evento `"scroll"` y los cambios de +foco pueden ser detactados con los eventos `"focus"` y `"blur"`. Cuando el +documento termina de cargarse, se lanza el evento `"load"` en la ventana. -## Exercises +## Ejercicios -### Balloon +### Globo {{index "balloon (exercise)", "arrow key"}} -Write a page that displays a ((balloon)) (using the balloon ((emoji)), -🎈). When you press the up arrow, it should inflate (grow) 10 percent, -and when you press the down arrow, it should deflate (shrink) 10 percent. +Escribe una página que muestre un ((globo)) (usando el globo ((emoji)), 🎈). +Cuando se presione la flecha hacia arriba, debe inflarse (crecer) un 10 por +cierto, y cuando se presiona la flecha hacia abajo, debe desinflarse +(contraerse) un 10 por cierto) {{index "font-size (CSS)"}} -You can control the size of text (emoji are text) by setting the -`font-size` CSS property (`style.fontSize`) on its parent element. -Remember to include a unit in the value—for example, pixels (`10px`). +Puedes controlar el tamaño del texto (los emojis son texto) configurando la +propiedad CSS `font-size` (`style.fontSize`) en su elemento padre. Recuerda +incluir una unidad en el valor, por ejemplo pixeles (`10px`). -The key names of the arrow keys are `"ArrowUp"` and `"ArrowDown"`. -Make sure the keys change only the balloon, without scrolling the -page. +Los nombres de las teclas de flecha son `"ArrowUp"` y `"ArrowDown"`. Asegúratede +que las teclas cambien solo al globo, sin desplazar la página. -When that works, add a feature where, if you blow up the balloon past -a certain size, it explodes. In this case, exploding means that it is -replaced with an 💥 emoji, and the event handler is removed (so that -you can't inflate or deflate the explosion). +Cuando eso funcione, agrega una nueva función en la que, si infla el globo más +allá de cierto tamaño, explote. En este caso, explotar significa que se +reemplaza con un emoji 💥, y se elimina el manejador de eventos (para que no se +pueda inflar o desinflar la explosión). {{if interactive @@ -980,21 +982,20 @@ if}} {{index "keydown event", "key property", "balloon (exercise)"}} -You'll want to register a handler for the `"keydown"` event and look -at `event.key` to figure out whether the up or down arrow key was -pressed. +Querrás registrar un manejador para el evento `"keydown"` y mirar `event.key` +para averiguar si se presionó la teclas de flecha hacia arra o hacia abajo. -The current size can be kept in a binding so that you can base the -new size on it. It'll be helpful to define a function that updates the -size—both the binding and the style of the balloon in the DOM—so that -you can call it from your event handler, and possibly also once when -starting, to set the initial size. +El tamaño actual se puede mantener en una vinculación para que puedas basar el +nuevo tamaño en él. Será útil definir una función que actualice el tamaño, tanto +el enlace como el estilo del globo en el DOM, para que pueda llamar desde su +manejador de eventos, y posiblemente también una vez al comenzar, para +establecer el tamaño inicial. {{index "replaceChild method", "textContent property"}} -You can change the balloon to an explosion by replacing the text node -with another one (using `replaceChild`) or by setting the -`textContent` property of its parent node to a new string. +Puedes cambiar el globo a una explosión reemplazando el texto del nodo con otro +(usando `replaceChild`) o estableciendo la propiedad `textContent` de su no +padre a una nueva cadena. hint}} @@ -1002,29 +1003,27 @@ hint}} {{index animation, "mouse trail (exercise)"}} -In JavaScript's early days, which was the high time of ((gaudy home -pages)) with lots of animated images, people came up with some truly -inspiring ways to use the language. +En los primeros días de JavaScript, que era el momento de ((páginas de inicio +llamativas)) con muchas imágenes, a la gente se le ocurrieron formas realmente +inspiradoras de usar el lenguaje. -One of these was the _mouse trail_—a series of elements that would -follow the mouse pointer as you moved it across the page. +Uno de estos fue el _rastro del mouse_, una serie de elementos que seguirían el +puntero del mouse mientras lo movías por la página. {{index "absolute positioning", "background (CSS)"}} -In this exercise, I want you to implement a mouse trail. Use -absolutely positioned `
` elements with a fixed size and -background color (refer to the [code](event#mouse_drawing) in the -"Mouse Clicks" section for an example). Create a bunch of such -elements and, when the mouse moves, display them in the wake of the -mouse pointer. +En este ejercicio, quiero que implementes un rastro del mouse. Utiliza elementos +`
` con un tamaño fijo y un color de fondo (consulta a +[code](event#mouse_drawing) en la sección "Clics del mouse" por un ejemplo). +Crea un montón de estos elementos y, cuando el mouse se mueva, muestralos +después del puntero del mouse. {{index "mousemove event"}} -There are various possible approaches here. You can make your solution -as simple or as complex as you want. A simple solution to start with -is to keep a fixed number of trail elements and cycle through them, -moving the next one to the mouse's current position every time a -`"mousemove"` event occurs. +Hay varios enfoques posibles aquí. Puedes hacer tu solución tan simple o tan +compleja como desees. Una solución simple para comenzar es mantener un número de +elementos de seguimiento fijos y recorrerlos, moviendo el sigueinte a la +posición actual del mouse cada vez que ocurra un evento `"mousemove"`. {{if interactive @@ -1052,63 +1051,64 @@ if}} {{index "mouse trail (exercise)"}} -Creating the elements is best done with a loop. Append them to the -document to make them show up. To be able to access them later to change their position, you'll want to store the elements in -an array. +La creación de los elementos se realiza de mjor manera con un ciclo. Añadelos al +documento para que aparezcan. Para poder acceder a ellos más tarde para cambiar +su posición, querrás alamacenar los elementos en una matriz. {{index "mousemove event", [array, indexing], "remainder operator", "% operator"}} -Cycling through them can be done by keeping a ((counter variable)) and -adding 1 to it every time the `"mousemove"` event fires. The remainder -operator (`% elements.length`) can then be used to get a valid array -index to pick the element you want to position during a given event. +Se puede hacer un ciclo a través de ellos manteniendo una ((variable contador)) +y agregando 1 cada vez que se activa el evento `"mousemove"`. El operador +restante (`% elements.length`) pueden ser usado para obtener un índice de matriz +válido para elegir el elemento que se desaea colocar durante un evento +determinado. {{index simulation, "requestAnimationFrame function"}} -Another interesting effect can be achieved by modeling a simple -((physics)) system. Use the `"mousemove"` event only to update a pair -of bindings that track the mouse position. Then use -`requestAnimationFrame` to simulate the trailing elements being -attracted to the position of the mouse pointer. At every animation -step, update their position based on their position relative to the -pointer (and, optionally, a speed that is stored for each element). -Figuring out a good way to do this is up to you. +Otro efecto interesante se puede lograr modelando un sistema simple ((físico)). +Utiliza el evento `"mousemove"` para actualizar un par de enlaces que rastrean +la posición del mouse. Luego usa `requestAnimationFrame` para simular que los +elementos finales son atraidos a la posición a la posición del puntero del +mouse. En cada paso de la animación, actualiza la posición en función de su +posición relativa al puntero (y, opcionalmente, una velocidad que se alamacena +para cada elemento). Averiguar una buena forma de hacerlo depende de ti. hint}} -### Tabs +### Pestañas {{index "tabbed interface (exercise)"}} -Tabbed panels are widely used in user interfaces. They allow you to -select an interface panel by choosing from a number of tabs "sticking -out" above an element. +Los paneles con pestañas son utilizados ampliamente en las interfaces de +usuario. Te permiten seleccionar un panel de interfaz eligiendo entre una serie +de pestañas "que sobresalen" sobre un elemento. {{index "button (HTML tag)", "display (CSS)", "hidden element", "data attribute"}} -In this exercise you must implement a simple tabbed interface. Write a -function, `asTabs`, that takes a DOM node and creates a tabbed -interface showing the child elements of that node. It should insert a -list of ` +

+ +
+ +
+
+ +
+ To run this chapter's code locally, use these files: +
    +
    + +
    + These files contain this chapter’s project code: +
      +
      + +

      If you've solved the exercise and want to compare your code with + mine, or you really tried, but can't get your code to work, + you can (or download it).

      + +

      + The base environment for this chapter (if any) is available in the + sandbox above, allowing you to run the chapter's examples by + simply pasting them into the editor. +

      + diff --git a/docs/code/intro.js b/docs/code/intro.js new file mode 100644 index 000000000..5b20af43a --- /dev/null +++ b/docs/code/intro.js @@ -0,0 +1,28 @@ +function range(start, end, step) { + if (step == null) step = 1; + var array = []; + + if (step > 0) { + for (var i = start; i <= end; i += step) + array.push(i); + } else { + for (var i = start; i >= end; i += step) + array.push(i); + } + return array; +} + +function sum(array) { + var total = 0; + for (var i = 0; i < array.length; i++) + total += array[i]; + return total; +} + +function factorial(n) { + if (n == 0) { + return 1; + } else { + return factorial(n - 1) * n; + } +} diff --git a/docs/code/journal.js b/docs/code/journal.js new file mode 100644 index 000000000..ee98d227f --- /dev/null +++ b/docs/code/journal.js @@ -0,0 +1,99 @@ +var JOURNAL = [ + {"events":["carrot","exercise","weekend"],"squirrel":false}, + {"events":["bread","pudding","brushed teeth","weekend","touched tree"],"squirrel":false}, + {"events":["carrot","nachos","brushed teeth","cycling","weekend"],"squirrel":false}, + {"events":["brussel sprouts","ice cream","brushed teeth","computer","weekend"],"squirrel":false}, + {"events":["potatoes","candy","brushed teeth","exercise","weekend","dentist"],"squirrel":false}, + {"events":["brussel sprouts","pudding","brushed teeth","running","weekend"],"squirrel":false}, + {"events":["pizza","brushed teeth","computer","work","touched tree"],"squirrel":false}, + {"events":["bread","beer","brushed teeth","cycling","work"],"squirrel":false}, + {"events":["cauliflower","brushed teeth","work"],"squirrel":false}, + {"events":["pizza","brushed teeth","cycling","work"],"squirrel":false}, + {"events":["lasagna","nachos","brushed teeth","work"],"squirrel":false}, + {"events":["brushed teeth","weekend","touched tree"],"squirrel":false}, + {"events":["lettuce","brushed teeth","television","weekend"],"squirrel":false}, + {"events":["spaghetti","brushed teeth","work"],"squirrel":false}, + {"events":["brushed teeth","computer","work"],"squirrel":false}, + {"events":["lettuce","nachos","brushed teeth","work"],"squirrel":false}, + {"events":["carrot","brushed teeth","running","work"],"squirrel":false}, + {"events":["brushed teeth","work"],"squirrel":false}, + {"events":["cauliflower","reading","weekend"],"squirrel":false}, + {"events":["bread","brushed teeth","weekend"],"squirrel":false}, + {"events":["lasagna","brushed teeth","exercise","work"],"squirrel":false}, + {"events":["spaghetti","brushed teeth","reading","work"],"squirrel":false}, + {"events":["carrot","ice cream","brushed teeth","television","work"],"squirrel":false}, + {"events":["spaghetti","nachos","work"],"squirrel":false}, + {"events":["cauliflower","ice cream","brushed teeth","cycling","work"],"squirrel":false}, + {"events":["spaghetti","peanuts","computer","weekend"],"squirrel":true}, + {"events":["potatoes","ice cream","brushed teeth","computer","weekend"],"squirrel":false}, + {"events":["potatoes","ice cream","brushed teeth","work"],"squirrel":false}, + {"events":["peanuts","brushed teeth","running","work"],"squirrel":false}, + {"events":["potatoes","exercise","work"],"squirrel":false}, + {"events":["pizza","ice cream","computer","work"],"squirrel":false}, + {"events":["lasagna","ice cream","work"],"squirrel":false}, + {"events":["cauliflower","candy","reading","weekend"],"squirrel":false}, + {"events":["lasagna","nachos","brushed teeth","running","weekend"],"squirrel":false}, + {"events":["potatoes","brushed teeth","work"],"squirrel":false}, + {"events":["carrot","work"],"squirrel":false}, + {"events":["pizza","beer","work","dentist"],"squirrel":false}, + {"events":["lasagna","pudding","cycling","work"],"squirrel":false}, + {"events":["spaghetti","brushed teeth","reading","work"],"squirrel":false}, + {"events":["spaghetti","pudding","television","weekend"],"squirrel":false}, + {"events":["bread","brushed teeth","exercise","weekend"],"squirrel":false}, + {"events":["lasagna","peanuts","work"],"squirrel":true}, + {"events":["pizza","work"],"squirrel":false}, + {"events":["potatoes","exercise","work"],"squirrel":false}, + {"events":["brushed teeth","exercise","work"],"squirrel":false}, + {"events":["spaghetti","brushed teeth","television","work"],"squirrel":false}, + {"events":["pizza","cycling","weekend"],"squirrel":false}, + {"events":["carrot","brushed teeth","weekend"],"squirrel":false}, + {"events":["carrot","beer","brushed teeth","work"],"squirrel":false}, + {"events":["pizza","peanuts","candy","work"],"squirrel":true}, + {"events":["carrot","peanuts","brushed teeth","reading","work"],"squirrel":false}, + {"events":["potatoes","peanuts","brushed teeth","work"],"squirrel":false}, + {"events":["carrot","nachos","brushed teeth","exercise","work"],"squirrel":false}, + {"events":["pizza","peanuts","brushed teeth","television","weekend"],"squirrel":false}, + {"events":["lasagna","brushed teeth","cycling","weekend"],"squirrel":false}, + {"events":["cauliflower","peanuts","brushed teeth","computer","work","touched tree"],"squirrel":false}, + {"events":["lettuce","brushed teeth","television","work"],"squirrel":false}, + {"events":["potatoes","brushed teeth","computer","work"],"squirrel":false}, + {"events":["bread","candy","work"],"squirrel":false}, + {"events":["potatoes","nachos","work"],"squirrel":false}, + {"events":["carrot","pudding","brushed teeth","weekend"],"squirrel":false}, + {"events":["carrot","brushed teeth","exercise","weekend","touched tree"],"squirrel":false}, + {"events":["brussel sprouts","running","work"],"squirrel":false}, + {"events":["brushed teeth","work"],"squirrel":false}, + {"events":["lettuce","brushed teeth","running","work"],"squirrel":false}, + {"events":["candy","brushed teeth","work"],"squirrel":false}, + {"events":["brussel sprouts","brushed teeth","computer","work"],"squirrel":false}, + {"events":["bread","brushed teeth","weekend"],"squirrel":false}, + {"events":["cauliflower","brushed teeth","weekend"],"squirrel":false}, + {"events":["spaghetti","candy","television","work","touched tree"],"squirrel":false}, + {"events":["carrot","pudding","brushed teeth","work"],"squirrel":false}, + {"events":["lettuce","brushed teeth","work"],"squirrel":false}, + {"events":["carrot","ice cream","brushed teeth","cycling","work"],"squirrel":false}, + {"events":["pizza","brushed teeth","work"],"squirrel":false}, + {"events":["spaghetti","peanuts","exercise","weekend"],"squirrel":true}, + {"events":["bread","beer","computer","weekend","touched tree"],"squirrel":false}, + {"events":["brushed teeth","running","work"],"squirrel":false}, + {"events":["lettuce","peanuts","brushed teeth","work","touched tree"],"squirrel":false}, + {"events":["lasagna","brushed teeth","television","work"],"squirrel":false}, + {"events":["cauliflower","brushed teeth","running","work"],"squirrel":false}, + {"events":["carrot","brushed teeth","running","work"],"squirrel":false}, + {"events":["carrot","reading","weekend"],"squirrel":false}, + {"events":["carrot","peanuts","reading","weekend"],"squirrel":true}, + {"events":["potatoes","brushed teeth","running","work"],"squirrel":false}, + {"events":["lasagna","ice cream","work","touched tree"],"squirrel":false}, + {"events":["cauliflower","peanuts","brushed teeth","cycling","work"],"squirrel":false}, + {"events":["pizza","brushed teeth","running","work"],"squirrel":false}, + {"events":["lettuce","brushed teeth","work"],"squirrel":false}, + {"events":["bread","brushed teeth","television","weekend"],"squirrel":false}, + {"events":["cauliflower","peanuts","brushed teeth","weekend"],"squirrel":false} +]; + +// This makes sure the data is exported in node.js — +// `require('./path/to/journal.js')` will get you the array. +if (typeof module != "undefined" && module.exports && (typeof window == "undefined" || window.exports != exports)) + module.exports = JOURNAL; +if (typeof global != "undefined" && !global.JOURNAL) + global.JOURNAL = JOURNAL; diff --git a/docs/code/levels.js b/docs/code/levels.js new file mode 100644 index 000000000..9ff491fab --- /dev/null +++ b/docs/code/levels.js @@ -0,0 +1,178 @@ +var GAME_LEVELS = [` +................................................................................ +................................................................................ +................................................................................ +................................................................................ +................................................................................ +................................................................................ +..................................................................###........... +...................................................##......##....##+##.......... +....................................o.o......##..................#+++#.......... +.................................................................##+##.......... +...................................#####..........................#v#........... +............................................................................##.. +..##......................................o.o................................#.. +..#.....................o....................................................#.. +..#......................................#####.............................o.#.. +..#..........####.......o....................................................#.. +..#..@.......#..#................................................#####.......#.. +..############..###############...####################.....#######...#########.. +..............................#...#..................#.....#.................... +..............................#+++#..................#+++++#.................... +..............................#+++#..................#+++++#.................... +..............................#####..................#######.................... +................................................................................ +................................................................................ +`,` +................................................................................ +................................................................................ +....###############################............................................. +...##.............................##########################################.... +...#.......................................................................##... +...#....o...................................................................#... +...#................................................=.......................#... +...#.o........################...................o..o...........|........o..#... +...#.........................#..............................................#... +...#....o....................##########.....###################....##########... +...#..................................#+++++#.................#....#............ +...###############....oo......=o.o.o..#######.###############.#....#............ +.....#...............o..o.............#.......#......#........#....#............ +.....#....................#############..######.####.#.########....########..... +.....#.............########..............#...........#.#..................#..... +.....#..........####......####...#####################.#..................#..... +.....#........###............###.......................########....########..... +.....#.......##................#########################......#....#............ +.....#.......#................................................#....#............ +.....###......................................................#....#............ +.......#...............o...........................................#............ +.......#...............................................o...........#............ +.......#########......###.....############.........................##........... +.............#..................#........#####....#######.o.........########.... +.............#++++++++++++++++++#............#....#.....#..................#.... +.............#++++++++++++++++++#..........###....###...####.o.............#.... +.............####################..........#........#......#.....|.........#.... +...........................................#++++++++#......####............#.... +...........................................#++++++++#.........#........@...#.... +...........................................#++++++++#.........##############.... +...........................................##########........................... +................................................................................ +`,` +......................................#++#........................#######....................................#+#.. +......................................#++#.....................####.....####.................................#+#.. +......................................#++##########...........##...........##................................#+#.. +......................................##++++++++++##.........##.............##...............................#+#.. +.......................................##########++#.........#....................................o...o...o..#+#.. +................................................##+#.........#.....o...o....................................##+#.. +.................................................#+#.........#................................###############++#.. +.................................................#v#.........#.....#...#........................++++++++++++++##.. +.............................................................##..|...|...|..##............#####################... +..............................................................##+++++++++++##............v........................ +...............................................................####+++++####...................................... +...............................................#.....#............#######........###.........###.................. +...............................................#.....#...........................#.#.........#.#.................. +...............................................#.....#.............................#.........#.................... +...............................................#.....#.............................##........#.................... +...............................................##....#.............................#.........#.................... +...............................................#.....#......o..o.....#...#.........#.........#.................... +...............#######........###...###........#.....#...............#...#.........#.........#.................... +..............##.....##.........#...#..........#.....#.....######....#...#...#########.......#.................... +.............##.......##........#.o.#..........#....##...............#...#...#...............#.................... +.....@.......#.........#........#...#..........#.....#...............#...#...#...............#.................... +....###......#.........#........#...#..........#.....#...............#...#####...######......#.................... +....#.#......#.........#.......##.o.##.........#.....#...............#.....o.....#.#.........#.................... +++++#.#++++++#.........#++++++##.....##++++++++##....#++++++++++.....#.....=.....#.#.........#.................... +++++#.#++++++#.........#+++++##.......##########.....#+++++++##+.....#############.##..o.o..##.................... +++++#.#++++++#.........#+++++#....o.................##++++++##.+....................##.....##..................... +++++#.#++++++#.........#+++++#.....................##++++++##..+.....................#######...................... +++++#.#++++++#.........#+++++##.......##############++++++##...+.................................................. +++++#.#++++++#.........#++++++#########++++++++++++++++++##....+.................................................. +++++#.#++++++#.........#++++++++++++++++++++++++++++++++##.....+.................................................. +`,` +.............................................................................................................. +.............................................................................................................. +.............................................................................................................. +.............................................................................................................. +.............................................................................................................. +........................................o..................................................................... +.............................................................................................................. +........................................#..................................................................... +........................................#..................................................................... +........................................#..................................................................... +........................................#..................................................................... +.......................................###.................................................................... +.......................................#.#.................+++........+++..###................................ +.......................................#.#.................+#+........+#+..................................... +.....................................###.###................#..........#...................................... +......................................#...#.................#...oooo...#.......###............................ +......................................#...#.................#..........#......#+++#........................... +......................................#...#.................############.......###............................ +.....................................##...##......#...#......#................................................ +......................................#...#########...########..............#.#............................... +......................................#...#...........#....................#+++#.............................. +......................................#...#...........#.....................###............................... +.....................................##...##..........#....................................................... +......................................#...#=.=.=.=....#............###........................................ +......................................#...#...........#...........#+++#....................................... +......................................#...#....=.=.=.=#.....o......###.......###.............................. +.....................................##...##..........#.....................#+++#............................. +..............................o...o...#...#...........#.....#................##v........###................... +......................................#...#...........#..............#.................#+++#.................. +.............................###.###.###.###.....o.o..#++++++++++++++#...................v#................... +.............................#.###.#.#.###.#..........#++++++++++++++#........................................ +.............................#.............#...#######################........................................ +.............................##...........##.........................................###...................... +..###.........................#.....#.....#.........................................#+++#................###.. +..#.#.........................#....###....#..........................................###.................#.#.. +..#...........................#....###....#######........................#####.............................#.. +..#...........................#...........#..............................#...#.............................#.. +..#...........................##..........#..............................#.#.#.............................#.. +..#.......................................#.......|####|....|####|.....###.###.............................#.. +..#................###.............o.o....#..............................#.........###.....................#.. +..#...............#####.......##..........#.............................###.......#+++#..........#.........#.. +..#...............o###o.......#....###....#.............................#.#........###..........###........#.. +..#................###........#############..#.oo.#....#.oo.#....#.oo..##.##....................###........#.. +..#......@..........#.........#...........#++#....#++++#....#++++#....##...##....................#.........#.. +..#############################...........#############################.....################################.. +.............................................................................................................. +.............................................................................................................. +`,` +..................................................................................................###.#....... +......................................................................................................#....... +..................................................................................................#####....... +..................................................................................................#........... +..................................................................................................#.###....... +..........................o.......................................................................#.#.#....... +.............................................................................................o.o.o###.#....... +...................###................................................................................#....... +.......+..o..+................................................#####.#####.#####.#####.#####.#####.#####....... +.......#.....#................................................#...#.#...#.#...#.#...#.#...#.#...#.#........... +.......#=.o..#............#...................................###.#.###.#.###.#.###.#.###.#.###.#.#####....... +.......#.....#..................................................#.#...#.#...#.#...#.#...#.#...#.#.....#....... +.......+..o..+............o..................................####.#####.#####.#####.#####.#####.#######....... +.............................................................................................................. +..........o..............###..............................##.................................................. +.............................................................................................................. +.............................................................................................................. +......................................................##...................................................... +...................###.........###............................................................................ +.............................................................................................................. +..........................o.....................................................#......#...................... +..........................................................##.....##........................................... +.............###.........###.........###.................................#..................#................. +.............................................................................................................. +.................................................................||........................................... +..###########................................................................................................. +..#.........#.o.#########.o.#########.o.##................................................#................... +..#.........#...#.......#...#.......#...#.................||..................#.....#......................... +..#..@......#####...o...#####...o...#####..................................................................... +..#######.....................................#####.......##.....##.....###................................... +........#=..................=................=#...#.....................###................................... +........#######################################...#+++++++++++++++++++++###+++++++++++++++++++++++++++++++++++ +..................................................############################################################ +.............................................................................................................. +`]; + +if (typeof module != "undefined" && module.exports && (typeof window == "undefined" || window.exports != exports)) + module.exports = GAME_LEVELS; +if (typeof global != "undefined" && !global.GAME_LEVELS) + global.GAME_LEVELS = GAME_LEVELS; diff --git a/docs/code/load.js b/docs/code/load.js new file mode 100644 index 000000000..bf44c1080 --- /dev/null +++ b/docs/code/load.js @@ -0,0 +1,9 @@ +// Since the code for most chapter in Eloquent JavaScript isn't +// written with node's module system in mind, this kludge is used to +// load dependency files into the global namespace, so that the +// examples can run on node. + +module.exports = function(...args) { + for (let arg of args) + (1,eval)(require("fs").readFileSync(__dirname + "/../" + arg, "utf8")) +} diff --git a/docs/code/packages_chapter_10.js b/docs/code/packages_chapter_10.js new file mode 100644 index 000000000..5d11cd7b0 --- /dev/null +++ b/docs/code/packages_chapter_10.js @@ -0,0 +1,507 @@ +/* ordinal 1.0.2: https://github.com/dcousens/ordinal/ + +Copyright (c) 2016, Daniel Cousens + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ + +require.preload("ordinal", String.raw` +function indicator (i) { + var cent = i % 100 + if (cent >= 10 && cent <= 20) return 'th' + var dec = i % 10 + if (dec === 1) return 'st' + if (dec === 2) return 'nd' + if (dec === 3) return 'rd' + return 'th' +} + +function ordinal (i) { + if (typeof i !== 'number') throw new TypeError('Expected Number, got ' + (typeof i) + ' ' + i) + return i + indicator(i) +} + +ordinal.indicator = indicator +module.exports = ordinal`) + +/* date-names 0.1.11: https://github.com/martinandert/date-names + +The MIT License (MIT) + +Copyright (c) 2014 Martin Andert + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + +require.preload("date-names", String.raw` +module.exports = { + __locale: "en", + days: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'], + abbreviated_days: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'], + months: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'], + abbreviated_months: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'], + am: 'AM', + pm: 'PM' +};`) + +/* ini 1.3.5: https://github.com/npm/ini + +The ISC License + +Copyright (c) Isaac Z. Schlueter and Contributors + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR +IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ + +require.preload("ini", String.raw`exports.parse = exports.decode = decode + +exports.stringify = exports.encode = encode + +exports.safe = safe +exports.unsafe = unsafe + +var eol = typeof process !== 'undefined' && + process.platform === 'win32' ? '\r\n' : '\n' + +function encode (obj, opt) { + var children = [] + var out = '' + + if (typeof opt === 'string') { + opt = { + section: opt, + whitespace: false + } + } else { + opt = opt || {} + opt.whitespace = opt.whitespace === true + } + + var separator = opt.whitespace ? ' = ' : '=' + + Object.keys(obj).forEach(function (k, _, __) { + var val = obj[k] + if (val && Array.isArray(val)) { + val.forEach(function (item) { + out += safe(k + '[]') + separator + safe(item) + '\n' + }) + } else if (val && typeof val === 'object') { + children.push(k) + } else { + out += safe(k) + separator + safe(val) + eol + } + }) + + if (opt.section && out.length) { + out = '[' + safe(opt.section) + ']' + eol + out + } + + children.forEach(function (k, _, __) { + var nk = dotSplit(k).join('\\.') + var section = (opt.section ? opt.section + '.' : '') + nk + var child = encode(obj[k], { + section: section, + whitespace: opt.whitespace + }) + if (out.length && child.length) { + out += eol + } + out += child + }) + + return out +} + +function dotSplit (str) { + return str.replace(/\1/g, '\u0002LITERAL\\1LITERAL\u0002') + .replace(/\\\./g, '\u0001') + .split(/\./).map(function (part) { + return part.replace(/\1/g, '\\.') + .replace(/\2LITERAL\\1LITERAL\2/g, '\u0001') + }) +} + +function decode (str) { + var out = {} + var p = out + var section = null + // section |key = value + var re = /^\[([^\]]*)\]$|^([^=]+)(=(.*))?$/i + var lines = str.split(/[\r\n]+/g) + + lines.forEach(function (line, _, __) { + if (!line || line.match(/^\s*[;#]/)) return + var match = line.match(re) + if (!match) return + if (match[1] !== undefined) { + section = unsafe(match[1]) + p = out[section] = out[section] || {} + return + } + var key = unsafe(match[2]) + var value = match[3] ? unsafe(match[4]) : true + switch (value) { + case 'true': + case 'false': + case 'null': value = JSON.parse(value) + } + + // Convert keys with '[]' suffix to an array + if (key.length > 2 && key.slice(-2) === '[]') { + key = key.substring(0, key.length - 2) + if (!p[key]) { + p[key] = [] + } else if (!Array.isArray(p[key])) { + p[key] = [p[key]] + } + } + + // safeguard against resetting a previously defined + // array by accidentally forgetting the brackets + if (Array.isArray(p[key])) { + p[key].push(value) + } else { + p[key] = value + } + }) + + // {a:{y:1},"a.b":{x:2}} --> {a:{y:1,b:{x:2}}} + // use a filter to return the keys that have to be deleted. + Object.keys(out).filter(function (k, _, __) { + if (!out[k] || + typeof out[k] !== 'object' || + Array.isArray(out[k])) { + return false + } + // see if the parent section is also an object. + // if so, add it to that, and mark this one for deletion + var parts = dotSplit(k) + var p = out + var l = parts.pop() + var nl = l.replace(/\\\./g, '.') + parts.forEach(function (part, _, __) { + if (!p[part] || typeof p[part] !== 'object') p[part] = {} + p = p[part] + }) + if (p === out && nl === l) { + return false + } + p[nl] = out[k] + return true + }).forEach(function (del, _, __) { + delete out[del] + }) + + return out +} + +function isQuoted (val) { + return (val.charAt(0) === '"' && val.slice(-1) === '"') || + (val.charAt(0) === "'" && val.slice(-1) === "'") +} + +function safe (val) { + return (typeof val !== 'string' || + val.match(/[=\r\n]/) || + val.match(/^\[/) || + (val.length > 1 && + isQuoted(val)) || + val !== val.trim()) + ? JSON.stringify(val) + : val.replace(/;/g, '\\;').replace(/#/g, '\\#') +} + +function unsafe (val, doUnesc) { + val = (val || '').trim() + if (isQuoted(val)) { + // remove the single quotes before calling JSON.parse + if (val.charAt(0) === "'") { + val = val.substr(1, val.length - 2) + } + try { val = JSON.parse(val) } catch (_) {} + } else { + // walk the val to find the first not-escaped ; character + var esc = false + var unesc = '' + for (var i = 0, l = val.length; i < l; i++) { + var c = val.charAt(i) + if (esc) { + if ('\\;#'.indexOf(c) !== -1) { + unesc += c + } else { + unesc += '\\' + c + } + esc = false + } else if (';#'.indexOf(c) !== -1) { + break + } else if (c === '\\') { + esc = true + } else { + unesc += c + } + } + if (esc) { + unesc += '\\' + } + return unesc.trim() + } + return val +}`) + +/* dijkstrajs 1.0.1: https://github.com/tcort/dijkstrajs/ + +Dijkstra path-finding functions. Adapted from the Dijkstar Python project. + +Copyright (C) 2008 + Wyatt Baldwin + All rights reserved + +Licensed under the MIT license. + + http://www.opensource.org/licenses/mit-license.php + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. */ + +require.preload("dijkstrajs", String.raw`'use strict'; + +var dijkstra = { + single_source_shortest_paths: function(graph, s, d) { + // Predecessor map for each node that has been encountered. + // node ID => predecessor node ID + var predecessors = {}; + + // Costs of shortest paths from s to all nodes encountered. + // node ID => cost + var costs = {}; + costs[s] = 0; + + // Costs of shortest paths from s to all nodes encountered; differs from + // costs in that it provides easy access to the node that currently has + // the known shortest path from s. + // XXX: Do we actually need both costs and open? + var open = dijkstra.PriorityQueue.make(); + open.push(s, 0); + + var closest, + u, v, + cost_of_s_to_u, + adjacent_nodes, + cost_of_e, + cost_of_s_to_u_plus_cost_of_e, + cost_of_s_to_v, + first_visit; + while (!open.empty()) { + // In the nodes remaining in graph that have a known cost from s, + // find the node, u, that currently has the shortest path from s. + closest = open.pop(); + u = closest.value; + cost_of_s_to_u = closest.cost; + + // Get nodes adjacent to u... + adjacent_nodes = graph[u] || {}; + + // ...and explore the edges that connect u to those nodes, updating + // the cost of the shortest paths to any or all of those nodes as + // necessary. v is the node across the current edge from u. + for (v in adjacent_nodes) { + if (adjacent_nodes.hasOwnProperty(v)) { + // Get the cost of the edge running from u to v. + cost_of_e = adjacent_nodes[v]; + + // Cost of s to u plus the cost of u to v across e--this is *a* + // cost from s to v that may or may not be less than the current + // known cost to v. + cost_of_s_to_u_plus_cost_of_e = cost_of_s_to_u + cost_of_e; + + // If we haven't visited v yet OR if the current known cost from s to + // v is greater than the new cost we just found (cost of s to u plus + // cost of u to v across e), update v's cost in the cost list and + // update v's predecessor in the predecessor list (it's now u). + cost_of_s_to_v = costs[v]; + first_visit = (typeof costs[v] === 'undefined'); + if (first_visit || cost_of_s_to_v > cost_of_s_to_u_plus_cost_of_e) { + costs[v] = cost_of_s_to_u_plus_cost_of_e; + open.push(v, cost_of_s_to_u_plus_cost_of_e); + predecessors[v] = u; + } + } + } + } + + if (typeof d !== 'undefined' && typeof costs[d] === 'undefined') { + var msg = ['Could not find a path from ', s, ' to ', d, '.'].join(''); + throw new Error(msg); + } + + return predecessors; + }, + + extract_shortest_path_from_predecessor_list: function(predecessors, d) { + var nodes = []; + var u = d; + var predecessor; + while (u) { + nodes.push(u); + predecessor = predecessors[u]; + u = predecessors[u]; + } + nodes.reverse(); + return nodes; + }, + + find_path: function(graph, s, d) { + var predecessors = dijkstra.single_source_shortest_paths(graph, s, d); + return dijkstra.extract_shortest_path_from_predecessor_list( + predecessors, d); + }, + + /** + * A very naive priority queue implementation. + */ + PriorityQueue: { + make: function (opts) { + var T = dijkstra.PriorityQueue, + t = {}, + key; + opts = opts || {}; + for (key in T) { + if (T.hasOwnProperty(key)) { + t[key] = T[key]; + } + } + t.queue = []; + t.sorter = opts.sorter || T.default_sorter; + return t; + }, + + default_sorter: function (a, b) { + return a.cost - b.cost; + }, + + /** + * Add a new item to the queue and ensure the highest priority element + * is at the front of the queue. + */ + push: function (value, cost) { + var item = {value: value, cost: cost}; + this.queue.push(item); + this.queue.sort(this.sorter); + }, + + /** + * Return the highest priority element in the queue. + */ + pop: function () { + return this.queue.shift(); + }, + + empty: function () { + return this.queue.length === 0; + } + } +}; + + +// node.js module exports +if (typeof module !== 'undefined') { + module.exports = dijkstra; +}`) + +/* random-item 1.0.0: https://github.com/sindresorhus/random-item + +The MIT License (MIT) + +Copyright (c) Sindre Sorhus (sindresorhus.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. */ + +require.preload("random-item", String.raw`'use strict'; +module.exports = function (arr) { + if (!Array.isArray(arr)) { + throw new TypeError('Expected an array'); + } + + return arr[Math.floor(Math.random() * arr.length)]; +};`) + +require.preload("./format-date", String.raw`const ordinal = require("ordinal"); +const {days, months} = require("date-names"); + +exports.formatDate = function(date, format) { + return format.replace(/YYYY|M(MMM)?|Do?|dddd/g, tag => { + if (tag == "YYYY") return date.getFullYear(); + if (tag == "M") return date.getMonth(); + if (tag == "MMMM") return months[date.getMonth()]; + if (tag == "D") return date.getDate(); + if (tag == "Do") return ordinal(date.getDate()); + if (tag == "dddd") return days[date.getDay()]; + }); +};`) + +require.preload("./graph", String.raw`exports.buildGraph = function(edges) { + let graph = Object.create(null); + function addEdge(from, to) { + if (!(from in graph)) graph[from] = Object.create(null); + graph[from][to] = 1; + } + for (let [from, to] of edges) { + addEdge(from, to); + addEdge(to, from); + } + return graph; +};`) + diff --git a/docs/code/scripts.js b/docs/code/scripts.js new file mode 100644 index 000000000..a58105b27 --- /dev/null +++ b/docs/code/scripts.js @@ -0,0 +1,1123 @@ +// Generated from the Unicode 10 database and https://en.wikipedia.org/wiki/Script_(Unicode) + +var SCRIPTS = [ + { + name: "Adlam", + ranges: [[125184, 125259], [125264, 125274], [125278, 125280]], + direction: "rtl", + year: 1987, + living: true, + link: "https://en.wikipedia.org/wiki/Fula_alphabets#Adlam_alphabet" + }, + { + name: "Caucasian Albanian", + ranges: [[66864, 66916], [66927, 66928]], + direction: "ltr", + year: 420, + living: false, + link: "https://en.wikipedia.org/wiki/Caucasian_Albanian_alphabet" + }, + { + name: "Ahom", + ranges: [[71424, 71450], [71453, 71468], [71472, 71488]], + direction: "ltr", + year: 1250, + living: false, + link: "https://en.wikipedia.org/wiki/Ahom_alphabet" + }, + { + name: "Arabic", + ranges: [[1536, 1541], [1542, 1548], [1549, 1563], [1564, 1565], [1566, 1567], [1568, 1600], [1601, 1611], [1622, 1648], [1649, 1757], [1758, 1792], [1872, 1920], [2208, 2229], [2230, 2238], [2260, 2274], [2275, 2304], [64336, 64450], [64467, 64830], [64848, 64912], [64914, 64968], [65008, 65022], [65136, 65141], [65142, 65277], [69216, 69247], [126464, 126468], [126469, 126496], [126497, 126499], [126500, 126501], [126503, 126504], [126505, 126515], [126516, 126520], [126521, 126522], [126523, 126524], [126530, 126531], [126535, 126536], [126537, 126538], [126539, 126540], [126541, 126544], [126545, 126547], [126548, 126549], [126551, 126552], [126553, 126554], [126555, 126556], [126557, 126558], [126559, 126560], [126561, 126563], [126564, 126565], [126567, 126571], [126572, 126579], [126580, 126584], [126585, 126589], [126590, 126591], [126592, 126602], [126603, 126620], [126625, 126628], [126629, 126634], [126635, 126652], [126704, 126706]], + direction: "rtl", + year: 400, + living: true, + link: "https://en.wikipedia.org/wiki/Arabic_script" + }, + { + name: "Imperial Aramaic", + ranges: [[67648, 67670], [67671, 67680]], + direction: "rtl", + year: 800, + living: false, + link: "https://en.wikipedia.org/wiki/Aramaic_alphabet" + }, + { + name: "Armenian", + ranges: [[1329, 1367], [1369, 1376], [1377, 1416], [1418, 1419], [1421, 1424], [64275, 64280]], + direction: "ltr", + year: 405, + living: true, + link: "https://en.wikipedia.org/wiki/Armenian_alphabet" + }, + { + name: "Avestan", + ranges: [[68352, 68406], [68409, 68416]], + direction: "rtl", + year: 400, + living: false, + link: "https://en.wikipedia.org/wiki/Avestan_alphabet" + }, + { + name: "Balinese", + ranges: [[6912, 6988], [6992, 7037]], + direction: "ltr", + year: 1000, + living: true, + link: "https://en.wikipedia.org/wiki/Balinese_script" + }, + { + name: "Bamum", + ranges: [[42656, 42744], [92160, 92729]], + direction: "ltr", + year: 1896, + living: true, + link: "https://en.wikipedia.org/wiki/Bamum_script" + }, + { + name: "Bassa Vah", + ranges: [[92880, 92910], [92912, 92918]], + direction: "ltr", + year: 1950, + living: false, + link: "https://en.wikipedia.org/wiki/Bassa_alphabet" + }, + { + name: "Batak", + ranges: [[7104, 7156], [7164, 7168]], + direction: "ltr", + year: 1300, + living: true, + link: "https://en.wikipedia.org/wiki/Batak_alphabet" + }, + { + name: "Bengali", + ranges: [[2432, 2436], [2437, 2445], [2447, 2449], [2451, 2473], [2474, 2481], [2482, 2483], [2486, 2490], [2492, 2501], [2503, 2505], [2507, 2511], [2519, 2520], [2524, 2526], [2527, 2532], [2534, 2558]], + direction: "ltr", + year: 1050, + living: true, + link: "https://en.wikipedia.org/wiki/Bengali_alphabet" + }, + { + name: "Bhaiksuki", + ranges: [[72704, 72713], [72714, 72759], [72760, 72774], [72784, 72813]], + direction: "ltr", + year: 1050, + living: false, + link: "https://en.wikipedia.org/wiki/Bhaiksuki_alphabet" + }, + { + name: "Bopomofo", + ranges: [[746, 748], [12549, 12591], [12704, 12731]], + direction: "ltr", + year: 1918, + living: true, + link: "https://en.wikipedia.org/wiki/Bopomofo" + }, + { + name: "Brahmi", + ranges: [[69632, 69710], [69714, 69744], [69759, 69760]], + direction: "ltr", + year: -250, + living: false, + link: "https://en.wikipedia.org/wiki/Brahmi_script" + }, + { + name: "Braille", + ranges: [[10240, 10496]], + direction: "ltr", + year: 1824, + living: true, + link: "https://en.wikipedia.org/wiki/Braille" + }, + { + name: "Buginese", + ranges: [[6656, 6684], [6686, 6688]], + direction: "ltr", + year: 1650, + living: true, + link: "https://en.wikipedia.org/wiki/Lontara_script" + }, + { + name: "Buhid", + ranges: [[5952, 5972]], + direction: "ltr", + year: 1300, + living: true, + link: "https://en.wikipedia.org/wiki/Buhid_alphabet" + }, + { + name: "Chakma", + ranges: [[69888, 69941], [69942, 69956]], + direction: "ltr", + year: 1050, + living: true, + link: "https://en.wikipedia.org/wiki/Chakma_alphabet" + }, + { + name: "Canadian Aboriginal", + ranges: [[5120, 5760], [6320, 6390]], + direction: "ltr", + year: 1840, + living: true, + link: "https://en.wikipedia.org/wiki/Canadian_Aboriginal_syllabics" + }, + { + name: "Carian", + ranges: [[66208, 66257]], + direction: "ltr", + year: -650, + living: false, + link: "https://en.wikipedia.org/wiki/Carian_alphabets" + }, + { + name: "Cham", + ranges: [[43520, 43575], [43584, 43598], [43600, 43610], [43612, 43616]], + direction: "ltr", + year: 750, + living: true, + link: "https://en.wikipedia.org/wiki/Cham_alphabet" + }, + { + name: "Cherokee", + ranges: [[5024, 5110], [5112, 5118], [43888, 43968]], + direction: "ltr", + year: 1820, + living: true, + link: "https://en.wikipedia.org/wiki/Cherokee_syllabary" + }, + { + name: "Coptic", + ranges: [[994, 1008], [11392, 11508], [11513, 11520]], + direction: "ltr", + year: -200, + living: false, + link: "https://en.wikipedia.org/wiki/Coptic_alphabet" + }, + { + name: "Cypriot", + ranges: [[67584, 67590], [67592, 67593], [67594, 67638], [67639, 67641], [67644, 67645], [67647, 67648]], + direction: "rtl", + year: -1100, + living: false, + link: "https://en.wikipedia.org/wiki/Cypriot_syllabary" + }, + { + name: "Cyrillic", + ranges: [[1024, 1157], [1159, 1328], [7296, 7305], [7467, 7468], [7544, 7545], [11744, 11776], [42560, 42656], [65070, 65072]], + direction: "ltr", + year: 950, + living: true, + link: "https://en.wikipedia.org/wiki/Cyrillic_script" + }, + { + name: "Devanagari", + ranges: [[2304, 2385], [2387, 2404], [2406, 2432], [43232, 43262]], + direction: "ltr", + year: 100, + living: true, + link: "https://en.wikipedia.org/wiki/Devanagari" + }, + { + name: "Deseret", + ranges: [[66560, 66640]], + direction: "ltr", + year: 1854, + living: true, + link: "https://en.wikipedia.org/wiki/Deseret_alphabet" + }, + { + name: "Duployan", + ranges: [[113664, 113771], [113776, 113789], [113792, 113801], [113808, 113818], [113820, 113824]], + direction: "ltr", + year: 1860, + living: true, + link: "https://en.wikipedia.org/wiki/Duployan_shorthand" + }, + { + name: "Egyptian Hieroglyphs", + ranges: [[77824, 78895]], + direction: "ltr", + year: -3200, + living: false, + link: "https://en.wikipedia.org/wiki/Egyptian_hieroglyphs" + }, + { + name: "Elbasan", + ranges: [[66816, 66856]], + direction: "ltr", + year: 1750, + living: false, + link: "https://en.wikipedia.org/wiki/Elbasan_alphabet" + }, + { + name: "Ethiopic", + ranges: [[4608, 4681], [4682, 4686], [4688, 4695], [4696, 4697], [4698, 4702], [4704, 4745], [4746, 4750], [4752, 4785], [4786, 4790], [4792, 4799], [4800, 4801], [4802, 4806], [4808, 4823], [4824, 4881], [4882, 4886], [4888, 4955], [4957, 4989], [4992, 5018], [11648, 11671], [11680, 11687], [11688, 11695], [11696, 11703], [11704, 11711], [11712, 11719], [11720, 11727], [11728, 11735], [11736, 11743], [43777, 43783], [43785, 43791], [43793, 43799], [43808, 43815], [43816, 43823]], + direction: "ltr", + year: -900, + living: true, + link: "https://en.wikipedia.org/wiki/Ge%27ez_script" + }, + { + name: "Georgian", + ranges: [[4256, 4294], [4295, 4296], [4301, 4302], [4304, 4347], [4348, 4352], [11520, 11558], [11559, 11560], [11565, 11566]], + direction: "ltr", + year: 430, + living: true, + link: "https://en.wikipedia.org/wiki/Georgian_scripts" + }, + { + name: "Glagolitic", + ranges: [[11264, 11311], [11312, 11359], [122880, 122887], [122888, 122905], [122907, 122914], [122915, 122917], [122918, 122923]], + direction: "ltr", + year: 862, + living: false, + link: "https://en.wikipedia.org/wiki/Glagolitic_script" + }, + { + name: "Masaram Gondi", + ranges: [[72960, 72967], [72968, 72970], [72971, 73015], [73018, 73019], [73020, 73022], [73023, 73032], [73040, 73050]], + direction: "ltr", + year: 1918, + living: true, + link: "https://en.wikipedia.org/wiki/Gondi_writing#Masaram" + }, + { + name: "Gothic", + ranges: [[66352, 66379]], + direction: "ltr", + year: 350, + living: false, + link: "https://en.wikipedia.org/wiki/Gothic_alphabet" + }, + { + name: "Grantha", + ranges: [[70400, 70404], [70405, 70413], [70415, 70417], [70419, 70441], [70442, 70449], [70450, 70452], [70453, 70458], [70460, 70469], [70471, 70473], [70475, 70478], [70480, 70481], [70487, 70488], [70493, 70500], [70502, 70509], [70512, 70517]], + direction: "ltr", + year: 550, + living: false, + link: "https://en.wikipedia.org/wiki/Grantha_alphabet" + }, + { + name: "Greek", + ranges: [[880, 884], [885, 888], [890, 894], [895, 896], [900, 901], [902, 903], [904, 907], [908, 909], [910, 930], [931, 994], [1008, 1024], [7462, 7467], [7517, 7522], [7526, 7531], [7615, 7616], [7936, 7958], [7960, 7966], [7968, 8006], [8008, 8014], [8016, 8024], [8025, 8026], [8027, 8028], [8029, 8030], [8031, 8062], [8064, 8117], [8118, 8133], [8134, 8148], [8150, 8156], [8157, 8176], [8178, 8181], [8182, 8191], [8486, 8487], [43877, 43878], [65856, 65935], [65952, 65953], [119296, 119366]], + direction: "ltr", + year: -800, + living: true, + link: "https://en.wikipedia.org/wiki/Greek_alphabet" + }, + { + name: "Gujarati", + ranges: [[2689, 2692], [2693, 2702], [2703, 2706], [2707, 2729], [2730, 2737], [2738, 2740], [2741, 2746], [2748, 2758], [2759, 2762], [2763, 2766], [2768, 2769], [2784, 2788], [2790, 2802], [2809, 2816]], + direction: "ltr", + year: 1592, + living: true, + link: "https://en.wikipedia.org/wiki/Gujarati_alphabet" + }, + { + name: "Gurmukhi", + ranges: [[2561, 2564], [2565, 2571], [2575, 2577], [2579, 2601], [2602, 2609], [2610, 2612], [2613, 2615], [2616, 2618], [2620, 2621], [2622, 2627], [2631, 2633], [2635, 2638], [2641, 2642], [2649, 2653], [2654, 2655], [2662, 2678]], + direction: "ltr", + year: 1550, + living: true, + link: "https://en.wikipedia.org/wiki/Gurmukh%C4%AB_alphabet" + }, + { + name: "Hangul", + ranges: [[4352, 4608], [12334, 12336], [12593, 12687], [12800, 12831], [12896, 12927], [43360, 43389], [44032, 55204], [55216, 55239], [55243, 55292], [65440, 65471], [65474, 65480], [65482, 65488], [65490, 65496], [65498, 65501]], + direction: "ltr", + year: 1443, + living: true, + link: "https://en.wikipedia.org/wiki/Hangul" + }, + { + name: "Han", + ranges: [[11904, 11930], [11931, 12020], [12032, 12246], [12293, 12294], [12295, 12296], [12321, 12330], [12344, 12348], [13312, 19894], [19968, 40939], [63744, 64110], [64112, 64218], [131072, 173783], [173824, 177973], [177984, 178206], [178208, 183970], [183984, 191457], [194560, 195102]], + direction: "ltr", + year: -1100, + living: true, + link: "https://en.wikipedia.org/wiki/Chinese_characters" + }, + { + name: "Hanunoo", + ranges: [[5920, 5941]], + direction: "ltr", + year: 1300, + living: true, + link: "https://en.wikipedia.org/wiki/Hanun%C3%B3%27o_alphabet" + }, + { + name: "Hatran", + ranges: [[67808, 67827], [67828, 67830], [67835, 67840]], + direction: "rtl", + year: -40, + living: false, + link: "https://en.wikipedia.org/wiki/Hatran_alphabet" + }, + { + name: "Hebrew", + ranges: [[1425, 1480], [1488, 1515], [1520, 1525], [64285, 64311], [64312, 64317], [64318, 64319], [64320, 64322], [64323, 64325], [64326, 64336]], + direction: "rtl", + year: -100, + living: true, + link: "https://en.wikipedia.org/wiki/Hebrew_alphabet" + }, + { + name: "Hiragana", + ranges: [[12353, 12439], [12445, 12448], [110593, 110879], [127488, 127489]], + direction: "ltr", + year: 800, + living: true, + link: "https://en.wikipedia.org/wiki/Hiragana" + }, + { + name: "Anatolian Hieroglyphs", + ranges: [[82944, 83527]], + direction: "ltr", + year: -1400, + living: false, + link: "https://en.wikipedia.org/wiki/Anatolian_hieroglyphs" + }, + { + name: "Pahawh Hmong", + ranges: [[92928, 92998], [93008, 93018], [93019, 93026], [93027, 93048], [93053, 93072]], + direction: "ltr", + year: 1959, + living: true, + link: "https://en.wikipedia.org/wiki/Pahawh_Hmong" + }, + { + name: "Old Hungarian", + ranges: [[68736, 68787], [68800, 68851], [68858, 68864]], + direction: "rtl", + year: 1150, + living: false, + link: "https://en.wikipedia.org/wiki/Old_Hungarian_alphabet" + }, + { + name: "Old Italic", + ranges: [[66304, 66340], [66349, 66352]], + direction: "ltr", + year: -750, + living: false, + link: "https://en.wikipedia.org/wiki/Old_Italic_script" + }, + { + name: "Javanese", + ranges: [[43392, 43470], [43472, 43482], [43486, 43488]], + direction: "ltr", + year: 1250, + living: true, + link: "https://en.wikipedia.org/wiki/Javanese_script" + }, + { + name: "Kayah Li", + ranges: [[43264, 43310], [43311, 43312]], + direction: "ltr", + year: 1962, + living: true, + link: "https://en.wikipedia.org/wiki/Kayah_Li_alphabet" + }, + { + name: "Katakana", + ranges: [[12449, 12539], [12541, 12544], [12784, 12800], [13008, 13055], [13056, 13144], [65382, 65392], [65393, 65438], [110592, 110593]], + direction: "ltr", + year: 800, + living: true, + link: "https://en.wikipedia.org/wiki/Katakana" + }, + { + name: "Kharoshthi", + ranges: [[68096, 68100], [68101, 68103], [68108, 68116], [68117, 68120], [68121, 68148], [68152, 68155], [68159, 68168], [68176, 68185]], + direction: "rtl", + year: -400, + living: false, + link: "https://en.wikipedia.org/wiki/Kharosthi" + }, + { + name: "Khmer", + ranges: [[6016, 6110], [6112, 6122], [6128, 6138], [6624, 6656]], + direction: "ltr", + year: 611, + living: true, + link: "https://en.wikipedia.org/wiki/Khmer_alphabet" + }, + { + name: "Khojki", + ranges: [[70144, 70162], [70163, 70207]], + direction: "ltr", + year: 1520, + living: false, + link: "https://en.wikipedia.org/wiki/Khojki_script" + }, + { + name: "Kannada", + ranges: [[3200, 3204], [3205, 3213], [3214, 3217], [3218, 3241], [3242, 3252], [3253, 3258], [3260, 3269], [3270, 3273], [3274, 3278], [3285, 3287], [3294, 3295], [3296, 3300], [3302, 3312], [3313, 3315]], + direction: "ltr", + year: 450, + living: true, + link: "https://en.wikipedia.org/wiki/Kannada_alphabet" + }, + { + name: "Kaithi", + ranges: [[69760, 69826]], + direction: "ltr", + year: 1550, + living: false, + link: "https://en.wikipedia.org/wiki/Kaithi" + }, + { + name: "Tai Tham", + ranges: [[6688, 6751], [6752, 6781], [6783, 6794], [6800, 6810], [6816, 6830]], + direction: "ltr", + year: 1300, + living: true, + link: "https://en.wikipedia.org/wiki/Tai_Tham_alphabet" + }, + { + name: "Lao", + ranges: [[3713, 3715], [3716, 3717], [3719, 3721], [3722, 3723], [3725, 3726], [3732, 3736], [3737, 3744], [3745, 3748], [3749, 3750], [3751, 3752], [3754, 3756], [3757, 3770], [3771, 3774], [3776, 3781], [3782, 3783], [3784, 3790], [3792, 3802], [3804, 3808]], + direction: "ltr", + year: 1350, + living: true, + link: "https://en.wikipedia.org/wiki/Lao_alphabet" + }, + { + name: "Latin", + ranges: [[65, 91], [97, 123], [170, 171], [186, 187], [192, 215], [216, 247], [248, 697], [736, 741], [7424, 7462], [7468, 7517], [7522, 7526], [7531, 7544], [7545, 7615], [7680, 7936], [8305, 8306], [8319, 8320], [8336, 8349], [8490, 8492], [8498, 8499], [8526, 8527], [8544, 8585], [11360, 11392], [42786, 42888], [42891, 42927], [42928, 42936], [42999, 43008], [43824, 43867], [43868, 43877], [64256, 64263], [65313, 65339], [65345, 65371]], + direction: "ltr", + year: -700, + living: true, + link: "https://en.wikipedia.org/wiki/Latin_script" + }, + { + name: "Lepcha", + ranges: [[7168, 7224], [7227, 7242], [7245, 7248]], + direction: "ltr", + year: 1700, + living: true, + link: "https://en.wikipedia.org/wiki/Lepcha_alphabet" + }, + { + name: "Limbu", + ranges: [[6400, 6431], [6432, 6444], [6448, 6460], [6464, 6465], [6468, 6480]], + direction: "ltr", + year: 1740, + living: true, + link: "https://en.wikipedia.org/wiki/Limbu_alphabet" + }, + { + name: "Linear A", + ranges: [[67072, 67383], [67392, 67414], [67424, 67432]], + direction: "ltr", + year: -2500, + living: false, + link: "https://en.wikipedia.org/wiki/Linear_A" + }, + { + name: "Linear B", + ranges: [[65536, 65548], [65549, 65575], [65576, 65595], [65596, 65598], [65599, 65614], [65616, 65630], [65664, 65787]], + direction: "ltr", + year: -1450, + living: false, + link: "https://en.wikipedia.org/wiki/Linear_B" + }, + { + name: "Lisu", + ranges: [[42192, 42240]], + direction: "ltr", + year: 1915, + living: true, + link: "https://en.wikipedia.org/wiki/Fraser_alphabet" + }, + { + name: "Lycian", + ranges: [[66176, 66205]], + direction: "ltr", + year: -500, + living: false, + link: "https://en.wikipedia.org/wiki/Lycian_alphabet" + }, + { + name: "Lydian", + ranges: [[67872, 67898], [67903, 67904]], + direction: "rtl", + year: -700, + living: false, + link: "https://en.wikipedia.org/wiki/Lydian_alphabet" + }, + { + name: "Mahajani", + ranges: [[69968, 70007]], + direction: "ltr", + year: 1150, + living: false, + link: "https://en.wikipedia.org/wiki/Mahajani" + }, + { + name: "Mandaic", + ranges: [[2112, 2140], [2142, 2143]], + direction: "rtl", + year: 200, + living: true, + link: "https://en.wikipedia.org/wiki/Mandaic_alphabet" + }, + { + name: "Manichaean", + ranges: [[68288, 68327], [68331, 68343]], + direction: "rtl", + year: 250, + living: false, + link: "https://en.wikipedia.org/wiki/Manichaean_alphabet" + }, + { + name: "Marchen", + ranges: [[72816, 72848], [72850, 72872], [72873, 72887]], + direction: "ltr", + year: 650, + living: false, + link: "https://en.wikipedia.org/wiki/Zhang-Zhung_language#Scripts" + }, + { + name: "Mende Kikakui", + ranges: [[124928, 125125], [125127, 125143]], + direction: "rtl", + year: 1880, + living: true, + link: "https://en.wikipedia.org/wiki/Mende_Kikakui_script" + }, + { + name: "Meroitic Cursive", + ranges: [[68000, 68024], [68028, 68048], [68050, 68096]], + direction: "rtl", + year: -300, + living: false, + link: "https://en.wikipedia.org/wiki/Meroitic_alphabet" + }, + { + name: "Meroitic Hieroglyphs", + ranges: [[67968, 68000]], + direction: "rtl", + year: -300, + living: false, + link: "https://en.wikipedia.org/wiki/Meroitic_alphabet" + }, + { + name: "Malayalam", + ranges: [[3328, 3332], [3333, 3341], [3342, 3345], [3346, 3397], [3398, 3401], [3402, 3408], [3412, 3428], [3430, 3456]], + direction: "ltr", + year: 830, + living: true, + link: "https://en.wikipedia.org/wiki/Malayalam_script" + }, + { + name: "Modi", + ranges: [[71168, 71237], [71248, 71258]], + direction: "ltr", + year: 1200, + living: false, + link: "https://en.wikipedia.org/wiki/Modi_alphabet" + }, + { + name: "Mongolian", + ranges: [[6144, 6146], [6148, 6149], [6150, 6159], [6160, 6170], [6176, 6264], [6272, 6315], [71264, 71277]], + direction: "ttb", + year: 1204, + living: false, + link: "https://en.wikipedia.org/wiki/Mongolian_script" + }, + { + name: "Mro", + ranges: [[92736, 92767], [92768, 92778], [92782, 92784]], + direction: "ltr", + year: 1985, + living: true, + link: "https://en.wikipedia.org/wiki/Mru_language#Alphabet" + }, + { + name: "Meetei Mayek", + ranges: [[43744, 43767], [43968, 44014], [44016, 44026]], + direction: "ltr", + year: 200, + living: true, + link: "https://en.wikipedia.org/wiki/Meitei_script" + }, + { + name: "Multani", + ranges: [[70272, 70279], [70280, 70281], [70282, 70286], [70287, 70302], [70303, 70314]], + direction: "ltr", + year: 1750, + living: false, + link: "https://en.wikipedia.org/wiki/Multani_alphabet" + }, + { + name: "Myanmar", + ranges: [[4096, 4256], [43488, 43519], [43616, 43648]], + direction: "ltr", + year: 984, + living: true, + link: "https://en.wikipedia.org/wiki/Burmese_alphabet" + }, + { + name: "Old North Arabian", + ranges: [[68224, 68256]], + direction: "rtl", + year: 750, + living: false, + link: "https://en.wikipedia.org/wiki/Ancient_North_Arabian" + }, + { + name: "Nabataean", + ranges: [[67712, 67743], [67751, 67760]], + direction: "rtl", + year: 150, + living: false, + link: "https://en.wikipedia.org/wiki/Nabataean_alphabet" + }, + { + name: "Newa", + ranges: [[70656, 70746], [70747, 70748], [70749, 70750]], + direction: "ltr", + year: 1000, + living: true, + link: "https://en.wikipedia.org/wiki/Prachalit_Nepal_alphabet" + }, + { + name: "Nko", + ranges: [[1984, 2043]], + direction: "rtl", + year: 1949, + living: false, + link: "https://en.wikipedia.org/wiki/N%27Ko_alphabet" + }, + { + name: "Nushu", + ranges: [[94177, 94178], [110960, 111356]], + direction: "ltr", + year: 1500, + living: true, + link: "https://en.wikipedia.org/wiki/N%C3%BCshu_script" + }, + { + name: "Ogham", + ranges: [[5760, 5789]], + direction: "ltr", + year: 350, + living: false, + link: "https://en.wikipedia.org/wiki/Ogham" + }, + { + name: "Ol Chiki", + ranges: [[7248, 7296]], + direction: "ltr", + year: 1925, + living: true, + link: "https://en.wikipedia.org/wiki/Ol_Chiki_script" + }, + { + name: "Old Turkic", + ranges: [[68608, 68681]], + direction: "rtl", + year: 750, + living: false, + link: "https://en.wikipedia.org/wiki/Old_Turkic_alphabet" + }, + { + name: "Oriya", + ranges: [[2817, 2820], [2821, 2829], [2831, 2833], [2835, 2857], [2858, 2865], [2866, 2868], [2869, 2874], [2876, 2885], [2887, 2889], [2891, 2894], [2902, 2904], [2908, 2910], [2911, 2916], [2918, 2936]], + direction: "ltr", + year: 1060, + living: true, + link: "https://en.wikipedia.org/wiki/Odia_alphabet" + }, + { + name: "Osage", + ranges: [[66736, 66772], [66776, 66812]], + direction: "ltr", + year: 2006, + living: true, + link: "https://en.wikipedia.org/wiki/Osage_alphabet" + }, + { + name: "Osmanya", + ranges: [[66688, 66718], [66720, 66730]], + direction: "ltr", + year: 1920, + living: true, + link: "https://en.wikipedia.org/wiki/Osmanya_alphabet" + }, + { + name: "Palmyrene", + ranges: [[67680, 67712]], + direction: "rtl", + year: -100, + living: false, + link: "https://en.wikipedia.org/wiki/Palmyrene_alphabet" + }, + { + name: "Pau Cin Hau", + ranges: [[72384, 72441]], + direction: "ltr", + year: 1900, + living: true, + link: "https://en.wikipedia.org/wiki/Pau_Cin_Hau" + }, + { + name: "Old Permic", + ranges: [[66384, 66427]], + direction: "ltr", + year: 1372, + living: false, + link: "https://en.wikipedia.org/wiki/Old_Permic_alphabet" + }, + { + name: "Phags-pa", + ranges: [[43072, 43123], [43124, 43127]], + direction: "ttb", + year: 1269, + living: false, + link: "https://en.wikipedia.org/wiki/%27Phags-pa_script" + }, + { + name: "Inscriptional Pahlavi", + ranges: [[68448, 68467], [68472, 68480]], + direction: "rtl", + year: -171, + living: false, + link: "https://en.wikipedia.org/wiki/Inscriptional_Pahlavi" + }, + { + name: "Psalter Pahlavi", + ranges: [[68480, 68498], [68505, 68509], [68521, 68528]], + direction: "rtl", + year: 550, + living: false, + link: "https://en.wikipedia.org/wiki/Psalter_Pahlavi" + }, + { + name: "Phoenician", + ranges: [[67840, 67868], [67871, 67872]], + direction: "rtl", + year: -1200, + living: false, + link: "https://en.wikipedia.org/wiki/Phoenician_alphabet" + }, + { + name: "Miao", + ranges: [[93952, 94021], [94032, 94079], [94095, 94112]], + direction: "ltr", + year: 1936, + living: true, + link: "https://en.wikipedia.org/wiki/Pollard_script" + }, + { + name: "Inscriptional Parthian", + ranges: [[68416, 68438], [68440, 68448]], + direction: "rtl", + year: -250, + living: false, + link: "https://en.wikipedia.org/wiki/Inscriptional_Parthian" + }, + { + name: "Rejang", + ranges: [[43312, 43348], [43359, 43360]], + direction: "ltr", + year: 1750, + living: true, + link: "https://en.wikipedia.org/wiki/Rejang_script" + }, + { + name: "Runic", + ranges: [[5792, 5867], [5870, 5881]], + direction: "ltr", + year: 150, + living: false, + link: "https://en.wikipedia.org/wiki/Runes" + }, + { + name: "Samaritan", + ranges: [[2048, 2094], [2096, 2111]], + direction: "rtl", + year: -600, + living: true, + link: "https://en.wikipedia.org/wiki/Samaritan_alphabet" + }, + { + name: "Old South Arabian", + ranges: [[68192, 68224]], + direction: "rtl", + year: -850, + living: false, + link: "https://en.wikipedia.org/wiki/Ancient_South_Arabian_script" + }, + { + name: "Saurashtra", + ranges: [[43136, 43206], [43214, 43226]], + direction: "ltr", + year: 1920, + living: true, + link: "https://en.wikipedia.org/wiki/Saurashtra_alphabet" + }, + { + name: "SignWriting", + ranges: [[120832, 121484], [121499, 121504], [121505, 121520]], + direction: "ttb", + year: 1974, + living: true, + link: "https://en.wikipedia.org/wiki/SignWriting" + }, + { + name: "Shavian", + ranges: [[66640, 66688]], + direction: "ltr", + year: 1960, + living: true, + link: "https://en.wikipedia.org/wiki/Shavian_alphabet" + }, + { + name: "Sharada", + ranges: [[70016, 70094], [70096, 70112]], + direction: "ltr", + year: 800, + living: true, + link: "https://en.wikipedia.org/wiki/%C5%9A%C4%81rad%C4%81_script" + }, + { + name: "Siddham", + ranges: [[71040, 71094], [71096, 71134]], + direction: "ltr", + year: 550, + living: false, + link: "https://en.wikipedia.org/wiki/Siddha%E1%B9%83_script" + }, + { + name: "Khudawadi", + ranges: [[70320, 70379], [70384, 70394]], + direction: "ltr", + year: 1550, + living: true, + link: "https://en.wikipedia.org/wiki/Khudabadi_script" + }, + { + name: "Sinhala", + ranges: [[3458, 3460], [3461, 3479], [3482, 3506], [3507, 3516], [3517, 3518], [3520, 3527], [3530, 3531], [3535, 3541], [3542, 3543], [3544, 3552], [3558, 3568], [3570, 3573], [70113, 70133]], + direction: "ltr", + year: 700, + living: true, + link: "https://en.wikipedia.org/wiki/Sinhalese_alphabet" + }, + { + name: "Sora Sompeng", + ranges: [[69840, 69865], [69872, 69882]], + direction: "ltr", + year: 1936, + living: true, + link: "https://en.wikipedia.org/wiki/Sorang_Sompeng_alphabet" + }, + { + name: "Soyombo", + ranges: [[72272, 72324], [72326, 72349], [72350, 72355]], + direction: "ltr", + year: 1650, + living: false, + link: "https://en.wikipedia.org/wiki/Soyombo_alphabet" + }, + { + name: "Sundanese", + ranges: [[7040, 7104], [7360, 7368]], + direction: "ltr", + year: 1350, + living: true, + link: "https://en.wikipedia.org/wiki/Sundanese_script" + }, + { + name: "Syloti Nagri", + ranges: [[43008, 43052]], + direction: "ltr", + year: 1303, + living: true, + link: "https://en.wikipedia.org/wiki/Sylheti_Nagari" + }, + { + name: "Syriac", + ranges: [[1792, 1806], [1807, 1867], [1869, 1872], [2144, 2155]], + direction: "rtl", + year: -200, + living: true, + link: "https://en.wikipedia.org/wiki/Syriac_alphabet" + }, + { + name: "Tagbanwa", + ranges: [[5984, 5997], [5998, 6001], [6002, 6004]], + direction: "ltr", + year: 1300, + living: true, + link: "https://en.wikipedia.org/wiki/Tagbanwa_script" + }, + { + name: "Takri", + ranges: [[71296, 71352], [71360, 71370]], + direction: "ltr", + year: 1550, + living: true, + link: "https://en.wikipedia.org/wiki/Takri_alphabet" + }, + { + name: "Tai Le", + ranges: [[6480, 6510], [6512, 6517]], + direction: "ltr", + year: 1200, + living: true, + link: "https://en.wikipedia.org/wiki/Tai_Le_alphabet" + }, + { + name: "New Tai Lue", + ranges: [[6528, 6572], [6576, 6602], [6608, 6619], [6622, 6624]], + direction: "ltr", + year: 1950, + living: true, + link: "https://en.wikipedia.org/wiki/New_Tai_Lue_alphabet" + }, + { + name: "Tamil", + ranges: [[2946, 2948], [2949, 2955], [2958, 2961], [2962, 2966], [2969, 2971], [2972, 2973], [2974, 2976], [2979, 2981], [2984, 2987], [2990, 3002], [3006, 3011], [3014, 3017], [3018, 3022], [3024, 3025], [3031, 3032], [3046, 3067]], + direction: "ltr", + year: 700, + living: true, + link: "https://en.wikipedia.org/wiki/Tamil_script" + }, + { + name: "Tangut", + ranges: [[94176, 94177], [94208, 100333], [100352, 101107]], + direction: "ltr", + year: 1036, + living: false, + link: "https://en.wikipedia.org/wiki/Tangut_script" + }, + { + name: "Tai Viet", + ranges: [[43648, 43715], [43739, 43744]], + direction: "ltr", + year: 1200, + living: true, + link: "https://en.wikipedia.org/wiki/Tai_Dam_language#Writing_system" + }, + { + name: "Telugu", + ranges: [[3072, 3076], [3077, 3085], [3086, 3089], [3090, 3113], [3114, 3130], [3133, 3141], [3142, 3145], [3146, 3150], [3157, 3159], [3160, 3163], [3168, 3172], [3174, 3184], [3192, 3200]], + direction: "ltr", + year: -900, + living: true, + link: "https://en.wikipedia.org/wiki/Telugu_script" + }, + { + name: "Tifinagh", + ranges: [[11568, 11624], [11631, 11633], [11647, 11648]], + direction: "ltr", + year: -300, + living: true, + link: "https://en.wikipedia.org/wiki/Tifinagh" + }, + { + name: "Tagalog", + ranges: [[5888, 5901], [5902, 5909]], + direction: "ltr", + year: 1250, + living: true, + link: "https://en.wikipedia.org/wiki/Baybayin" + }, + { + name: "Thaana", + ranges: [[1920, 1970]], + direction: "rtl", + year: 1599, + living: true, + link: "https://en.wikipedia.org/wiki/Thaana" + }, + { + name: "Thai", + ranges: [[3585, 3643], [3648, 3676]], + direction: "ltr", + year: 1283, + living: true, + link: "https://en.wikipedia.org/wiki/Thai_alphabet" + }, + { + name: "Tibetan", + ranges: [[3840, 3912], [3913, 3949], [3953, 3992], [3993, 4029], [4030, 4045], [4046, 4053], [4057, 4059]], + direction: "ltr", + year: 650, + living: false, + link: "https://en.wikipedia.org/wiki/Tibetan_alphabet" + }, + { + name: "Tirhuta", + ranges: [[70784, 70856], [70864, 70874]], + direction: "ltr", + year: 1450, + living: true, + link: "https://en.wikipedia.org/wiki/Tirhuta" + }, + { + name: "Ugaritic", + ranges: [[66432, 66462], [66463, 66464]], + direction: "ltr", + year: -1400, + living: false, + link: "https://en.wikipedia.org/wiki/Ugaritic_alphabet" + }, + { + name: "Vai", + ranges: [[42240, 42540]], + direction: "ltr", + year: 1830, + living: true, + link: "https://en.wikipedia.org/wiki/Vai_syllabary" + }, + { + name: "Warang Citi", + ranges: [[71840, 71923], [71935, 71936]], + direction: "ltr", + year: 1946, + living: true, + link: "https://en.wikipedia.org/wiki/Warang_Citi" + }, + { + name: "Old Persian", + ranges: [[66464, 66500], [66504, 66518]], + direction: "ltr", + year: -525, + living: false, + link: "https://en.wikipedia.org/wiki/Old_Persian_cuneiform" + }, + { + name: "Cuneiform", + ranges: [[73728, 74650], [74752, 74863], [74864, 74869], [74880, 75076]], + direction: "ltr", + year: -3050, + living: false, + link: "https://en.wikipedia.org/wiki/Cuneiform_script" + }, + { + name: "Yi", + ranges: [[40960, 42125], [42128, 42183]], + direction: "ltr", + year: 1450, + living: true, + link: "https://en.wikipedia.org/wiki/Yi_script" + }, + { + name: "Zanabazar Square", + ranges: [[72192, 72264]], + direction: "ltr", + year: 1700, + living: false, + link: "https://en.wikipedia.org/wiki/Mongolian_writing_systems#Horizontal_square_script" + } +]; + +// This makes sure the data is exported in node.js — +// `require('./path/to/scripts.js')` will get you the array. +if (typeof module != "undefined" && module.exports && (typeof window == "undefined" || window.exports != exports)) + module.exports = SCRIPTS; +if (typeof global != "undefined" && !global.SCRIPTS) + global.SCRIPTS = SCRIPTS; diff --git a/docs/code/skillsharing.zip b/docs/code/skillsharing.zip new file mode 100644 index 000000000..ba5b0652c Binary files /dev/null and b/docs/code/skillsharing.zip differ diff --git a/docs/code/skillsharing/.keep b/docs/code/skillsharing/.keep new file mode 100644 index 000000000..e69de29bb diff --git a/docs/code/skillsharing/package.json b/docs/code/skillsharing/package.json new file mode 100644 index 000000000..03181f034 --- /dev/null +++ b/docs/code/skillsharing/package.json @@ -0,0 +1,23 @@ +{ + "name": "ejs-skillsharing", + "version": "1.0.0", + "main": "skillsharing_server.js", + "description": "Skill-sharing website example from Eloquent JavaScript", + "dependencies": { + "ecstatic": "^3.1.0" + }, + "license": "MIT", + "bugs": "https://github.com/marijnh/Eloquent-JavaScript/issues", + "homepage": "https://eloquentjavascript.net/21_skillsharing.html", + "maintainers": [ + { + "name": "Marijn Haverbeke", + "email": "marijnh@gmail.com", + "web": "https://marijnhaverbeke.nl/" + } + ], + "repository": { + "type": "git", + "url": "https://github.com/marijnh/Eloquent-JavaScript.git" + } +} diff --git a/docs/code/skillsharing/public/index.html b/docs/code/skillsharing/public/index.html new file mode 100644 index 000000000..47f1fbb5c --- /dev/null +++ b/docs/code/skillsharing/public/index.html @@ -0,0 +1,8 @@ + + +Skill Sharing + + +

      Skill Sharing

      + + diff --git a/docs/code/skillsharing/public/skillsharing.css b/docs/code/skillsharing/public/skillsharing.css new file mode 100644 index 000000000..75da06bbc --- /dev/null +++ b/docs/code/skillsharing/public/skillsharing.css @@ -0,0 +1,11 @@ +.talk { margin: 40px 0; } + +.comment { font-style: italic; margin: 0; } +.comment strong { font-style: normal; } + +.talk h2 { font-size: 130%; margin-bottom: 0; } +.talk h2 button { vertical-align: bottom; } + +h1, h3 { margin-bottom: 0.33em; } + +label input { display: block; width: 30em; } diff --git a/docs/code/skillsharing/public/skillsharing_client.js b/docs/code/skillsharing/public/skillsharing_client.js new file mode 100644 index 000000000..9ae7b80c0 --- /dev/null +++ b/docs/code/skillsharing/public/skillsharing_client.js @@ -0,0 +1,178 @@ +function handleAction(state, action) { + if (action.type == "setUser") { + localStorage.setItem("userName", action.user); + return Object.assign({}, state, {user: action.user}); + } else if (action.type == "setTalks") { + return Object.assign({}, state, {talks: action.talks}); + } else if (action.type == "newTalk") { + fetchOK(talkURL(action.title), { + method: "PUT", + headers: {"Content-Type": "application/json"}, + body: JSON.stringify({ + presenter: state.user, + summary: action.summary + }) + }).catch(reportError); + } else if (action.type == "deleteTalk") { + fetchOK(talkURL(action.talk), {method: "DELETE"}) + .catch(reportError); + } else if (action.type == "newComment") { + fetchOK(talkURL(action.talk) + "/comments", { + method: "POST", + headers: {"Content-Type": "application/json"}, + body: JSON.stringify({ + author: state.user, + message: action.message + }) + }).catch(reportError); + } + return state; +} + +function fetchOK(url, options) { + return fetch(url, options).then(response => { + if (response.status < 400) return response; + else throw new Error(response.statusText); + }); +} + +function talkURL(title) { + return "talks/" + encodeURIComponent(title); +} + +function reportError(error) { + alert(String(error)); +} + +function renderUserField(name, dispatch) { + return elt("label", {}, "Your name: ", elt("input", { + type: "text", + value: name, + onchange(event) { + dispatch({type: "setUser", user: event.target.value}); + } + })); +} + +function elt(type, props, ...children) { + let dom = document.createElement(type); + if (props) Object.assign(dom, props); + for (let child of children) { + if (typeof child != "string") dom.appendChild(child); + else dom.appendChild(document.createTextNode(child)); + } + return dom; +} + +function renderTalk(talk, dispatch) { + return elt( + "section", {className: "talk"}, + elt("h2", null, talk.title, " ", elt("button", { + type: "button", + onclick() { + dispatch({type: "deleteTalk", talk: talk.title}); + } + }, "Delete")), + elt("div", null, "by ", + elt("strong", null, talk.presenter)), + elt("p", null, talk.summary), + ...talk.comments.map(renderComment), + elt("form", { + onsubmit(event) { + event.preventDefault(); + let form = event.target; + dispatch({type: "newComment", + talk: talk.title, + message: form.elements.comment.value}); + form.reset(); + } + }, elt("input", {type: "text", name: "comment"}), " ", + elt("button", {type: "submit"}, "Add comment"))); +} + +function renderComment(comment) { + return elt("p", {className: "comment"}, + elt("strong", null, comment.author), + ": ", comment.message); +} + +function renderTalkForm(dispatch) { + let title = elt("input", {type: "text"}); + let summary = elt("input", {type: "text"}); + return elt("form", { + onsubmit(event) { + event.preventDefault(); + dispatch({type: "newTalk", + title: title.value, + summary: summary.value}); + event.target.reset(); + } + }, elt("h3", null, "Submit a Talk"), + elt("label", null, "Title: ", title), + elt("label", null, "Summary: ", summary), + elt("button", {type: "submit"}, "Submit")); +} + +async function pollTalks(update) { + let tag = undefined; + for (;;) { + let response; + try { + response = await fetchOK("/talks", { + headers: tag && {"If-None-Match": tag, + "Prefer": "wait=90"} + }); + } catch (e) { + console.log("Request failed: " + e); + await new Promise(resolve => setTimeout(resolve, 500)); + continue; + } + if (response.status == 304) continue; + tag = response.headers.get("ETag"); + update(await response.json()); + } +} + +var SkillShareApp = class SkillShareApp { + constructor(state, dispatch) { + this.dispatch = dispatch; + this.talkDOM = elt("div", {className: "talks"}); + this.dom = elt("div", null, + renderUserField(state.user, dispatch), + this.talkDOM, + renderTalkForm(dispatch)); + this.syncState(state); + } + + syncState(state) { + if (state.talks != this.talks) { + this.talkDOM.textContent = ""; + for (let talk of state.talks) { + this.talkDOM.appendChild( + renderTalk(talk, this.dispatch)); + } + this.talks = state.talks; + } + } +} + +function runApp() { + let user = localStorage.getItem("userName") || "Anon"; + let state, app; + function dispatch(action) { + state = handleAction(state, action); + app.syncState(state); + } + + pollTalks(talks => { + if (!app) { + state = {user, talks}; + app = new SkillShareApp(state, dispatch); + document.body.appendChild(app.dom); + } else { + dispatch({type: "setTalks", talks}); + } + }).catch(reportError); +} + +runApp(); diff --git a/docs/code/skillsharing/router.js b/docs/code/skillsharing/router.js new file mode 100644 index 000000000..522a94da7 --- /dev/null +++ b/docs/code/skillsharing/router.js @@ -0,0 +1,21 @@ +var {parse} = require("url"); + +module.exports = class Router { + constructor() { + this.routes = []; + } + add(method, url, handler) { + this.routes.push({method, url, handler}); + } + resolve(context, request) { + let path = parse(request.url).pathname; + + for (let {method, url, handler} of this.routes) { + let match = url.exec(path); + if (!match || request.method != method) continue; + let urlParts = match.slice(1).map(decodeURIComponent); + return handler(context, ...urlParts, request); + } + return null; + } +}; diff --git a/docs/code/skillsharing/skillsharing_server.js b/docs/code/skillsharing/skillsharing_server.js new file mode 100644 index 000000000..8012a5f41 --- /dev/null +++ b/docs/code/skillsharing/skillsharing_server.js @@ -0,0 +1,150 @@ +var {createServer} = require("http"); +var Router = require("./router"); +var ecstatic = require("ecstatic"); + +var router = new Router(); +var defaultHeaders = {"Content-Type": "text/plain"}; + +var SkillShareServer = class SkillShareServer { + constructor(talks) { + this.talks = talks; + this.version = 0; + this.waiting = []; + + let fileServer = ecstatic({root: "./public"}); + this.server = createServer((request, response) => { + let resolved = router.resolve(this, request); + if (resolved) { + resolved.catch(error => { + if (error.status != null) return error; + return {body: String(error), status: 500}; + }).then(({body, + status = 200, + headers = defaultHeaders}) => { + response.writeHead(status, headers); + response.end(body); + }); + } else { + fileServer(request, response); + } + }); + } + start(port) { + this.server.listen(port); + } + stop() { + this.server.close(); + } +} + +const talkPath = /^\/talks\/([^\/]+)$/; + +router.add("GET", talkPath, async (server, title) => { + if (title in server.talks) { + return {body: JSON.stringify(server.talks[title]), + headers: {"Content-Type": "application/json"}}; + } else { + return {status: 404, body: `No talk '${title}' found`}; + } +}); + +router.add("DELETE", talkPath, async (server, title) => { + if (title in server.talks) { + delete server.talks[title]; + server.updated(); + } + return {status: 204}; +}); + +function readStream(stream) { + return new Promise((resolve, reject) => { + let data = ""; + stream.on("error", reject); + stream.on("data", chunk => data += chunk.toString()); + stream.on("end", () => resolve(data)); + }); +} + +router.add("PUT", talkPath, + async (server, title, request) => { + let requestBody = await readStream(request); + let talk; + try { talk = JSON.parse(requestBody); } + catch (_) { return {status: 400, body: "Invalid JSON"}; } + + if (!talk || + typeof talk.presenter != "string" || + typeof talk.summary != "string") { + return {status: 400, body: "Bad talk data"}; + } + server.talks[title] = {title, + presenter: talk.presenter, + summary: talk.summary, + comments: []}; + server.updated(); + return {status: 204}; +}); + +router.add("POST", /^\/talks\/([^\/]+)\/comments$/, + async (server, title, request) => { + let requestBody = await readStream(request); + let comment; + try { comment = JSON.parse(requestBody); } + catch (_) { return {status: 400, body: "Invalid JSON"}; } + + if (!comment || + typeof comment.author != "string" || + typeof comment.message != "string") { + return {status: 400, body: "Bad comment data"}; + } else if (title in server.talks) { + server.talks[title].comments.push(comment); + server.updated(); + return {status: 204}; + } else { + return {status: 404, body: `No talk '${title}' found`}; + } +}); + +SkillShareServer.prototype.talkResponse = function() { + let talks = []; + for (let title of Object.keys(this.talks)) { + talks.push(this.talks[title]); + } + return { + body: JSON.stringify(talks), + headers: {"Content-Type": "application/json", + "ETag": `"${this.version}"`} + }; +}; + +router.add("GET", /^\/talks$/, async (server, request) => { + let tag = /"(.*)"/.exec(request.headers["if-none-match"]); + let wait = /\bwait=(\d+)/.exec(request.headers["prefer"]); + if (!tag || tag[1] != server.version) { + return server.talkResponse(); + } else if (!wait) { + return {status: 304}; + } else { + return server.waitForChanges(Number(wait[1])); + } +}); + +SkillShareServer.prototype.waitForChanges = function(time) { + return new Promise(resolve => { + this.waiting.push(resolve); + setTimeout(() => { + if (!this.waiting.includes(resolve)) return; + this.waiting = this.waiting.filter(r => r != resolve); + resolve({status: 304}); + }, time * 1000); + }); +}; + +SkillShareServer.prototype.updated = function() { + this.version++; + let response = this.talkResponse(); + this.waiting.forEach(resolve => resolve(response)); + this.waiting = []; +}; + +new SkillShareServer(Object.create(null)).start(8000); diff --git a/docs/code/solutions/02_1_looping_a_triangle.js b/docs/code/solutions/02_1_looping_a_triangle.js new file mode 100644 index 000000000..2a7ba5f15 --- /dev/null +++ b/docs/code/solutions/02_1_looping_a_triangle.js @@ -0,0 +1,2 @@ +for (let line = "#"; line.length < 8; line += "#") + console.log(line); diff --git a/docs/code/solutions/02_2_fizzbuzz.js b/docs/code/solutions/02_2_fizzbuzz.js new file mode 100644 index 000000000..67138a2a3 --- /dev/null +++ b/docs/code/solutions/02_2_fizzbuzz.js @@ -0,0 +1,6 @@ +for (let n = 1; n <= 100; n++) { + let output = ""; + if (n % 3 == 0) output += "Fizz"; + if (n % 5 == 0) output += "Buzz"; + console.log(output || n); +} diff --git a/docs/code/solutions/02_3_chessboard.js b/docs/code/solutions/02_3_chessboard.js new file mode 100644 index 000000000..0d70bac9b --- /dev/null +++ b/docs/code/solutions/02_3_chessboard.js @@ -0,0 +1,16 @@ +let size = 8; + +let board = ""; + +for (let y = 0; y < size; y++) { + for (let x = 0; x < size; x++) { + if ((x + y) % 2 == 0) { + board += " "; + } else { + board += "#"; + } + } + board += "\n"; +} + +console.log(board); diff --git a/docs/code/solutions/03_1_minimum.js b/docs/code/solutions/03_1_minimum.js new file mode 100644 index 000000000..6954b43a9 --- /dev/null +++ b/docs/code/solutions/03_1_minimum.js @@ -0,0 +1,9 @@ +function min(a, b) { + if (a < b) return a; + else return b; +} + +console.log(min(0, 10)); +// → 0 +console.log(min(0, -10)); +// → -10 diff --git a/docs/code/solutions/03_2_recursion.js b/docs/code/solutions/03_2_recursion.js new file mode 100644 index 000000000..db94f41e4 --- /dev/null +++ b/docs/code/solutions/03_2_recursion.js @@ -0,0 +1,13 @@ +function isEven(n) { + if (n == 0) return true; + else if (n == 1) return false; + else if (n < 0) return isEven(-n); + else return isEven(n - 2); +} + +console.log(isEven(50)); +// → true +console.log(isEven(75)); +// → false +console.log(isEven(-1)); +// → false diff --git a/docs/code/solutions/03_3_bean_counting.js b/docs/code/solutions/03_3_bean_counting.js new file mode 100644 index 000000000..02b04cb86 --- /dev/null +++ b/docs/code/solutions/03_3_bean_counting.js @@ -0,0 +1,18 @@ +function countChar(string, ch) { + let counted = 0; + for (let i = 0; i < string.length; i++) { + if (string[i] == ch) { + counted += 1; + } + } + return counted; +} + +function countBs(string) { + return countChar(string, "B"); +} + +console.log(countBs("BBC")); +// → 2 +console.log(countChar("kakkerlak", "k")); +// → 4 diff --git a/docs/code/solutions/04_1_the_sum_of_a_range.js b/docs/code/solutions/04_1_the_sum_of_a_range.js new file mode 100644 index 000000000..d5b502990 --- /dev/null +++ b/docs/code/solutions/04_1_the_sum_of_a_range.js @@ -0,0 +1,25 @@ +function range(start, end, step = start < end ? 1 : -1) { + let array = []; + + if (step > 0) { + for (let i = start; i <= end; i += step) array.push(i); + } else { + for (let i = start; i >= end; i += step) array.push(i); + } + return array; +} + +function sum(array) { + let total = 0; + for (let value of array) { + total += value; + } + return total; +} + +console.log(range(1, 10)) +// → [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] +console.log(range(5, 2, -1)); +// → [5, 4, 3, 2] +console.log(sum(range(1, 10))); +// → 55 diff --git a/docs/code/solutions/04_2_reversing_an_array.js b/docs/code/solutions/04_2_reversing_an_array.js new file mode 100644 index 000000000..442dde8af --- /dev/null +++ b/docs/code/solutions/04_2_reversing_an_array.js @@ -0,0 +1,23 @@ +function reverseArray(array) { + let output = []; + for (let i = array.length - 1; i >= 0; i--) { + output.push(array[i]); + } + return output; +} + +function reverseArrayInPlace(array) { + for (let i = 0; i < Math.floor(array.length / 2); i++) { + let old = array[i]; + array[i] = array[array.length - 1 - i]; + array[array.length - 1 - i] = old; + } + return array; +} + +console.log(reverseArray(["A", "B", "C"])); +// → ["C", "B", "A"]; +let arrayValue = [1, 2, 3, 4, 5]; +reverseArrayInPlace(arrayValue); +console.log(arrayValue); +// → [5, 4, 3, 2, 1] diff --git a/docs/code/solutions/04_3_a_list.js b/docs/code/solutions/04_3_a_list.js new file mode 100644 index 000000000..bf93ac075 --- /dev/null +++ b/docs/code/solutions/04_3_a_list.js @@ -0,0 +1,34 @@ +function arrayToList(array) { + let list = null; + for (let i = array.length - 1; i >= 0; i--) { + list = {value: array[i], rest: list}; + } + return list; +} + +function listToArray(list) { + let array = []; + for (let node = list; node; node = node.rest) { + array.push(node.value); + } + return array; +} + +function prepend(value, list) { + return {value, rest: list}; +} + +function nth(list, n) { + if (!list) return undefined; + else if (n == 0) return list.value; + else return nth(list.rest, n - 1); +} + +console.log(arrayToList([10, 20])); +// → {value: 10, rest: {value: 20, rest: null}} +console.log(listToArray(arrayToList([10, 20, 30]))); +// → [10, 20, 30] +console.log(prepend(10, prepend(20, null))); +// → {value: 10, rest: {value: 20, rest: null}} +console.log(nth(arrayToList([10, 20, 30]), 1)); +// → 20 diff --git a/docs/code/solutions/04_4_deep_comparison.js b/docs/code/solutions/04_4_deep_comparison.js new file mode 100644 index 000000000..bd3fde9b1 --- /dev/null +++ b/docs/code/solutions/04_4_deep_comparison.js @@ -0,0 +1,24 @@ +function deepEqual(a, b) { + if (a === b) return true; + + if (a == null || typeof a != "object" || + b == null || typeof b != "object") return false; + + let keysA = Object.keys(a), keysB = Object.keys(b); + + if (keysA.length != keysB.length) return false; + + for (let key of keysA) { + if (!keysB.includes(key) || !deepEqual(a[key], b[key])) return false; + } + + return true; +} + +let obj = {here: {is: "an"}, object: 2}; +console.log(deepEqual(obj, obj)); +// → true +console.log(deepEqual(obj, {here: 1, object: 2})); +// → false +console.log(deepEqual(obj, {here: {is: "an"}, object: 2})); +// → true diff --git a/docs/code/solutions/05_1_flattening.js b/docs/code/solutions/05_1_flattening.js new file mode 100644 index 000000000..fbd6b0fed --- /dev/null +++ b/docs/code/solutions/05_1_flattening.js @@ -0,0 +1,4 @@ +let arrays = [[1, 2, 3], [4, 5], [6]]; + +console.log(arrays.reduce((flat, current) => flat.concat(current), [])); +// → [1, 2, 3, 4, 5, 6] diff --git a/docs/code/solutions/05_2_your_own_loop.js b/docs/code/solutions/05_2_your_own_loop.js new file mode 100644 index 000000000..55a715c43 --- /dev/null +++ b/docs/code/solutions/05_2_your_own_loop.js @@ -0,0 +1,10 @@ +function loop(start, test, update, body) { + for (let value = start; test(value); value = update(value)) { + body(value); + } +} + +loop(3, n => n > 0, n => n - 1, console.log); +// → 3 +// → 2 +// → 1 diff --git a/docs/code/solutions/05_3_everything.js b/docs/code/solutions/05_3_everything.js new file mode 100644 index 000000000..08e0e533a --- /dev/null +++ b/docs/code/solutions/05_3_everything.js @@ -0,0 +1,17 @@ +function every(array, predicate) { + for (let element of array) { + if (!predicate(element)) return false; + } + return true; +} + +function every2(array, predicate) { + return !array.some(element => !predicate(element)); +} + +console.log(every([1, 3, 5], n => n < 10)); +// → true +console.log(every([2, 4, 16], n => n < 10)); +// → false +console.log(every([], n => n < 10)); +// → true diff --git a/docs/code/solutions/05_4_dominant_writing_direction.js b/docs/code/solutions/05_4_dominant_writing_direction.js new file mode 100644 index 000000000..1b5494981 --- /dev/null +++ b/docs/code/solutions/05_4_dominant_writing_direction.js @@ -0,0 +1,15 @@ +function dominantDirection(text) { + let counted = countBy(text, char => { + let script = characterScript(char.codePointAt(0)); + return script ? script.direction : "none"; + }).filter(({name}) => name != "none"); + + if (counted.length == 0) return "ltr"; + + return counted.reduce((a, b) => a.count > b.count ? a : b).name; +} + +console.log(dominantDirection("Hello!")); +// → ltr +console.log(dominantDirection("Hey, مساء الخير")); +// → rtl diff --git a/docs/code/solutions/06_1_a_vector_type.js b/docs/code/solutions/06_1_a_vector_type.js new file mode 100644 index 000000000..ee676c10e --- /dev/null +++ b/docs/code/solutions/06_1_a_vector_type.js @@ -0,0 +1,25 @@ +class Vec { + constructor(x, y) { + this.x = x; + this.y = y; + } + + plus(other) { + return new Vec(this.x + other.x, this.y + other.y); + } + + minus(other) { + return new Vec(this.x - other.x, this.y - other.y); + } + + get length() { + return Math.sqrt(this.x * this.x + this.y * this.y); + } +} + +console.log(new Vec(1, 2).plus(new Vec(2, 3))); +// → Vec{x: 3, y: 5} +console.log(new Vec(1, 2).minus(new Vec(2, 3))); +// → Vec{x: -1, y: -1} +console.log(new Vec(3, 4).length); +// → 5 diff --git a/docs/code/solutions/06_2_groups.js b/docs/code/solutions/06_2_groups.js new file mode 100644 index 000000000..e1c4cb3c9 --- /dev/null +++ b/docs/code/solutions/06_2_groups.js @@ -0,0 +1,36 @@ +class Group { + constructor() { + this.members = []; + } + + add(value) { + if (!this.has(value)) { + this.members.push(value); + } + } + + delete(value) { + this.members = this.members.filter(v => v !== value); + } + + has(value) { + return this.members.includes(value); + } + + static from(collection) { + let group = new Group; + for (let value of collection) { + group.add(value); + } + return group; + } +} + +let group = Group.from([10, 20]); +console.log(group.has(10)); +// → true +console.log(group.has(30)); +// → false +group.add(10); +group.delete(10); +console.log(group.has(10)); diff --git a/docs/code/solutions/06_3_iterable_groups.js b/docs/code/solutions/06_3_iterable_groups.js new file mode 100644 index 000000000..202d98ebe --- /dev/null +++ b/docs/code/solutions/06_3_iterable_groups.js @@ -0,0 +1,56 @@ +class Group { + constructor() { + this.members = []; + } + + add(value) { + if (!this.has(value)) { + this.members.push(value); + } + } + + delete(value) { + this.members = this.members.filter(v => v !== value); + } + + has(value) { + return this.members.includes(value); + } + + static from(collection) { + let group = new Group; + for (let value of collection) { + group.add(value); + } + return group; + } + + [Symbol.iterator]() { + return new GroupIterator(this); + } +} + +class GroupIterator { + constructor(group) { + this.group = group; + this.position = 0; + } + + next() { + if (this.position >= this.group.members.length) { + return {done: true}; + } else { + let result = {value: this.group.members[this.position], + done: false}; + this.position++; + return result; + } + } +} + +for (let value of Group.from(["a", "b", "c"])) { + console.log(value); +} +// → a +// → b +// → c diff --git a/docs/code/solutions/06_4_borrowing_a_method.js b/docs/code/solutions/06_4_borrowing_a_method.js new file mode 100644 index 000000000..29543894e --- /dev/null +++ b/docs/code/solutions/06_4_borrowing_a_method.js @@ -0,0 +1,4 @@ +let map = {one: true, two: true, hasOwnProperty: true}; + +console.log(Object.prototype.hasOwnProperty.call(map, "one")); +// → true diff --git a/docs/code/solutions/07_1_measuring_a_robot.js b/docs/code/solutions/07_1_measuring_a_robot.js new file mode 100644 index 000000000..dc95bbcee --- /dev/null +++ b/docs/code/solutions/07_1_measuring_a_robot.js @@ -0,0 +1,21 @@ +function countSteps(state, robot, memory) { + for (let steps = 0;; steps++) { + if (state.parcels.length == 0) return steps; + let action = robot(state, memory); + state = state.move(action.direction); + memory = action.memory; + } +} + +function compareRobots(robot1, memory1, robot2, memory2) { + let total1 = 0, total2 = 0; + for (let i = 0; i < 100; i++) { + let state = VillageState.random(); + total1 += countSteps(state, robot1, memory1); + total2 += countSteps(state, robot2, memory2); + } + console.log(`Robot 1 needed ${total1 / 100} steps per task`) + console.log(`Robot 2 needed ${total2 / 100}`) +} + +compareRobots(routeRobot, [], goalOrientedRobot, []); diff --git a/docs/code/solutions/07_2_robot_efficiency.js b/docs/code/solutions/07_2_robot_efficiency.js new file mode 100644 index 000000000..5d2184aa1 --- /dev/null +++ b/docs/code/solutions/07_2_robot_efficiency.js @@ -0,0 +1,26 @@ +function lazyRobot({place, parcels}, route) { + if (route.length == 0) { + // Describe a route for every parcel + let routes = parcels.map(parcel => { + if (parcel.place != place) { + return {route: findRoute(roadGraph, place, parcel.place), + pickUp: true}; + } else { + return {route: findRoute(roadGraph, place, parcel.address), + pickUp: false}; + } + }); + + // This determines the precedence a route gets when choosing. + // Route length counts negatively, routes that pick up a package + // get a small bonus. + function score({route, pickUp}) { + return (pickUp ? 0.5 : 0) - route.length; + } + route = routes.reduce((a, b) => score(a) > score(b) ? a : b).route; + } + + return {direction: route[0], memory: route.slice(1)}; +} + +runRobotAnimation(VillageState.random(), lazyRobot, []); diff --git a/docs/code/solutions/07_3_persistent_group.js b/docs/code/solutions/07_3_persistent_group.js new file mode 100644 index 000000000..33020584c --- /dev/null +++ b/docs/code/solutions/07_3_persistent_group.js @@ -0,0 +1,32 @@ +class PGroup { + constructor(members) { + this.members = members; + } + + add(value) { + if (this.has(value)) return this; + return new PGroup(this.members.concat([value])); + } + + delete(value) { + if (!this.has(value)) return this; + return new PGroup(this.members.filter(m => m !== value)); + } + + has(value) { + return this.members.includes(value); + } +} + +PGroup.empty = new PGroup([]); + +let a = PGroup.empty.add("a"); +let ab = a.add("b"); +let b = ab.delete("a"); + +console.log(b.has("b")); +// → true +console.log(a.has("b")); +// → false +console.log(b.has("a")); +// → false diff --git a/docs/code/solutions/08_1_retry.js b/docs/code/solutions/08_1_retry.js new file mode 100644 index 000000000..f2a7ae10c --- /dev/null +++ b/docs/code/solutions/08_1_retry.js @@ -0,0 +1,23 @@ +class MultiplicatorUnitFailure extends Error {} + +function primitiveMultiply(a, b) { + if (Math.random() < 0.2) { + return a * b; + } else { + throw new MultiplicatorUnitFailure("Klunk"); + } +} + +function reliableMultiply(a, b) { + for (;;) { + try { + return primitiveMultiply(a, b); + } catch (e) { + if (!(e instanceof MultiplicatorUnitFailure)) + throw e; + } + } +} + +console.log(reliableMultiply(8, 8)); +// → 64 diff --git a/docs/code/solutions/08_2_the_locked_box.js b/docs/code/solutions/08_2_the_locked_box.js new file mode 100644 index 000000000..6a36be3f4 --- /dev/null +++ b/docs/code/solutions/08_2_the_locked_box.js @@ -0,0 +1,39 @@ +const box = { + locked: true, + unlock() { this.locked = false; }, + lock() { this.locked = true; }, + _content: [], + get content() { + if (this.locked) throw new Error("Locked!"); + return this._content; + } +}; + +function withBoxUnlocked(body) { + let locked = box.locked; + if (!locked) { + return body(); + } + + box.unlock(); + try { + return body(); + } finally { + box.lock(); + } +} + +withBoxUnlocked(function() { + box.content.push("gold piece"); +}); + +try { + withBoxUnlocked(function() { + throw new Error("Pirates on the horizon! Abort!"); + }); +} catch (e) { + console.log("Error raised:", e); +} + +console.log(box.locked); +// → true diff --git a/docs/code/solutions/09_1_regexp_golf.js b/docs/code/solutions/09_1_regexp_golf.js new file mode 100644 index 000000000..72a6af16a --- /dev/null +++ b/docs/code/solutions/09_1_regexp_golf.js @@ -0,0 +1,41 @@ +// Fill in the regular expressions + +verify(/ca[rt]/, + ["my car", "bad cats"], + ["camper", "high art"]); + +verify(/pr?op/, + ["pop culture", "mad props"], + ["plop", "prrrop"]); + +verify(/ferr(et|y|ari)/, + ["ferret", "ferry", "ferrari"], + ["ferrum", "transfer A"]); + +verify(/ious\b/, + ["how delicious", "spacious room"], + ["ruinous", "consciousness"]); + +verify(/\s[.,:;]/, + ["bad punctuation ."], + ["escape the dot"]); + +verify(/\w{7}/, + ["hottentottententen"], + ["no", "hotten totten tenten"]); + +verify(/\b[^\We]+\b/i, + ["red platypus", "wobbling nest"], + ["earth bed", "learning ape", "BEET"]); + + +function verify(regexp, yes, no) { + // Ignore unfinished exercises + if (regexp.source == "...") return; + for (let str of yes) if (!regexp.test(str)) { + console.log(`Failure to match '${str}'`); + } + for (let str of no) if (regexp.test(str)) { + console.log(`Unexpected match for '${str}'`); + } +} diff --git a/docs/code/solutions/09_2_quoting_style.js b/docs/code/solutions/09_2_quoting_style.js new file mode 100644 index 000000000..f5b0f8934 --- /dev/null +++ b/docs/code/solutions/09_2_quoting_style.js @@ -0,0 +1,5 @@ +let text = "'I'm the cook,' he said, 'it's my job.'"; + +console.log(text.replace(/(^|\W)'|'(\W|$)/g, '$1"$2')); +// → "I'm the cook," he said, "it's my job." + diff --git a/docs/code/solutions/09_3_numbers_again.js b/docs/code/solutions/09_3_numbers_again.js new file mode 100644 index 000000000..34691f449 --- /dev/null +++ b/docs/code/solutions/09_3_numbers_again.js @@ -0,0 +1,16 @@ +// Fill in this regular expression. +let number = /^[+\-]?(\d+(\.\d*)?|\.\d+)([eE][+\-]?\d+)?$/; + +// Tests: +for (let str of ["1", "-1", "+15", "1.55", ".5", "5.", + "1.3e2", "1E-4", "1e+12"]) { + if (!number.test(str)) { + console.log(`Failed to match '${str}'`); + } +} +for (let str of ["1a", "+-1", "1.2.3", "1+1", "1e4.5", + ".5.", "1f5", "."]) { + if (number.test(str)) { + console.log(`Incorrectly accepted '${str}'`); + } +} diff --git a/docs/code/solutions/10_2_roads_module.js b/docs/code/solutions/10_2_roads_module.js new file mode 100644 index 000000000..ec83e579b --- /dev/null +++ b/docs/code/solutions/10_2_roads_module.js @@ -0,0 +1,13 @@ +const {buildGraph} = require("./graph"); + +const roads = [ + "Alice's House-Bob's House", "Alice's House-Cabin", + "Alice's House-Post Office", "Bob's House-Town Hall", + "Daria's House-Ernie's House", "Daria's House-Town Hall", + "Ernie's House-Grete's House", "Grete's House-Farm", + "Grete's House-Shop", "Marketplace-Farm", + "Marketplace-Post Office", "Marketplace-Shop", + "Marketplace-Town Hall", "Shop-Town Hall" +]; + +exports.roadGraph = buildGraph(roads.map(r => r.split("-"))); diff --git a/docs/code/solutions/11_1_tracking_the_scalpel.js b/docs/code/solutions/11_1_tracking_the_scalpel.js new file mode 100644 index 000000000..cd74b5119 --- /dev/null +++ b/docs/code/solutions/11_1_tracking_the_scalpel.js @@ -0,0 +1,23 @@ +async function locateScalpel(nest) { + let current = nest.name; + for (;;) { + let next = await anyStorage(nest, current, "scalpel"); + if (next == current) return current; + current = next; + } +} + +function locateScalpel2(nest) { + function loop(current) { + return anyStorage(nest, current, "scalpel").then(next => { + if (next == current) return current; + else return loop(next); + }); + } + return loop(nest.name); +} + +locateScalpel(bigOak).then(console.log); +// → Butcher's Shop +locateScalpel2(bigOak).then(console.log); +// → Butcher's Shop diff --git a/docs/code/solutions/11_2_building_promiseall.js b/docs/code/solutions/11_2_building_promiseall.js new file mode 100644 index 000000000..9e8e1465e --- /dev/null +++ b/docs/code/solutions/11_2_building_promiseall.js @@ -0,0 +1,34 @@ +function Promise_all(promises) { + return new Promise((resolve, reject) => { + let results = []; + let pending = promises.length; + for (let i = 0; i < promises.length; i++) { + promises[i].then(result => { + results[i] = result; + pending--; + if (pending == 0) resolve(results); + }).catch(reject); + } + if (promises.length == 0) resolve(results); + }); +} + +// Test code. +Promise_all([]).then(array => { + console.log("This should be []:", array); +}); +function soon(val) { + return new Promise(resolve => { + setTimeout(() => resolve(val), Math.random() * 500); + }); +} +Promise_all([soon(1), soon(2), soon(3)]).then(array => { + console.log("This should be [1, 2, 3]:", array); +}); +Promise_all([soon(1), Promise.reject("X"), soon(3)]).then(array => { + console.log("We should not get here"); +}).catch(error => { + if (error != "X") { + console.log("Unexpected failure:", error); + } +}); diff --git a/docs/code/solutions/12_1_arrays.js b/docs/code/solutions/12_1_arrays.js new file mode 100644 index 000000000..e857c914b --- /dev/null +++ b/docs/code/solutions/12_1_arrays.js @@ -0,0 +1,17 @@ +topScope.array = (...values) => values; + +topScope.length = array => array.length; + +topScope.element = (array, i) => array[i]; + +run(` +do(define(sum, fun(array, + do(define(i, 0), + define(sum, 0), + while(<(i, length(array)), + do(define(sum, +(sum, element(array, i))), + define(i, +(i, 1)))), + sum))), + print(sum(array(1, 2, 3)))) +`); +// → 6 diff --git a/docs/code/solutions/12_3_comments.js b/docs/code/solutions/12_3_comments.js new file mode 100644 index 000000000..7afc21a9e --- /dev/null +++ b/docs/code/solutions/12_3_comments.js @@ -0,0 +1,12 @@ +function skipSpace(string) { + let skippable = string.match(/^(\s|#.*)*/); + return string.slice(skippable[0].length); +} + +console.log(parse("# hello\nx")); +// → {type: "word", name: "x"} + +console.log(parse("a # one\n # two\n()")); +// → {type: "apply", +// operator: {type: "word", name: "a"}, +// args: []} diff --git a/docs/code/solutions/12_4_fixing_scope.js b/docs/code/solutions/12_4_fixing_scope.js new file mode 100644 index 000000000..1b4c6e723 --- /dev/null +++ b/docs/code/solutions/12_4_fixing_scope.js @@ -0,0 +1,25 @@ +specialForms.set = (args, env) => { + if (args.length != 2 || args[0].type != "word") { + throw new SyntaxError("Bad use of set"); + } + let varName = args[0].name; + let value = evaluate(args[1], env); + + for (let scope = env; scope; scope = Object.getPrototypeOf(scope)) { + if (Object.prototype.hasOwnProperty.call(scope, varName)) { + scope[varName] = value; + return value; + } + } + throw new ReferenceError(`Setting undefined variable ${varName}`); +}; + +run(` +do(define(x, 4), + define(setx, fun(val, set(x, val))), + setx(50), + print(x)) +`); +// → 50 +run(`set(quux, true)`); +// → Some kind of ReferenceError diff --git a/docs/code/solutions/14_1_build_a_table.html b/docs/code/solutions/14_1_build_a_table.html new file mode 100644 index 000000000..4df385bd3 --- /dev/null +++ b/docs/code/solutions/14_1_build_a_table.html @@ -0,0 +1,49 @@ + + + +

      Mountains

      + +
      + + diff --git a/docs/code/solutions/14_2_elements_by_tag_name.html b/docs/code/solutions/14_2_elements_by_tag_name.html new file mode 100644 index 000000000..45e9dbc3b --- /dev/null +++ b/docs/code/solutions/14_2_elements_by_tag_name.html @@ -0,0 +1,33 @@ + + +

      Heading with a span element.

      +

      A paragraph with one, two + spans.

      + + diff --git a/docs/code/solutions/14_3_the_cats_hat.html b/docs/code/solutions/14_3_the_cats_hat.html new file mode 100644 index 000000000..92d7444a4 --- /dev/null +++ b/docs/code/solutions/14_3_the_cats_hat.html @@ -0,0 +1,27 @@ + + + + + + + + + + diff --git a/docs/code/solutions/15_1_balloon.html b/docs/code/solutions/15_1_balloon.html new file mode 100644 index 000000000..8adfeb9a1 --- /dev/null +++ b/docs/code/solutions/15_1_balloon.html @@ -0,0 +1,29 @@ + + +

      🎈

      + + diff --git a/docs/code/solutions/15_2_mouse_trail.html b/docs/code/solutions/15_2_mouse_trail.html new file mode 100644 index 000000000..1e9e48855 --- /dev/null +++ b/docs/code/solutions/15_2_mouse_trail.html @@ -0,0 +1,33 @@ + + + + + + + diff --git a/docs/code/solutions/15_3_tabs.html b/docs/code/solutions/15_3_tabs.html new file mode 100644 index 000000000..04edba1d2 --- /dev/null +++ b/docs/code/solutions/15_3_tabs.html @@ -0,0 +1,33 @@ + + + +
      Tab one
      +
      Tab two
      +
      Tab three
      +
      + diff --git a/docs/code/solutions/16_1_game_over.html b/docs/code/solutions/16_1_game_over.html new file mode 100644 index 000000000..5b6579300 --- /dev/null +++ b/docs/code/solutions/16_1_game_over.html @@ -0,0 +1,29 @@ + + + + + + + + + + + diff --git a/docs/code/solutions/16_2_pausing_the_game.html b/docs/code/solutions/16_2_pausing_the_game.html new file mode 100644 index 000000000..756261f33 --- /dev/null +++ b/docs/code/solutions/16_2_pausing_the_game.html @@ -0,0 +1,93 @@ + + + + + + + + + + + diff --git a/docs/code/solutions/16_3_a_monster.html b/docs/code/solutions/16_3_a_monster.html new file mode 100644 index 000000000..a9ef810aa --- /dev/null +++ b/docs/code/solutions/16_3_a_monster.html @@ -0,0 +1,62 @@ + + + + + + + + + + + + + diff --git a/docs/code/solutions/17_1_shapes.html b/docs/code/solutions/17_1_shapes.html new file mode 100644 index 000000000..f5d6fb481 --- /dev/null +++ b/docs/code/solutions/17_1_shapes.html @@ -0,0 +1,66 @@ + + + + diff --git a/docs/code/solutions/17_2_the_pie_chart.html b/docs/code/solutions/17_2_the_pie_chart.html new file mode 100644 index 000000000..c3ac4e408 --- /dev/null +++ b/docs/code/solutions/17_2_the_pie_chart.html @@ -0,0 +1,40 @@ + + + + + + + diff --git a/docs/code/solutions/17_3_a_bouncing_ball.html b/docs/code/solutions/17_3_a_bouncing_ball.html new file mode 100644 index 000000000..afd954165 --- /dev/null +++ b/docs/code/solutions/17_3_a_bouncing_ball.html @@ -0,0 +1,36 @@ + + + + diff --git a/docs/code/solutions/18_1_content_negotiation.js b/docs/code/solutions/18_1_content_negotiation.js new file mode 100644 index 000000000..bd203e052 --- /dev/null +++ b/docs/code/solutions/18_1_content_negotiation.js @@ -0,0 +1,14 @@ +const url = "https://eloquentjavascript.net/author"; +const types = ["text/plain", + "text/html", + "application/json", + "application/rainbows+unicorns"]; + +async function showTypes() { + for (let type of types) { + let resp = await fetch(url, {headers: {accept: type}}); + console.log(`${type}: ${await resp.text()}\n`); + } +} + +showTypes(); diff --git a/docs/code/solutions/18_2_a_javascript_workbench.html b/docs/code/solutions/18_2_a_javascript_workbench.html new file mode 100644 index 000000000..5e087fd76 --- /dev/null +++ b/docs/code/solutions/18_2_a_javascript_workbench.html @@ -0,0 +1,18 @@ + + + + +
      
      +
      +
      diff --git a/docs/code/solutions/18_3_conways_game_of_life.html b/docs/code/solutions/18_3_conways_game_of_life.html
      new file mode 100644
      index 000000000..a32165ac5
      --- /dev/null
      +++ b/docs/code/solutions/18_3_conways_game_of_life.html
      @@ -0,0 +1,89 @@
      +
      +
      +
      + + + + diff --git a/docs/code/solutions/19_1_keyboard_bindings.html b/docs/code/solutions/19_1_keyboard_bindings.html new file mode 100644 index 000000000..0e4b84bb5 --- /dev/null +++ b/docs/code/solutions/19_1_keyboard_bindings.html @@ -0,0 +1,52 @@ + + + + + +
      + diff --git a/docs/code/solutions/19_2_efficient_drawing.html b/docs/code/solutions/19_2_efficient_drawing.html new file mode 100644 index 000000000..c0c8b2f5d --- /dev/null +++ b/docs/code/solutions/19_2_efficient_drawing.html @@ -0,0 +1,37 @@ + + + + + +
      + diff --git a/docs/code/solutions/19_3_circles.html b/docs/code/solutions/19_3_circles.html new file mode 100644 index 000000000..04d96bb19 --- /dev/null +++ b/docs/code/solutions/19_3_circles.html @@ -0,0 +1,34 @@ + + + + + +
      + diff --git a/docs/code/solutions/19_4_proper_lines.html b/docs/code/solutions/19_4_proper_lines.html new file mode 100644 index 000000000..4cb9e2a39 --- /dev/null +++ b/docs/code/solutions/19_4_proper_lines.html @@ -0,0 +1,49 @@ + + + + + +
      + diff --git a/docs/code/solutions/20_1_search_tool.js b/docs/code/solutions/20_1_search_tool.js new file mode 100644 index 000000000..36790d50b --- /dev/null +++ b/docs/code/solutions/20_1_search_tool.js @@ -0,0 +1,18 @@ +const {statSync, readdirSync, readFileSync} = require("fs"); + +let searchTerm = new RegExp(process.argv[2]); + +for (let arg of process.argv.slice(3)) { + search(arg); +} + +function search(file) { + let stats = statSync(file); + if (stats.isDirectory()) { + for (let f of readdirSync(file)) { + search(file + "/" + f); + } + } else if (searchTerm.test(readFileSync(file, "utf8"))) { + console.log(file); + } +} diff --git a/docs/code/solutions/20_2_directory_creation.js b/docs/code/solutions/20_2_directory_creation.js new file mode 100644 index 000000000..741bc127f --- /dev/null +++ b/docs/code/solutions/20_2_directory_creation.js @@ -0,0 +1,18 @@ +// This code won't work on its own, but is also included in the +// code/file_server.js file, which defines the whole system. + +const {mkdir} = require("fs").promises; + +methods.MKCOL = async function(request) { + let path = urlPath(request.url); + let stats; + try { + stats = await stat(path); + } catch (error) { + if (error.code != "ENOENT") throw error; + await mkdir(path); + return {status: 204}; + } + if (stats.isDirectory()) return {status: 204}; + else return {status: 400, body: "Not a directory"}; +}; diff --git a/docs/code/solutions/20_3_a_public_space_on_the_web.zip b/docs/code/solutions/20_3_a_public_space_on_the_web.zip new file mode 100644 index 000000000..c51884ffe Binary files /dev/null and b/docs/code/solutions/20_3_a_public_space_on_the_web.zip differ diff --git a/docs/code/solutions/20_3_a_public_space_on_the_web/index.html b/docs/code/solutions/20_3_a_public_space_on_the_web/index.html new file mode 100644 index 000000000..e6ad51515 --- /dev/null +++ b/docs/code/solutions/20_3_a_public_space_on_the_web/index.html @@ -0,0 +1,17 @@ + + + +

      A Public Space on the Web

      + +

      This is a self-editing website. Select a file, edit it, and save to +update the website.

      + +

      Files:

      + +

      + +
      + +

      + + diff --git a/docs/code/solutions/20_3_a_public_space_on_the_web/other.html b/docs/code/solutions/20_3_a_public_space_on_the_web/other.html new file mode 100644 index 000000000..a9b89b115 --- /dev/null +++ b/docs/code/solutions/20_3_a_public_space_on_the_web/other.html @@ -0,0 +1,4 @@ + + + +

      This is another file

      diff --git a/docs/code/solutions/20_3_a_public_space_on_the_web/public_space.js b/docs/code/solutions/20_3_a_public_space_on_the_web/public_space.js new file mode 100644 index 000000000..ee321fc42 --- /dev/null +++ b/docs/code/solutions/20_3_a_public_space_on_the_web/public_space.js @@ -0,0 +1,31 @@ +// Get a reference to the DOM nodes we need +let filelist = document.querySelector("#filelist"); +let textarea = document.querySelector("#file"); + +// This loads the initial file list from the server +fetch("/").then(resp => resp.text()).then(files => { + for (let file of files.split("\n")) { + let option = document.createElement("option"); + option.textContent = file; + filelist.appendChild(option); + } + // Now that we have a list of files, make sure the textarea contains + // the currently selected one. + loadCurrentFile(); +}); + +// Fetch a file from the server and put it in the textarea. +function loadCurrentFile() { + fetch(filelist.value).then(resp => resp.text()).then(file => { + textarea.value = file; + }); +} + +filelist.addEventListener("change", loadCurrentFile); + +// Called by the button on the page. Makes a request to save the +// currently selected file. +function saveFile() { + fetch(filelist.value, {method: "PUT", + body: textarea.value}); +} diff --git a/docs/code/solutions/21_1_disk_persistence.js b/docs/code/solutions/21_1_disk_persistence.js new file mode 100644 index 000000000..8d7c36c6f --- /dev/null +++ b/docs/code/solutions/21_1_disk_persistence.js @@ -0,0 +1,30 @@ +// This isn't a stand-alone file, only a redefinition of a few +// fragments from skillsharing/skillsharing_server.js + +const {readFileSync, writeFile} = require("fs"); + +const fileName = "./talks.json"; + +function loadTalks() { + let json; + try { + json = JSON.parse(readFileSync(fileName, "utf8")); + } catch (e) { + json = {}; + } + return Object.assign(Object.create(null), json); +} + +SkillShareServer.prototype.updated = function() { + this.version++; + let response = this.talkResponse(); + this.waiting.forEach(resolve => resolve(response)); + this.waiting = []; + + writeFile(fileName, JSON.stringify(this.talks), e => { + if (e) throw e; + }); +}; + +// The line that starts the server must be changed to +new SkillShareServer(loadTalks()).start(8000); diff --git a/docs/code/solutions/21_2_comment_field_resets.js b/docs/code/solutions/21_2_comment_field_resets.js new file mode 100644 index 000000000..57bbf1338 --- /dev/null +++ b/docs/code/solutions/21_2_comment_field_resets.js @@ -0,0 +1,76 @@ +// This isn't a stand-alone file, only a redefinition of the main +// component from skillsharing/public/skillsharing_client.js + +class Talk { + constructor(talk, dispatch) { + this.comments = elt("div"); + this.dom = elt( + "section", {className: "talk"}, + elt("h2", null, talk.title, " ", elt("button", { + type: "button", + onclick: () => dispatch({type: "deleteTalk", + talk: talk.title}) + }, "Delete")), + elt("div", null, "by ", + elt("strong", null, talk.presenter)), + elt("p", null, talk.summary), + this.comments, + elt("form", { + onsubmit(event) { + event.preventDefault(); + let form = event.target; + dispatch({type: "newComment", + talk: talk.title, + message: form.elements.comment.value}); + form.reset(); + } + }, elt("input", {type: "text", name: "comment"}), " ", + elt("button", {type: "submit"}, "Add comment"))); + this.syncState(talk); + } + + syncState(talk) { + this.talk = talk; + this.comments.textContent = ""; + for (let comment of talk.comments) { + this.comments.appendChild(renderComment(comment)); + } + } +} + +class SkillShareApp { + constructor(state, dispatch) { + this.dispatch = dispatch; + this.talkDOM = elt("div", {className: "talks"}); + this.talkMap = Object.create(null); + this.dom = elt("div", null, + renderUserField(state.user, dispatch), + this.talkDOM, + renderTalkForm(dispatch)); + this.syncState(state); + } + + syncState(state) { + if (state.talks == this.talks) return; + this.talks = state.talks; + + for (let talk of state.talks) { + let cmp = this.talkMap[talk.title]; + if (cmp && cmp.talk.author == talk.author && + cmp.talk.summary == talk.summary) { + cmp.syncState(talk); + } else { + if (cmp) cmp.dom.remove(); + cmp = new Talk(talk, this.dispatch); + this.talkMap[talk.title] = cmp; + this.talkDOM.appendChild(cmp.dom); + } + } + for (let title of Object.keys(this.talkMap)) { + if (!state.talks.some(talk => talk.title == title)) { + this.talkMap[title].dom.remove(); + delete this.talkMap[title]; + } + } + } +} diff --git a/docs/code/solutions/22_1_pathfinding.js b/docs/code/solutions/22_1_pathfinding.js new file mode 100644 index 000000000..b9907a859 --- /dev/null +++ b/docs/code/solutions/22_1_pathfinding.js @@ -0,0 +1,21 @@ +function findPath(a, b) { + let work = [[a]]; + for (let path of work) { + let end = path[path.length - 1]; + if (end == b) return path; + for (let next of end.edges) { + if (!work.some(path => path[path.length - 1] == next)) { + work.push(path.concat([next])); + } + } + } +} + +let graph = treeGraph(4, 4); +let root = graph[0], leaf = graph[graph.length - 1]; +console.log(findPath(root, leaf).length); +// → 4 + +leaf.connect(root); +console.log(findPath(root, leaf).length); +// → 2 diff --git a/docs/code/solutions/22_2_timing.js b/docs/code/solutions/22_2_timing.js new file mode 100644 index 000000000..d37c83806 --- /dev/null +++ b/docs/code/solutions/22_2_timing.js @@ -0,0 +1,20 @@ +function findPath(a, b) { + let work = [[a]]; + for (let path of work) { + let end = path[path.length - 1]; + if (end == b) return path; + for (let next of end.edges) { + if (!work.some(path => path[path.length - 1] == next)) { + work.push(path.concat([next])); + } + } + } +} + +function time(findPath) { + let graph = treeGraph(6, 6); + let startTime = Date.now(); + let result = findPath(graph[0], graph[graph.length - 1]); + console.log(`Path with length ${result.length} found in ${Date.now() - startTime}ms`); +} +time(findPath); diff --git a/docs/code/solutions/22_3_optimizing.js b/docs/code/solutions/22_3_optimizing.js new file mode 100644 index 000000000..dff729050 --- /dev/null +++ b/docs/code/solutions/22_3_optimizing.js @@ -0,0 +1,45 @@ +function time(findPath) { + let graph = treeGraph(6, 6); + let startTime = Date.now(); + let result = findPath(graph[0], graph[graph.length - 1]); + console.log(`Path with length ${result.length} found in ${Date.now() - startTime}ms`); +} + +function findPath_set(a, b) { + let work = [[a]]; + let reached = new Set([a]); + for (let path of work) { + let end = path[path.length - 1]; + if (end == b) return path; + for (let next of end.edges) { + if (!reached.has(next)) { + reached.add(next); + work.push(path.concat([next])); + } + } + } +} + +time(findPath_set); + +function pathToArray(path) { + let result = []; + for (; path; path = path.via) result.unshift(path.at); + return result; +} + +function findPath_list(a, b) { + let work = [{at: a, via: null}]; + let reached = new Set([a]); + for (let path of work) { + if (path.at == b) return pathToArray(path); + for (let next of path.at.edges) { + if (!reached.has(next)) { + reached.add(next); + work.push({at: next, via: path}); + } + } + } +} + +time(findPath_list); diff --git a/docs/code/squareworker.js b/docs/code/squareworker.js new file mode 100644 index 000000000..58a5bed9e --- /dev/null +++ b/docs/code/squareworker.js @@ -0,0 +1,3 @@ +addEventListener("message", event => { + postMessage(event.data * event.data); +}); diff --git a/docs/css/ejs.css b/docs/css/ejs.css new file mode 100644 index 000000000..7b3dfca8e --- /dev/null +++ b/docs/css/ejs.css @@ -0,0 +1,461 @@ +@font-face { + font-family: 'Cinzel'; + font-style: normal; + font-weight: 700; + src: local('Cinzel-Bold'), url(../font/cinzel_bold.woff) format('woff'); +} + +@font-face { + font-family: 'PT Mono'; + font-style: normal; + font-weight: 400; + src: local('PT Mono'), local('PTMono-Regular'), url(../font/pt_mono.woff) format('woff'); +} + +html, body { + padding: 0; + margin: 0; +} + +body { + font-family: Georgia, 'Nimbus Roman No9 L', 'Century Schoolbook L', serif; + font-size: 20px; + line-height: 1.45; + color: black; + background: white; +} + +article { + margin: 0 auto; + max-width: 35em; + padding: 2em 1em 5em; + position: relative; + overflow-wrap: break-word; +} + +nav { + display: block; + height: 0; + text-align: right; +} + +nav a { + font-size: 80%; + color: #aaa !important; + text-decoration: none !important; +} + +a.subtlelink { + color: black !important; + text-decoration: none !important; +} + +pre { + padding: 5px 0 5px 15px; + line-height: 1.35; + margin: 1rem 0; + max-width: 100%; + overflow-x: auto; +} + +pre[data-language=javascript] { + cursor: pointer; +} + +p:hover a.p_ident:after, pre:hover a.c_ident:after, h2:hover a.h_ident:after, h3:hover a.i_ident:after { + content: "¶"; + font-family: 'Cinzel', Georgia, serif; + color: #888; + font-size: 17px; + position: absolute; + right: -10px; +} + +@media screen and (max-width: 800px) { + p:hover a.p_ident:after, pre:hover a.c_ident:after, h2:hover a.h_ident:after, h3:hover a.i_ident:after { + right: 5px; + } + + blockquote p:hover a.p_ident:after { + right: -15px; + } +} + + +code, pre, .CodeMirror { + font-size: 18px; + font-family: 'PT Mono', monospace; +} + +code { + padding: 0 2px; +} + +h1, h2, h3 { + font-family: 'Cinzel', Georgia, serif; + font-weight: 700; + margin: 1rem 0; + letter-spacing: 2px; +} + +h1 { + font-size: 130%; +} +h2 { + font-size: 115%; +} +h3 { + font-size: 100%; +} + +pre.cm-s-default, p, h2, h3 { + margin-right: -30px; + padding-right: 30px; +} + +@media screen and (max-width: 800px) { + pre.cm-s-default, p, h2, h3 { + margin-right: 0; + padding-right: 0; + } +} + +a, a:visited, a:active { + text-decoration: none; + color: #467; +} + +a:hover { + text-decoration: underline; +} + +ol { + margin: 1em 0; + padding: 0; + counter-reset: li; +} + +ol li { + margin: 0 0 0 40px; + padding: 0; + list-style: none; + position: relative; +} + +ol li:before { + content: counter(li) "."; + counter-increment: li; + position: absolute; + width: 2em; + text-align: right; + left: -2.5em; top: 1px; + font-size: 90%; +} + +ol li p { + margin: 0; +} + +.chap_num { + font-size: 60%; + color: #aaa; + margin-top: -.7em; + display: block; +} + +blockquote { + margin: 0 0 0 3em; + padding: 0; + position: relative; + font-size: 85%; +} + +blockquote p { + color: #333; +} + +blockquote:before { + content: '“'; + position: absolute; + left: -.5em; +} + +blockquote p:last-of-type:after { + content: '”'; +} + +blockquote footer { + position: relative; + margin-left: 1em; +} + +p + footer { + margin-top: -.5em; +} + +blockquote footer cite { + font-style: italic; +} + +blockquote footer:before { + content: '—'; + position: absolute; + left: -1em; +} + +.editor-wrap { + margin: 1rem 0; + position: relative; + -moz-transition: margin-left .5s ease-out, margin-right .5s ease-out; + -webkit-transition: margin-left .5s ease-out, margin-right .5s ease-out; + -o-transition: margin-left .5s ease-out, margin-right .5s ease-out; + transition: margin-left .5s ease-out, margin-right .5s ease-out; + border-bottom: 1px solid #4ab; +} + +.sandbox-output { + border-top: 1px solid #4ab; + padding: 4px 0 4px 10px; + white-space: pre; + max-height: 25em; + overflow: auto; +} + +.sandbox-output:empty { + display: none; +} + +.editor-wrap iframe { + display: block; + border: 1px dotted #4ab; + border-top: 1px solid #4ab; + border-bottom-width: 0; + padding: 0; margin: 0; + width: 100%; + -moz-box-sizing: border-box; + box-sizing: border-box; +} + +.sandbox-output pre { + margin: 0; + padding: 0; + white-space: pre-wrap; +} + +.sandbox-output-error { color: red; } +.sandbox-output-warn { color: orange; } + +.sandbox-output-etc { + color: #1a1; + background: #dfd; + cursor: pointer; + border-radius: 5px; + padding: 0 1px; +} + +.sandbox-output-prop { + color: #444; +} + +.sandbox-output-etc-block { + display: inline-block; + vertical-align: top; +} + +.sandbox-output-etc-block table { + border-collapse: collapse; +} +.sandbox-output-etc-block table td { + vertical-align: top; + white-space: pre-wrap; + font-family: 'PT Mono', monospace; +} +.sandbox-output-etc-block table td:first-child { + text-align: right; +} + +.sandbox-menu { + position: absolute; + z-index: 19; + right: -13px; top: -1px; + cursor: pointer; + font-size: 80%; + overflow: hidden; + width: 10px; + border-top: 2px solid #4ab; + height: 2px; + border-bottom: 6px double #4ab; +} + +.sandbox-open-menu { + font-family: tahoma, arial, sans-serif; + position: absolute; + background: white; + border: 1px solid #aaa; + box-shadow: 2px 2px 10px rgba(0, 0, 0, .2); + padding: 0; + font-size: 75%; + color: black; + line-height: 1.3; + right: -9px; top: 5px; + z-index: 20; +} + +.sandbox-open-menu div { + cursor: pointer; + padding: 5px 10px; +} + +.sandbox-open-menu div:hover { + background: #bdd; +} + +/* Toned-down CodeMirror style */ + +.cm-s-default .cm-keyword, .sandbox-output-null, .sandbox-output-fun {color: #506;} +.cm-s-default .cm-atom, .sandbox-output-bool {color: #106;} +.cm-s-default .cm-number, .sandbox-output-number {color: #042;} +.cm-s-default .cm-def {color: #009;} +.cm-s-default .cm-variable-2, .cm-s-default .cm-attribute {color: #027;} +.cm-s-default .cm-variable-3 {color: #072;} +.cm-s-default .cm-comment {color: #740;} +.cm-s-default .cm-string, .sandbox-output-string {color: #700;} +.cm-s-default .cm-string-2 {color: #740;} +.cm-s-default .cm-tag, .sandbox-output-symbol {color: #170;} + +.CodeMirror { + height: auto; + line-height: 1.35; + border-top: 1px solid #4ab; + overflow-wrap: normal; +} +.CodeMirror-scroll { + max-height: 700px; +} +.CodeMirror pre { + padding: 0 4px 0 10px; +} +.CodeMirror-gutters { + border: none; + background: white; +} +.CodeMirror-linenumber { + padding: .5em 3px 0 0; + min-width: 12px; + color: #4ab; + font-size: 60%; +} + +.sandboxhint { + position: absolute; + right: -15px; + font-family: tahoma, arial, sans-serif; + font-size: 70%; + padding: 4px 8px; + background: rgb(220, 220, 220); + color: white; + border-radius: 5px; +} + +@media screen and (max-width: 800px) { + .sandboxhint { + right: 5px; + } +} + +.sandboxhint:before { + position: absolute; + width: 0; height: 0; + border-top: 6px solid transparent; + border-bottom: 6px solid transparent; + border-right: 12px solid rgb(220, 220, 220); + top: 6px; + left: -11px; + content: ''; +} + + +figure { + max-width: 640px; + margin: 0 30px; +} + +figure.chapter { + text-align: center; + margin: 3em 0 2em; +} + +figure.chapter img { + max-width: 75%; +} + +figure.framed img { + border-radius: 50%; + border: 4px double #666; +} + +figure.square-framed img { + border-radius: 30px; + border: 4px double #666; +} + +span.keyname { font-variant: small-caps } + +@media screen and (max-width: 500px) { + figure { + margin: 0; + } +} + +figure img { + max-width: 100%; +} + +div.solution:before { + content: "» Display hints..."; +} + +div.solution { + color: #156; + cursor: pointer; +} + +div.solution-text { + display: none; +} + +div.solution.open:before { + content: ""; +} + +div.solution.open { + cursor: default; +} + +div.solution.open div.solution-text { + display: block; +} + +td { + vertical-align: top; +} + +td + td { + padding-left: 1em; +} + +table { + margin-left: 15px; +} + +sub, sup { + line-height: 1; +} + +sub { + font-size: 60%; +} + +sup { + font-size: 70%; +} diff --git a/docs/css/game.css b/docs/css/game.css new file mode 100644 index 000000000..bb777788a --- /dev/null +++ b/docs/css/game.css @@ -0,0 +1,24 @@ +.background { background: rgb(52, 166, 251); + table-layout: fixed; + border-spacing: 0; } +.background td { padding: 0; } +.lava { background: rgb(255, 100, 100); } +.wall { background: white; } + +.actor { position: absolute; } +.coin { background: rgb(241, 229, 89); } +.player { background: rgb(64, 64, 64); } + +.game { + overflow: hidden; + max-width: 600px; + max-height: 450px; + position: relative; +} + +.lost .player { + background: rgb(160, 64, 64); +} +.won .player { + box-shadow: -4px -7px 8px white, 4px -7px 8px white; +} diff --git a/docs/css/paint.css b/docs/css/paint.css new file mode 100644 index 000000000..159faddf4 --- /dev/null +++ b/docs/css/paint.css @@ -0,0 +1,13 @@ +.picturepanel { + width: -webkit-fit-content; + width: -moz-fit-content; + width: -ms-fit-content; + width: fit-content; + max-width: 500px; + max-height: 300px; + border: 2px solid silver; + overflow: auto; + position: relative; +} +.picturepanel canvas { display: block; } +.toolbar > * { margin-right: 5px; } diff --git a/docs/empty.html b/docs/empty.html new file mode 100644 index 000000000..c2a57ea02 --- /dev/null +++ b/docs/empty.html @@ -0,0 +1 @@ + diff --git a/docs/errata.html b/docs/errata.html new file mode 100644 index 000000000..6bb107bef --- /dev/null +++ b/docs/errata.html @@ -0,0 +1,98 @@ + + + + + Eloquent JavaScript :: Errata + + + + + +
      + +

      Errata
      Eloquent JavaScript, 3rd Edition

      + +

      These are the known mistakes in the third edition +of the book. For errata in the first edition, +see this +page. For the second edition, +see this +page. To report a problem that is not listed +here, send me an email.

      + +

      Issues whose page number is followed by an ordinal number are only +present up to the print denoted by that number. I.e. those followed by +“1st” were fixed in the second print.

      + +

      Chapter 2

      + +

      Page 34 (1st) Updating Bindings Succintly: Where it +says counter- it should be counter--.

      + +

      Chapter 5

      + +

      Page 91 Composability: Due to an initial +mistake in the script data set, the results of the computations on +this page differ from the ones with the current, corrected data. The +average year for living scripts should be 1165, the average for +non-living scripts should be 204.

      + +

      Chapter 6

      + +

      Page 111 (2nd) Inheritance: In the second +paragraph below the example code, instead of “content +method”, the text should say “element function”. + +

      Chapter 8

      + +

      Page 134 (2nd) Error Propagation: In the third +paragraph of the section, a function promptInteger is +referred to. The function is actually +called promptNumber, and the word “whole” should be +dropped from the sentence (it accepts non-whole numbers, too).

      + +

      Chapter 10

      + +

      Page 168 (1st) Modules as Building Blocks: In “each +needs it own private scope“, it should say “its own private +scope“.

      + +

      Chapter 14

      + +

      Page 234 (2nd) Creating Nodes: In the +code, “edition” is misspelled as “editon”.

      + +

      Chapter 15

      + +

      Page 258 Load Event: The description of +the beforeunload claims that you just need to return a +string from your event handler. For handlers registered +with addEventListener you, in fact, need to +call preventDefault and set a returnValue +property to get the warn-on-leave behavior.

      + +

      Chapter 16

      + +

      Page 285 (2nd) Pausing the Game: The text +refers to the arrow binding, where it should +say arrowKeys.

      + +

      Chapter 20

      + +

      Page 369 (1st) Directory +Creation: MKCOL stands for “make collection”, not “make +column” as the book claims.

      + +

      Chapter 21

      + +

      Page 373 HTTP Interface: There is a +superfluous closing brace at the end of the example JSON snippet.

      + +

      Exercise Hints

      + +

      Page 414 A Modular Robot: +The dijkstrajs package name is misspelled +as dijkstajs. + +

      diff --git a/docs/example/bert.json b/docs/example/bert.json new file mode 100644 index 000000000..c943c0942 --- /dev/null +++ b/docs/example/bert.json @@ -0,0 +1,4 @@ +{ + "name": "Bert", + "spouse": "example/suzie.json" +} diff --git a/docs/example/data.txt b/docs/example/data.txt new file mode 100644 index 000000000..2a5a69639 --- /dev/null +++ b/docs/example/data.txt @@ -0,0 +1 @@ +This is the content of data.txt diff --git a/docs/example/fruit.json b/docs/example/fruit.json new file mode 100644 index 000000000..b54ea36b7 --- /dev/null +++ b/docs/example/fruit.json @@ -0,0 +1,3 @@ +{"banana": "yellow", + "lemon": "yellow", + "cherry": "red"} diff --git a/docs/example/fruit.xml b/docs/example/fruit.xml new file mode 100644 index 000000000..d932c647a --- /dev/null +++ b/docs/example/fruit.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/docs/example/message.html b/docs/example/message.html new file mode 100644 index 000000000..cf92826f7 --- /dev/null +++ b/docs/example/message.html @@ -0,0 +1,35 @@ + +Example form target + + + + + + +

      (Note that this page is just an illustration. No actual message was delivered anywhere.)

      + + + + diff --git a/docs/example/muriel.json b/docs/example/muriel.json new file mode 100644 index 000000000..921f40d45 --- /dev/null +++ b/docs/example/muriel.json @@ -0,0 +1,3 @@ +{ + "name": "Muriel" +} diff --git a/docs/example/submit.html b/docs/example/submit.html new file mode 100644 index 000000000..6607f2275 --- /dev/null +++ b/docs/example/submit.html @@ -0,0 +1,22 @@ + +Example form target + + + +

      You submitted...

      + +
      
      +
      +
      +
      +
      diff --git a/docs/example/suzie.json b/docs/example/suzie.json
      new file mode 100644
      index 000000000..1e47fac94
      --- /dev/null
      +++ b/docs/example/suzie.json
      @@ -0,0 +1,5 @@
      +{
      +  "name": "Suzie",
      +  "spouse": "example/bert.json",
      +  "mother": "example/muriel.json"
      +}
      diff --git a/docs/favicon.ico b/docs/favicon.ico
      new file mode 100644
      index 000000000..382b70692
      Binary files /dev/null and b/docs/favicon.ico differ
      diff --git a/docs/font/cinzel_bold.woff b/docs/font/cinzel_bold.woff
      new file mode 100644
      index 000000000..e6b4c6c09
      Binary files /dev/null and b/docs/font/cinzel_bold.woff differ
      diff --git a/docs/font/pt_mono.woff b/docs/font/pt_mono.woff
      new file mode 100644
      index 000000000..441549c73
      Binary files /dev/null and b/docs/font/pt_mono.woff differ
      diff --git a/docs/img/Hieres-sur-Amby.png b/docs/img/Hieres-sur-Amby.png
      new file mode 100644
      index 000000000..57721e9da
      Binary files /dev/null and b/docs/img/Hieres-sur-Amby.png differ
      diff --git a/docs/img/blockquote.png b/docs/img/blockquote.png
      new file mode 100644
      index 000000000..a08559251
      Binary files /dev/null and b/docs/img/blockquote.png differ
      diff --git a/docs/img/boxed-in.png b/docs/img/boxed-in.png
      new file mode 100644
      index 000000000..45bbb40df
      Binary files /dev/null and b/docs/img/boxed-in.png differ
      diff --git a/docs/img/button_disabled.png b/docs/img/button_disabled.png
      new file mode 100644
      index 000000000..2e44b2199
      Binary files /dev/null and b/docs/img/button_disabled.png differ
      diff --git a/docs/img/canvas_beziercurve.png b/docs/img/canvas_beziercurve.png
      new file mode 100644
      index 000000000..120e0cafa
      Binary files /dev/null and b/docs/img/canvas_beziercurve.png differ
      diff --git a/docs/img/canvas_circle.png b/docs/img/canvas_circle.png
      new file mode 100644
      index 000000000..ab2d266b3
      Binary files /dev/null and b/docs/img/canvas_circle.png differ
      diff --git a/docs/img/canvas_fill.png b/docs/img/canvas_fill.png
      new file mode 100644
      index 000000000..09965aecc
      Binary files /dev/null and b/docs/img/canvas_fill.png differ
      diff --git a/docs/img/canvas_game.png b/docs/img/canvas_game.png
      new file mode 100644
      index 000000000..611ac8d36
      Binary files /dev/null and b/docs/img/canvas_game.png differ
      diff --git a/docs/img/canvas_path.png b/docs/img/canvas_path.png
      new file mode 100644
      index 000000000..14231f2c7
      Binary files /dev/null and b/docs/img/canvas_path.png differ
      diff --git a/docs/img/canvas_pie_chart.png b/docs/img/canvas_pie_chart.png
      new file mode 100644
      index 000000000..022289cc2
      Binary files /dev/null and b/docs/img/canvas_pie_chart.png differ
      diff --git a/docs/img/canvas_quadraticcurve.png b/docs/img/canvas_quadraticcurve.png
      new file mode 100644
      index 000000000..121cd4a64
      Binary files /dev/null and b/docs/img/canvas_quadraticcurve.png differ
      diff --git a/docs/img/canvas_scale.png b/docs/img/canvas_scale.png
      new file mode 100644
      index 000000000..373b9c361
      Binary files /dev/null and b/docs/img/canvas_scale.png differ
      diff --git a/docs/img/canvas_stroke.png b/docs/img/canvas_stroke.png
      new file mode 100644
      index 000000000..2a4b38247
      Binary files /dev/null and b/docs/img/canvas_stroke.png differ
      diff --git a/docs/img/canvas_tree.png b/docs/img/canvas_tree.png
      new file mode 100644
      index 000000000..f0b301d40
      Binary files /dev/null and b/docs/img/canvas_tree.png differ
      diff --git a/docs/img/canvas_triangle.png b/docs/img/canvas_triangle.png
      new file mode 100644
      index 000000000..50bcd14cd
      Binary files /dev/null and b/docs/img/canvas_triangle.png differ
      diff --git a/docs/img/cat-animation.png b/docs/img/cat-animation.png
      new file mode 100644
      index 000000000..4241c9676
      Binary files /dev/null and b/docs/img/cat-animation.png differ
      diff --git a/docs/img/cat.png b/docs/img/cat.png
      new file mode 100644
      index 000000000..e365386fb
      Binary files /dev/null and b/docs/img/cat.png differ
      diff --git a/docs/img/chapter_picture_00.jpg b/docs/img/chapter_picture_00.jpg
      new file mode 100644
      index 000000000..a3f4730cf
      Binary files /dev/null and b/docs/img/chapter_picture_00.jpg differ
      diff --git a/docs/img/chapter_picture_1.jpg b/docs/img/chapter_picture_1.jpg
      new file mode 100644
      index 000000000..ebc0689f0
      Binary files /dev/null and b/docs/img/chapter_picture_1.jpg differ
      diff --git a/docs/img/chapter_picture_10.jpg b/docs/img/chapter_picture_10.jpg
      new file mode 100644
      index 000000000..40706b183
      Binary files /dev/null and b/docs/img/chapter_picture_10.jpg differ
      diff --git a/docs/img/chapter_picture_11.jpg b/docs/img/chapter_picture_11.jpg
      new file mode 100644
      index 000000000..8b63ef70b
      Binary files /dev/null and b/docs/img/chapter_picture_11.jpg differ
      diff --git a/docs/img/chapter_picture_12.jpg b/docs/img/chapter_picture_12.jpg
      new file mode 100644
      index 000000000..b90572f1c
      Binary files /dev/null and b/docs/img/chapter_picture_12.jpg differ
      diff --git a/docs/img/chapter_picture_13.jpg b/docs/img/chapter_picture_13.jpg
      new file mode 100644
      index 000000000..bc3513a2b
      Binary files /dev/null and b/docs/img/chapter_picture_13.jpg differ
      diff --git a/docs/img/chapter_picture_14.jpg b/docs/img/chapter_picture_14.jpg
      new file mode 100644
      index 000000000..90af0d741
      Binary files /dev/null and b/docs/img/chapter_picture_14.jpg differ
      diff --git a/docs/img/chapter_picture_15.jpg b/docs/img/chapter_picture_15.jpg
      new file mode 100644
      index 000000000..85462c0be
      Binary files /dev/null and b/docs/img/chapter_picture_15.jpg differ
      diff --git a/docs/img/chapter_picture_16.jpg b/docs/img/chapter_picture_16.jpg
      new file mode 100644
      index 000000000..ed2b4e6dd
      Binary files /dev/null and b/docs/img/chapter_picture_16.jpg differ
      diff --git a/docs/img/chapter_picture_17.jpg b/docs/img/chapter_picture_17.jpg
      new file mode 100644
      index 000000000..7445f048d
      Binary files /dev/null and b/docs/img/chapter_picture_17.jpg differ
      diff --git a/docs/img/chapter_picture_18.jpg b/docs/img/chapter_picture_18.jpg
      new file mode 100644
      index 000000000..7c9a93259
      Binary files /dev/null and b/docs/img/chapter_picture_18.jpg differ
      diff --git a/docs/img/chapter_picture_19.jpg b/docs/img/chapter_picture_19.jpg
      new file mode 100644
      index 000000000..742981d29
      Binary files /dev/null and b/docs/img/chapter_picture_19.jpg differ
      diff --git a/docs/img/chapter_picture_2.jpg b/docs/img/chapter_picture_2.jpg
      new file mode 100644
      index 000000000..3563ae5c0
      Binary files /dev/null and b/docs/img/chapter_picture_2.jpg differ
      diff --git a/docs/img/chapter_picture_20.jpg b/docs/img/chapter_picture_20.jpg
      new file mode 100644
      index 000000000..6a487a9e7
      Binary files /dev/null and b/docs/img/chapter_picture_20.jpg differ
      diff --git a/docs/img/chapter_picture_21.jpg b/docs/img/chapter_picture_21.jpg
      new file mode 100644
      index 000000000..834e9c065
      Binary files /dev/null and b/docs/img/chapter_picture_21.jpg differ
      diff --git a/docs/img/chapter_picture_3.jpg b/docs/img/chapter_picture_3.jpg
      new file mode 100644
      index 000000000..53923d98e
      Binary files /dev/null and b/docs/img/chapter_picture_3.jpg differ
      diff --git a/docs/img/chapter_picture_4.jpg b/docs/img/chapter_picture_4.jpg
      new file mode 100644
      index 000000000..9deda2e6f
      Binary files /dev/null and b/docs/img/chapter_picture_4.jpg differ
      diff --git a/docs/img/chapter_picture_5.jpg b/docs/img/chapter_picture_5.jpg
      new file mode 100644
      index 000000000..76658b8bf
      Binary files /dev/null and b/docs/img/chapter_picture_5.jpg differ
      diff --git a/docs/img/chapter_picture_6.jpg b/docs/img/chapter_picture_6.jpg
      new file mode 100644
      index 000000000..8af17dbc5
      Binary files /dev/null and b/docs/img/chapter_picture_6.jpg differ
      diff --git a/docs/img/chapter_picture_7.jpg b/docs/img/chapter_picture_7.jpg
      new file mode 100644
      index 000000000..5154d33aa
      Binary files /dev/null and b/docs/img/chapter_picture_7.jpg differ
      diff --git a/docs/img/chapter_picture_8.jpg b/docs/img/chapter_picture_8.jpg
      new file mode 100644
      index 000000000..e61d44b40
      Binary files /dev/null and b/docs/img/chapter_picture_8.jpg differ
      diff --git a/docs/img/chapter_picture_9.jpg b/docs/img/chapter_picture_9.jpg
      new file mode 100644
      index 000000000..efb340015
      Binary files /dev/null and b/docs/img/chapter_picture_9.jpg differ
      diff --git a/docs/img/color-field.png b/docs/img/color-field.png
      new file mode 100644
      index 000000000..2ee57c267
      Binary files /dev/null and b/docs/img/color-field.png differ
      diff --git a/docs/img/colored-links.png b/docs/img/colored-links.png
      new file mode 100644
      index 000000000..ab4efaf0c
      Binary files /dev/null and b/docs/img/colored-links.png differ
      diff --git a/docs/img/control-io.svg b/docs/img/control-io.svg
      new file mode 100644
      index 000000000..97a6f6142
      --- /dev/null
      +++ b/docs/img/control-io.svg
      @@ -0,0 +1,10 @@
      +
      +
      +synchronous, single thread of controlsynchronous, two threads of controlasynchronous
      diff --git a/docs/img/controlflow-else.svg b/docs/img/controlflow-else.svg
      new file mode 100644
      index 000000000..4faaf6b12
      --- /dev/null
      +++ b/docs/img/controlflow-else.svg
      @@ -0,0 +1,175 @@
      +
      +
      +
      +
      +  
      +    
      +      
      +    
      +    
      +      
      +    
      +    
      +      
      +    
      +    
      +      
      +    
      +  
      +  
      +  
      +    
      +      
      +        image/svg+xml
      +        
      +        
      +      
      +    
      +  
      +  
      +    
      +    
      +    
      +    
      +    
      +    
      +    
      +    
      +  
      +
      diff --git a/docs/img/controlflow-if.svg b/docs/img/controlflow-if.svg
      new file mode 100644
      index 000000000..618e410e6
      --- /dev/null
      +++ b/docs/img/controlflow-if.svg
      @@ -0,0 +1,155 @@
      +
      +
      +
      +
      +  
      +    
      +      
      +    
      +    
      +      
      +    
      +    
      +      
      +    
      +    
      +      
      +    
      +  
      +  
      +  
      +    
      +      
      +        image/svg+xml
      +        
      +        
      +      
      +    
      +  
      +  
      +    
      +      
      +      
      +      
      +      
      +      
      +    
      +  
      +
      diff --git a/docs/img/controlflow-loop.svg b/docs/img/controlflow-loop.svg
      new file mode 100644
      index 000000000..b4ffe3f2f
      --- /dev/null
      +++ b/docs/img/controlflow-loop.svg
      @@ -0,0 +1,147 @@
      +
      +
      +
      +
      +  
      +    
      +      
      +    
      +    
      +      
      +    
      +    
      +      
      +    
      +    
      +      
      +    
      +  
      +  
      +  
      +    
      +      
      +        image/svg+xml
      +        
      +        
      +      
      +    
      +  
      +  
      +    
      +    
      +    
      +    
      +  
      +
      diff --git a/docs/img/controlflow-nested-if.svg b/docs/img/controlflow-nested-if.svg
      new file mode 100644
      index 000000000..f920ba22e
      --- /dev/null
      +++ b/docs/img/controlflow-nested-if.svg
      @@ -0,0 +1,175 @@
      +
      +
      +
      +
      +  
      +    
      +      
      +    
      +    
      +      
      +    
      +    
      +      
      +    
      +    
      +      
      +    
      +  
      +  
      +  
      +    
      +      
      +        image/svg+xml
      +        
      +        
      +      
      +    
      +  
      +  
      +    
      +    
      +    
      +    
      +    
      +    
      +    
      +    
      +  
      +
      diff --git a/docs/img/controlflow-straight.svg b/docs/img/controlflow-straight.svg
      new file mode 100644
      index 000000000..82fe554d6
      --- /dev/null
      +++ b/docs/img/controlflow-straight.svg
      @@ -0,0 +1,81 @@
      +
      +
      +
      +
      +  
      +    
      +      
      +    
      +  
      +  
      +  
      +    
      +      
      +        image/svg+xml
      +        
      +        
      +      
      +    
      +  
      +  
      +    
      +  
      +
      diff --git a/docs/img/cos_sin.svg b/docs/img/cos_sin.svg
      new file mode 100644
      index 000000000..bfde16fb7
      --- /dev/null
      +++ b/docs/img/cos_sin.svg
      @@ -0,0 +1,11 @@
      +
      +
      +cos(¼π)sin(¼π)cos(-⅔π)sin(-⅔π)
      diff --git a/docs/img/cover.jpg b/docs/img/cover.jpg
      new file mode 100644
      index 000000000..8faf78581
      Binary files /dev/null and b/docs/img/cover.jpg differ
      diff --git a/docs/img/darkblue.png b/docs/img/darkblue.png
      new file mode 100644
      index 000000000..3af8c8a80
      Binary files /dev/null and b/docs/img/darkblue.png differ
      diff --git a/docs/img/display.png b/docs/img/display.png
      new file mode 100644
      index 000000000..0e56b8851
      Binary files /dev/null and b/docs/img/display.png differ
      diff --git a/docs/img/drag-bar.png b/docs/img/drag-bar.png
      new file mode 100644
      index 000000000..aebf6f56b
      Binary files /dev/null and b/docs/img/drag-bar.png differ
      diff --git a/docs/img/exercise_shapes.png b/docs/img/exercise_shapes.png
      new file mode 100644
      index 000000000..702899332
      Binary files /dev/null and b/docs/img/exercise_shapes.png differ
      diff --git a/docs/img/flood-grid.svg b/docs/img/flood-grid.svg
      new file mode 100644
      index 000000000..4cb0b075a
      --- /dev/null
      +++ b/docs/img/flood-grid.svg
      @@ -0,0 +1,2 @@
      +
      +
      \ No newline at end of file
      diff --git a/docs/img/form_fields.png b/docs/img/form_fields.png
      new file mode 100644
      index 000000000..667b3b6fa
      Binary files /dev/null and b/docs/img/form_fields.png differ
      diff --git a/docs/img/form_select.png b/docs/img/form_select.png
      new file mode 100644
      index 000000000..1e5f254c3
      Binary files /dev/null and b/docs/img/form_select.png differ
      diff --git a/docs/img/game-grid.svg b/docs/img/game-grid.svg
      new file mode 100644
      index 000000000..954b028f7
      --- /dev/null
      +++ b/docs/img/game-grid.svg
      @@ -0,0 +1,2 @@
      +
      +
      \ No newline at end of file
      diff --git a/docs/img/game_simpleLevel.png b/docs/img/game_simpleLevel.png
      new file mode 100644
      index 000000000..3f00e008f
      Binary files /dev/null and b/docs/img/game_simpleLevel.png differ
      diff --git a/docs/img/generated/.keep b/docs/img/generated/.keep
      new file mode 100644
      index 000000000..e69de29bb
      diff --git a/docs/img/generated/control-io.pdf b/docs/img/generated/control-io.pdf
      new file mode 100644
      index 000000000..f3efa8892
      Binary files /dev/null and b/docs/img/generated/control-io.pdf differ
      diff --git a/docs/img/generated/controlflow-else.pdf b/docs/img/generated/controlflow-else.pdf
      new file mode 100644
      index 000000000..c8c7e7039
      Binary files /dev/null and b/docs/img/generated/controlflow-else.pdf differ
      diff --git a/docs/img/generated/controlflow-if.pdf b/docs/img/generated/controlflow-if.pdf
      new file mode 100644
      index 000000000..4b89888f3
      Binary files /dev/null and b/docs/img/generated/controlflow-if.pdf differ
      diff --git a/docs/img/generated/controlflow-loop.pdf b/docs/img/generated/controlflow-loop.pdf
      new file mode 100644
      index 000000000..beeffc1e3
      Binary files /dev/null and b/docs/img/generated/controlflow-loop.pdf differ
      diff --git a/docs/img/generated/controlflow-nested-if.pdf b/docs/img/generated/controlflow-nested-if.pdf
      new file mode 100644
      index 000000000..61e6adc00
      Binary files /dev/null and b/docs/img/generated/controlflow-nested-if.pdf differ
      diff --git a/docs/img/generated/controlflow-straight.pdf b/docs/img/generated/controlflow-straight.pdf
      new file mode 100644
      index 000000000..97c9e144c
      Binary files /dev/null and b/docs/img/generated/controlflow-straight.pdf differ
      diff --git a/docs/img/generated/cos_sin.pdf b/docs/img/generated/cos_sin.pdf
      new file mode 100644
      index 000000000..60647a803
      Binary files /dev/null and b/docs/img/generated/cos_sin.pdf differ
      diff --git a/docs/img/generated/flood-grid.pdf b/docs/img/generated/flood-grid.pdf
      new file mode 100644
      index 000000000..9489edc45
      Binary files /dev/null and b/docs/img/generated/flood-grid.pdf differ
      diff --git a/docs/img/generated/game-grid.pdf b/docs/img/generated/game-grid.pdf
      new file mode 100644
      index 000000000..44e31bdb4
      Binary files /dev/null and b/docs/img/generated/game-grid.pdf differ
      diff --git a/docs/img/generated/html-boxes.pdf b/docs/img/generated/html-boxes.pdf
      new file mode 100644
      index 000000000..aab4128f7
      Binary files /dev/null and b/docs/img/generated/html-boxes.pdf differ
      diff --git a/docs/img/generated/html-links.pdf b/docs/img/generated/html-links.pdf
      new file mode 100644
      index 000000000..a9381c5a5
      Binary files /dev/null and b/docs/img/generated/html-links.pdf differ
      diff --git a/docs/img/generated/html-tree.pdf b/docs/img/generated/html-tree.pdf
      new file mode 100644
      index 000000000..58e772138
      Binary files /dev/null and b/docs/img/generated/html-tree.pdf differ
      diff --git a/docs/img/generated/line-grid.pdf b/docs/img/generated/line-grid.pdf
      new file mode 100644
      index 000000000..a3dda863c
      Binary files /dev/null and b/docs/img/generated/line-grid.pdf differ
      diff --git a/docs/img/generated/linked-list.pdf b/docs/img/generated/linked-list.pdf
      new file mode 100644
      index 000000000..e8c651ccd
      Binary files /dev/null and b/docs/img/generated/linked-list.pdf differ
      diff --git a/docs/img/generated/mirror.pdf b/docs/img/generated/mirror.pdf
      new file mode 100644
      index 000000000..eece34712
      Binary files /dev/null and b/docs/img/generated/mirror.pdf differ
      diff --git a/docs/img/generated/pizza-squirrel.pdf b/docs/img/generated/pizza-squirrel.pdf
      new file mode 100644
      index 000000000..60d715c74
      Binary files /dev/null and b/docs/img/generated/pizza-squirrel.pdf differ
      diff --git a/docs/img/generated/rabbits.pdf b/docs/img/generated/rabbits.pdf
      new file mode 100644
      index 000000000..070fadb2b
      Binary files /dev/null and b/docs/img/generated/rabbits.pdf differ
      diff --git a/docs/img/generated/re_number.pdf b/docs/img/generated/re_number.pdf
      new file mode 100644
      index 000000000..30481086b
      Binary files /dev/null and b/docs/img/generated/re_number.pdf differ
      diff --git a/docs/img/generated/re_pigchickens.pdf b/docs/img/generated/re_pigchickens.pdf
      new file mode 100644
      index 000000000..93c0bac29
      Binary files /dev/null and b/docs/img/generated/re_pigchickens.pdf differ
      diff --git a/docs/img/generated/re_slow.pdf b/docs/img/generated/re_slow.pdf
      new file mode 100644
      index 000000000..565e0b94e
      Binary files /dev/null and b/docs/img/generated/re_slow.pdf differ
      diff --git a/docs/img/generated/syntax_tree.pdf b/docs/img/generated/syntax_tree.pdf
      new file mode 100644
      index 000000000..72eb44cb4
      Binary files /dev/null and b/docs/img/generated/syntax_tree.pdf differ
      diff --git a/docs/img/generated/transform.pdf b/docs/img/generated/transform.pdf
      new file mode 100644
      index 000000000..0e538de10
      Binary files /dev/null and b/docs/img/generated/transform.pdf differ
      diff --git a/docs/img/generated/unicycle.pdf b/docs/img/generated/unicycle.pdf
      new file mode 100644
      index 000000000..c0dfcb681
      Binary files /dev/null and b/docs/img/generated/unicycle.pdf differ
      diff --git a/docs/img/generated/weresquirrel.pdf b/docs/img/generated/weresquirrel.pdf
      new file mode 100644
      index 000000000..a710709b3
      Binary files /dev/null and b/docs/img/generated/weresquirrel.pdf differ
      diff --git a/docs/img/ghostery.png b/docs/img/ghostery.png
      new file mode 100644
      index 000000000..218102b6a
      Binary files /dev/null and b/docs/img/ghostery.png differ
      diff --git a/docs/img/ghostery_mini.png b/docs/img/ghostery_mini.png
      new file mode 100644
      index 000000000..ed5928305
      Binary files /dev/null and b/docs/img/ghostery_mini.png differ
      diff --git a/docs/img/hack_reactor.png b/docs/img/hack_reactor.png
      new file mode 100644
      index 000000000..4da0c8e0b
      Binary files /dev/null and b/docs/img/hack_reactor.png differ
      diff --git a/docs/img/hack_reactor_mini.png b/docs/img/hack_reactor_mini.png
      new file mode 100644
      index 000000000..5a892c069
      Binary files /dev/null and b/docs/img/hack_reactor_mini.png differ
      diff --git a/docs/img/hat.png b/docs/img/hat.png
      new file mode 100644
      index 000000000..f2889e84c
      Binary files /dev/null and b/docs/img/hat.png differ
      diff --git a/docs/img/help-field.png b/docs/img/help-field.png
      new file mode 100644
      index 000000000..2324da542
      Binary files /dev/null and b/docs/img/help-field.png differ
      diff --git a/docs/img/highlighted.png b/docs/img/highlighted.png
      new file mode 100644
      index 000000000..be41fd6dc
      Binary files /dev/null and b/docs/img/highlighted.png differ
      diff --git a/docs/img/holberton.png b/docs/img/holberton.png
      new file mode 100644
      index 000000000..918b84fdf
      Binary files /dev/null and b/docs/img/holberton.png differ
      diff --git a/docs/img/home-page.png b/docs/img/home-page.png
      new file mode 100644
      index 000000000..42418121a
      Binary files /dev/null and b/docs/img/home-page.png differ
      diff --git a/docs/img/html-boxes.svg b/docs/img/html-boxes.svg
      new file mode 100644
      index 000000000..f985457db
      --- /dev/null
      +++ b/docs/img/html-boxes.svg
      @@ -0,0 +1,23 @@
      +
      +
      +herea.I also wrote a book! Read itpHello, I am Marijn and this is...pMy home pageh1bodyMy home pagetitleheadhtml
      diff --git a/docs/img/html-links.svg b/docs/img/html-links.svg
      new file mode 100644
      index 000000000..1fc0487b5
      --- /dev/null
      +++ b/docs/img/html-links.svg
      @@ -0,0 +1,25 @@
      +
      +
      +I also wrote a book! ...pHello, I am Marijn...pMy home pageh1body012childNodesfirstChildlastChildpreviousSiblingnextSiblingparentNode
      diff --git a/docs/img/html-tree.svg b/docs/img/html-tree.svg
      new file mode 100644
      index 000000000..c6c35108d
      --- /dev/null
      +++ b/docs/img/html-tree.svg
      @@ -0,0 +1,36 @@
      +
      +
      +  
      +    
      +      
      +      
      +    
      +    
      +      
      +      
      +    
      +  
      +htmlheadtitleMy home pagebodyh1My home pagepHello! I am...pI also wrote...herea.
      diff --git a/docs/img/line-grid.svg b/docs/img/line-grid.svg
      new file mode 100644
      index 000000000..95bbf53eb
      --- /dev/null
      +++ b/docs/img/line-grid.svg
      @@ -0,0 +1,2 @@
      +
      +
      \ No newline at end of file
      diff --git a/docs/img/linked-list.svg b/docs/img/linked-list.svg
      new file mode 100644
      index 000000000..9e00738ea
      --- /dev/null
      +++ b/docs/img/linked-list.svg
      @@ -0,0 +1,13 @@
      +
      +
      +value: 1rest:value: 2rest:value: 3rest: null
      diff --git a/docs/img/middle_east_graph.png b/docs/img/middle_east_graph.png
      new file mode 100644
      index 000000000..430e88b14
      Binary files /dev/null and b/docs/img/middle_east_graph.png differ
      diff --git a/docs/img/middle_east_graph_random.png b/docs/img/middle_east_graph_random.png
      new file mode 100644
      index 000000000..c7098f852
      Binary files /dev/null and b/docs/img/middle_east_graph_random.png differ
      diff --git a/docs/img/mirror.svg b/docs/img/mirror.svg
      new file mode 100644
      index 000000000..6f3c6c169
      --- /dev/null
      +++ b/docs/img/mirror.svg
      @@ -0,0 +1,11 @@
      +
      +
      +mirror1234
      diff --git a/docs/img/mozilla.png b/docs/img/mozilla.png
      new file mode 100644
      index 000000000..806dcfcd6
      Binary files /dev/null and b/docs/img/mozilla.png differ
      diff --git a/docs/img/mozilla_mini.png b/docs/img/mozilla_mini.png
      new file mode 100644
      index 000000000..272144250
      Binary files /dev/null and b/docs/img/mozilla_mini.png differ
      diff --git a/docs/img/nextjournal.png b/docs/img/nextjournal.png
      new file mode 100644
      index 000000000..e1bc6ea52
      Binary files /dev/null and b/docs/img/nextjournal.png differ
      diff --git a/docs/img/object.jpg b/docs/img/object.jpg
      new file mode 100644
      index 000000000..d9c24dd60
      Binary files /dev/null and b/docs/img/object.jpg differ
      diff --git a/docs/img/object_full.jpg b/docs/img/object_full.jpg
      new file mode 100644
      index 000000000..3b3c4f12b
      Binary files /dev/null and b/docs/img/object_full.jpg differ
      diff --git a/docs/img/ostrich.png b/docs/img/ostrich.png
      new file mode 100644
      index 000000000..1dbe890a4
      Binary files /dev/null and b/docs/img/ostrich.png differ
      diff --git a/docs/img/parcel2x.png b/docs/img/parcel2x.png
      new file mode 100644
      index 000000000..ff518258a
      Binary files /dev/null and b/docs/img/parcel2x.png differ
      diff --git a/docs/img/pixel_editor.png b/docs/img/pixel_editor.png
      new file mode 100644
      index 000000000..e1c80901f
      Binary files /dev/null and b/docs/img/pixel_editor.png differ
      diff --git a/docs/img/pizza-squirrel.svg b/docs/img/pizza-squirrel.svg
      new file mode 100644
      index 000000000..4cc93317c
      --- /dev/null
      +++ b/docs/img/pizza-squirrel.svg
      @@ -0,0 +1,18 @@
      +
      +
      +  
      +    
      +    
      +    
      +  
      +  
      +    
      +    
      +    
      +    
      +    
      +    
      +    
      +  
      +
      +No squirrel, no pizza76Squirrel, no pizza4No squirrel, pizza9Squirrel, pizza1
      \ No newline at end of file
      diff --git a/docs/img/player.png b/docs/img/player.png
      new file mode 100644
      index 000000000..1244769ac
      Binary files /dev/null and b/docs/img/player.png differ
      diff --git a/docs/img/player_big.png b/docs/img/player_big.png
      new file mode 100644
      index 000000000..de305fcc7
      Binary files /dev/null and b/docs/img/player_big.png differ
      diff --git a/docs/img/prompt.png b/docs/img/prompt.png
      new file mode 100644
      index 000000000..d485a0ef9
      Binary files /dev/null and b/docs/img/prompt.png differ
      diff --git a/docs/img/rabbits.svg b/docs/img/rabbits.svg
      new file mode 100644
      index 000000000..5ee2dac16
      --- /dev/null
      +++ b/docs/img/rabbits.svg
      @@ -0,0 +1,13 @@
      +
      +
      +toString: <function>...teeth: "small"speak: <function>killerRabbitteeth: "long, sharp, ..."type: "killer"RabbitprototypeObjectcreate: <function>prototype...
      diff --git a/docs/img/re_number.svg b/docs/img/re_number.svg
      new file mode 100644
      index 000000000..a2fe450e5
      --- /dev/null
      +++ b/docs/img/re_number.svg
      @@ -0,0 +1,72 @@
      +
      +
      +              
      +                
      +              
      +              
      +                
      +                  
      +                    
      +                    
      +                    
      +                    
      +                    
      +                  
      +                
      +              
      +            Created with Snapword boundarygroup #1One of:01bOne of:digit-afhdigitword boundary
      diff --git a/docs/img/re_pigchickens.svg b/docs/img/re_pigchickens.svg
      new file mode 100644
      index 000000000..4bec21c4d
      --- /dev/null
      +++ b/docs/img/re_pigchickens.svg
      @@ -0,0 +1,307 @@
      +
      +
      +  
      +  
      +  
      +  
      +  
      +  
      +  
      +  
      +  
      +  
      +  
      +  
      +  
      +  
      +  
      +  
      +  
      +  
      +  
      +  Created with Raphaël 2.1.0
      +  
      +  
      +  
      +    " "
      +  
      +  
      +  
      +    
      +  
      +  
      +    boundary
      +  
      +  
      +  
      +    boundary
      +  
      +  
      +  
      +    Group #1
      +  
      +  
      +  
      +    "chicken"
      +  
      +  
      +  
      +    "cow"
      +  
      +  
      +  
      +    "pig"
      +  
      +  
      +  
      +    digit
      +  
      +  
      +  
      +    "s"
      +  
      +  
      +  
      +  
      +  
      +
      diff --git a/docs/img/re_slow.svg b/docs/img/re_slow.svg
      new file mode 100644
      index 000000000..493c9bc99
      --- /dev/null
      +++ b/docs/img/re_slow.svg
      @@ -0,0 +1,175 @@
      +
      +
      +  
      +  
      +  
      +  
      +  
      +  
      +  
      +  
      +  
      +  Created with Raphaël 2.1.0
      +  
      +  
      +  
      +    "b"
      +  
      +  
      +  
      +    Group #1
      +  
      +  
      +  
      +    One of:
      +  
      +  
      +  
      +    "1"
      +  
      +  
      +  
      +    "0"
      +  
      +  
      +  
      +  
      +  
      +
      diff --git a/docs/img/robot_idle.png b/docs/img/robot_idle.png
      new file mode 100644
      index 000000000..605293ef1
      Binary files /dev/null and b/docs/img/robot_idle.png differ
      diff --git a/docs/img/robot_idle2x.png b/docs/img/robot_idle2x.png
      new file mode 100644
      index 000000000..d1b36b960
      Binary files /dev/null and b/docs/img/robot_idle2x.png differ
      diff --git a/docs/img/robot_moving.gif b/docs/img/robot_moving.gif
      new file mode 100644
      index 000000000..d73feb3b3
      Binary files /dev/null and b/docs/img/robot_moving.gif differ
      diff --git a/docs/img/robot_moving2x.gif b/docs/img/robot_moving2x.gif
      new file mode 100644
      index 000000000..3e145bbae
      Binary files /dev/null and b/docs/img/robot_moving2x.gif differ
      diff --git a/docs/img/skillsharing.png b/docs/img/skillsharing.png
      new file mode 100644
      index 000000000..48781637a
      Binary files /dev/null and b/docs/img/skillsharing.png differ
      diff --git a/docs/img/sprites.png b/docs/img/sprites.png
      new file mode 100644
      index 000000000..2a77ab991
      Binary files /dev/null and b/docs/img/sprites.png differ
      diff --git a/docs/img/sprites_big.png b/docs/img/sprites_big.png
      new file mode 100644
      index 000000000..afa6e2c85
      Binary files /dev/null and b/docs/img/sprites_big.png differ
      diff --git a/docs/img/svg-demo.png b/docs/img/svg-demo.png
      new file mode 100644
      index 000000000..1efcff823
      Binary files /dev/null and b/docs/img/svg-demo.png differ
      diff --git a/docs/img/syntax_tree.svg b/docs/img/syntax_tree.svg
      new file mode 100644
      index 000000000..3aab52091
      --- /dev/null
      +++ b/docs/img/syntax_tree.svg
      @@ -0,0 +1,11 @@
      +
      +
      +dodefinex10if>x5print"large"print"small"
      \ No newline at end of file
      diff --git a/docs/img/tamil.png b/docs/img/tamil.png
      new file mode 100644
      index 000000000..dc7591f74
      Binary files /dev/null and b/docs/img/tamil.png differ
      diff --git a/docs/img/transform.svg b/docs/img/transform.svg
      new file mode 100644
      index 000000000..553cd6e9a
      --- /dev/null
      +++ b/docs/img/transform.svg
      @@ -0,0 +1,11 @@
      +
      +
      +translate(50, 50)rotate(0.1*Math.PI)rotate(0.1*Math.PI)translate(50, 50)
      diff --git a/docs/img/tree_graph.png b/docs/img/tree_graph.png
      new file mode 100644
      index 000000000..9f19e8e5a
      Binary files /dev/null and b/docs/img/tree_graph.png differ
      diff --git a/docs/img/unicycle.svg b/docs/img/unicycle.svg
      new file mode 100644
      index 000000000..fb261d0dd
      --- /dev/null
      +++ b/docs/img/unicycle.svg
      @@ -0,0 +1,277 @@
      +
      +
      +
      +image/svg+xml
      \ No newline at end of file
      diff --git a/docs/img/village.png b/docs/img/village.png
      new file mode 100644
      index 000000000..886c8fce1
      Binary files /dev/null and b/docs/img/village.png differ
      diff --git a/docs/img/village2x.png b/docs/img/village2x.png
      new file mode 100644
      index 000000000..8b1d7c765
      Binary files /dev/null and b/docs/img/village2x.png differ
      diff --git a/docs/img/weresquirrel.png b/docs/img/weresquirrel.png
      new file mode 100644
      index 000000000..c24d684c5
      Binary files /dev/null and b/docs/img/weresquirrel.png differ
      diff --git a/docs/img/weresquirrel.svg b/docs/img/weresquirrel.svg
      new file mode 100644
      index 000000000..687c3675a
      --- /dev/null
      +++ b/docs/img/weresquirrel.svg
      @@ -0,0 +1,6994 @@
      +
      +
      +
      +image/svg+xml
      \ No newline at end of file
      diff --git a/docs/index.html b/docs/index.html
      new file mode 100644
      index 000000000..c7702175a
      --- /dev/null
      +++ b/docs/index.html
      @@ -0,0 +1,146 @@
      +
      +
      +  
      +  
      +  Eloquent JavaScript
      +  
      +  
      +
      +
      +
      +
      + +

      + + Cover image + +

      + +

      Eloquent JavaScript
      3rd edition

      + +

      This is a book about JavaScript, programming, and the wonders of + the digital. You can read it online here, or get your own + paperback + copy.

      + +

      Written by Marijn Haverbeke.

      + +
      +

      Licensed under + a Creative + Commons attribution-noncommercial license. All code in this book + may also be considered licensed under + an MIT license.

      + +

      Illustrations by various artists: Cover and chapter illustrations + by Madalina Tantareanu. + Pixel art in Chapters 7 and 16 by Antonio Perdomo Pastor. Regular + expression diagrams in Chapter 9 generated + with regexper.com by Jeff + Avallone. Village photograph in Chapter 11 by Fabrice Creuzot. Game + concept for Chapter 15 by Thomas + Palef.

      + +

      The third edition was made possible + by 325 financial backers, most + notably and . The second edition + was supported by 454 backers, with + significant contributions + from , , + and .

      +
      + +
      + + + + +
      diff --git a/docs/js/.tern-project b/docs/js/.tern-project new file mode 100644 index 000000000..3a218cbc1 --- /dev/null +++ b/docs/js/.tern-project @@ -0,0 +1,3 @@ +{ + "libs": ["browser"] +} \ No newline at end of file diff --git a/docs/js/acorn_codemirror.js b/docs/js/acorn_codemirror.js new file mode 100644 index 000000000..729457568 --- /dev/null +++ b/docs/js/acorn_codemirror.js @@ -0,0 +1,11 @@ +(function(e,t){typeof exports==="object"&&typeof module!=="undefined"?module.exports=t():typeof define==="function"&&define.amd?define(t):e.CodeMirror=t()})(this,function(){"use strict";var e=navigator.userAgent;var t=navigator.platform;var r=/gecko\/\d/i.test(e);var i=/MSIE \d/.test(e);var n=/Trident\/(?:[7-9]|\d{2,})\..*rv:(\d+)/.exec(e);var a=/Edge\/(\d+)/.exec(e);var s=i||n||a;var o=s&&(i?document.documentMode||6:+(a||n)[1]);var l=!a&&/WebKit\//.test(e);var u=l&&/Qt\/\d+\.\d+/.test(e);var c=!a&&/Chrome\//.test(e);var f=/Opera\//.test(e);var h=/Apple Computer/.test(navigator.vendor);var p=/Mac OS X 1\d\D([8-9]|\d\d)\D/.test(e);var d=/PhantomJS/.test(e);var m=!a&&/AppleWebKit/.test(e)&&/Mobile\/\w+/.test(e);var v=/Android/.test(e);var g=m||v||/webOS|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(e);var y=m||/Mac/.test(t);var x=/\bCrOS\b/.test(e);var b=/win/i.test(t);var w=f&&e.match(/Version\/(\d*\.\d*)/);if(w){w=Number(w[1])}if(w&&w>=15){f=false;l=true}var k=y&&(u||f&&(w==null||w<12.11));var C=r||s&&o>=9;function S(e){return new RegExp("(^|\\s)"+e+"(?:$|\\s)\\s*")}var L=function(e,t){var r=e.className;var i=S(t).exec(r);if(i){var n=r.slice(i.index+i[0].length);e.className=r.slice(0,i.index)+(n?i[1]+n:"")}};function T(e){for(var t=e.childNodes.length;t>0;--t){e.removeChild(e.firstChild)}return e}function A(e,t){return T(e).appendChild(t)}function E(e,t,r,i){var n=document.createElement(e);if(r){n.className=r}if(i){n.style.cssText=i}if(typeof t=="string"){n.appendChild(document.createTextNode(t))}else if(t){for(var a=0;a=t){return s+(t-a)}s+=o-a;s+=r-s%r;a=o+1}}var W=function(){this.id=null;this.f=null;this.time=0;this.handler=R(this.onTimeout,this)};W.prototype.onTimeout=function(e){e.id=0;if(e.time<=+new Date){e.f()}else{setTimeout(e.handler,e.time-+new Date)}};W.prototype.set=function(e,t){this.f=t;var r=+new Date+e;if(!this.id||r=t){return i+Math.min(s,t-n)}n+=a-i;n+=r-n%r;i=a+1;if(n>=t){return i}}}var K=[""];function $(e){while(K.length<=e){K.push(X(K)+" ")}return K[e]}function X(e){return e[e.length-1]}function Y(e,t){var r=[];for(var i=0;i"€"&&(e.toUpperCase()!=e.toLowerCase()||ee.test(e))}function re(e,t){if(!t){return te(e)}if(t.source.indexOf("\\w")>-1&&te(e)){return true}return t.test(e)}function ie(e){for(var t in e){if(e.hasOwnProperty(t)&&e[t]){return false}}return true}var ne=/[\u0300-\u036f\u0483-\u0489\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610-\u061a\u064b-\u065e\u0670\u06d6-\u06dc\u06de-\u06e4\u06e7\u06e8\u06ea-\u06ed\u0711\u0730-\u074a\u07a6-\u07b0\u07eb-\u07f3\u0816-\u0819\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0900-\u0902\u093c\u0941-\u0948\u094d\u0951-\u0955\u0962\u0963\u0981\u09bc\u09be\u09c1-\u09c4\u09cd\u09d7\u09e2\u09e3\u0a01\u0a02\u0a3c\u0a41\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a70\u0a71\u0a75\u0a81\u0a82\u0abc\u0ac1-\u0ac5\u0ac7\u0ac8\u0acd\u0ae2\u0ae3\u0b01\u0b3c\u0b3e\u0b3f\u0b41-\u0b44\u0b4d\u0b56\u0b57\u0b62\u0b63\u0b82\u0bbe\u0bc0\u0bcd\u0bd7\u0c3e-\u0c40\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62\u0c63\u0cbc\u0cbf\u0cc2\u0cc6\u0ccc\u0ccd\u0cd5\u0cd6\u0ce2\u0ce3\u0d3e\u0d41-\u0d44\u0d4d\u0d57\u0d62\u0d63\u0dca\u0dcf\u0dd2-\u0dd4\u0dd6\u0ddf\u0e31\u0e34-\u0e3a\u0e47-\u0e4e\u0eb1\u0eb4-\u0eb9\u0ebb\u0ebc\u0ec8-\u0ecd\u0f18\u0f19\u0f35\u0f37\u0f39\u0f71-\u0f7e\u0f80-\u0f84\u0f86\u0f87\u0f90-\u0f97\u0f99-\u0fbc\u0fc6\u102d-\u1030\u1032-\u1037\u1039\u103a\u103d\u103e\u1058\u1059\u105e-\u1060\u1071-\u1074\u1082\u1085\u1086\u108d\u109d\u135f\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17b7-\u17bd\u17c6\u17c9-\u17d3\u17dd\u180b-\u180d\u18a9\u1920-\u1922\u1927\u1928\u1932\u1939-\u193b\u1a17\u1a18\u1a56\u1a58-\u1a5e\u1a60\u1a62\u1a65-\u1a6c\u1a73-\u1a7c\u1a7f\u1b00-\u1b03\u1b34\u1b36-\u1b3a\u1b3c\u1b42\u1b6b-\u1b73\u1b80\u1b81\u1ba2-\u1ba5\u1ba8\u1ba9\u1c2c-\u1c33\u1c36\u1c37\u1cd0-\u1cd2\u1cd4-\u1ce0\u1ce2-\u1ce8\u1ced\u1dc0-\u1de6\u1dfd-\u1dff\u200c\u200d\u20d0-\u20f0\u2cef-\u2cf1\u2de0-\u2dff\u302a-\u302f\u3099\u309a\ua66f-\ua672\ua67c\ua67d\ua6f0\ua6f1\ua802\ua806\ua80b\ua825\ua826\ua8c4\ua8e0-\ua8f1\ua926-\ua92d\ua947-\ua951\ua980-\ua982\ua9b3\ua9b6-\ua9b9\ua9bc\uaa29-\uaa2e\uaa31\uaa32\uaa35\uaa36\uaa43\uaa4c\uaab0\uaab2-\uaab4\uaab7\uaab8\uaabe\uaabf\uaac1\uabe5\uabe8\uabed\udc00-\udfff\ufb1e\ufe00-\ufe0f\ufe20-\ufe26\uff9e\uff9f]/;function ae(e){return e.charCodeAt(0)>=768&&ne.test(e)}function se(e,t,r){while((r<0?t>0:tr?-1:1;for(;;){if(t==r){return t}var n=(t+r)/2,a=i<0?Math.ceil(n):Math.floor(n);if(a==t){return e(a)?t:r}if(e(a)){r=a}else{t=a+i}}}function le(e,t,r,i){if(!e){return i(t,r,"ltr",0)}var n=false;for(var a=0;at||t==r&&s.to==t){i(Math.max(s.from,t),Math.min(s.to,r),s.level==1?"rtl":"ltr",a);n=true}}if(!n){i(t,r,"ltr")}}var ue=null;function ce(e,t,r){var i;ue=null;for(var n=0;nt){return n}if(a.to==t){if(a.from!=a.to&&r=="before"){i=n}else{ue=n}}if(a.from==t){if(a.from!=a.to&&r!="before"){i=n}else{ue=n}}}return i!=null?i:ue}var fe=function(){var e="bbbbbbbbbtstwsbbbbbbbbbbbbbbssstwNN%%%NNNNNN,N,N1111111111NNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNbbbbbbsbbbbbbbbbbbbbbbbbbbbbbbbbb,N%%%%NNNNLNNNNN%%11NLNNN1LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLN";var t="nnnnnnNNr%%r,rNNmmmmmmmmmmmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmmmmmmmmnnnnnnnnnn%nnrrrmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmnNmmmmmmrrmmNmmmmrr1111111111";function r(r){if(r<=247){return e.charAt(r)}else if(1424<=r&&r<=1524){return"R"}else if(1536<=r&&r<=1785){return t.charAt(r-1536)}else if(1774<=r&&r<=2220){return"r"}else if(8192<=r&&r<=8203){return"w"}else if(r==8204){return"b"}else{return"L"}}var i=/[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/;var n=/[stwN]/,a=/[LRr]/,s=/[Lb1n]/,o=/[1n]/;function l(e,t,r){this.level=e;this.from=t;this.to=r}return function(e,t){var u=t=="ltr"?"L":"R";if(e.length==0||t=="ltr"&&!i.test(e)){return false}var c=e.length,f=[];for(var h=0;h-1){i[t]=n.slice(0,a).concat(n.slice(a+1))}}}}function ge(e,t){var r=me(e,t);if(!r.length){return}var i=Array.prototype.slice.call(arguments,2);for(var n=0;n0}function we(e){e.prototype.on=function(e,t){de(this,e,t)};e.prototype.off=function(e,t){ve(this,e,t)}}function ke(e){if(e.preventDefault){e.preventDefault()}else{e.returnValue=false}}function Ce(e){if(e.stopPropagation){e.stopPropagation()}else{e.cancelBubble=true}}function Se(e){return e.defaultPrevented!=null?e.defaultPrevented:e.returnValue==false}function Le(e){ke(e);Ce(e)}function Te(e){return e.target||e.srcElement}function Ae(e){var t=e.which;if(t==null){if(e.button&1){t=1}else if(e.button&2){t=3}else if(e.button&4){t=2}}if(y&&e.ctrlKey&&t==1){t=3}return t}var Ee=function(){if(s&&o<9){return false}var e=E("div");return"draggable"in e||"dragDrop"in e}();var Me;function Ne(e){if(Me==null){var t=E("span","​");A(e,E("span",[t,document.createTextNode("x")]));if(e.firstChild.offsetHeight!=0){Me=t.offsetWidth<=1&&t.offsetHeight>2&&!(s&&o<8)}}var r=Me?E("span","​"):E("span"," ",null,"display: inline-block; width: 1px; margin-right: -1px");r.setAttribute("cm-text","");return r}var _e;function Pe(e){if(_e!=null){return _e}var t=A(e,document.createTextNode("AخA"));var r=N(t,0,1).getBoundingClientRect();var i=N(t,1,2).getBoundingClientRect();T(e);if(!r||r.left==r.right){return false}return _e=i.right-r.right<3}var Ie="\n\nb".split(/\n/).length!=3?function(e){var t=0,r=[],i=e.length;while(t<=i){var n=e.indexOf("\n",t);if(n==-1){n=e.length}var a=e.slice(t,e.charAt(n-1)=="\r"?n-1:n);var s=a.indexOf("\r");if(s!=-1){r.push(a.slice(0,s));t+=s+1}else{r.push(a);t=n+1}}return r}:function(e){return e.split(/\r\n?|\n/)};var Oe=window.getSelection?function(e){try{return e.selectionStart!=e.selectionEnd}catch(e){return false}}:function(e){var t;try{t=e.ownerDocument.selection.createRange()}catch(e){}if(!t||t.parentElement()!=e){return false}return t.compareEndPoints("StartToEnd",t)!=0};var De=function(){var e=E("div");if("oncopy"in e){return true}e.setAttribute("oncopy","return;");return typeof e.oncopy=="function"}();var Re=null;function Ve(e){if(Re!=null){return Re}var t=A(e,E("span","x"));var r=t.getBoundingClientRect();var i=N(t,0,1).getBoundingClientRect();return Re=Math.abs(r.left-i.left)>1}var Fe={},We={};function ze(e,t){if(arguments.length>2){t.dependencies=Array.prototype.slice.call(arguments,2)}Fe[e]=t}function Be(e,t){We[e]=t}function He(e){if(typeof e=="string"&&We.hasOwnProperty(e)){e=We[e]}else if(e&&typeof e.name=="string"&&We.hasOwnProperty(e.name)){var t=We[e.name];if(typeof t=="string"){t={name:t}}e=J(t,e);e.name=t.name}else if(typeof e=="string"&&/^[\w\-]+\/[\w\-]+\+xml$/.test(e)){return He("application/xml")}else if(typeof e=="string"&&/^[\w\-]+\/[\w\-]+\+json$/.test(e)){return He("application/json")}if(typeof e=="string"){return{name:e}}else{return e||{name:"null"}}}function je(e,t){t=He(t);var r=Fe[t.name];if(!r){return je(e,"text/plain")}var i=r(e,t);if(Ue.hasOwnProperty(t.name)){var n=Ue[t.name];for(var a in n){if(!n.hasOwnProperty(a)){continue}if(i.hasOwnProperty(a)){i["_"+a]=i[a]}i[a]=n[a]}}i.name=t.name;if(t.helperType){i.helperType=t.helperType}if(t.modeProps){for(var s in t.modeProps){i[s]=t.modeProps[s]}}return i}var Ue={};function Ge(e,t){var r=Ue.hasOwnProperty(e)?Ue[e]:Ue[e]={};V(t,r)}function qe(e,t){if(t===true){return t}if(e.copyState){return e.copyState(t)}var r={};for(var i in t){var n=t[i];if(n instanceof Array){n=n.concat([])}r[i]=n}return r}function Ke(e,t){var r;while(e.innerMode){r=e.innerMode(t);if(!r||r.mode==e){break}t=r.state;e=r.mode}return r||{mode:e,state:t}}function $e(e,t,r){return e.startState?e.startState(t,r):true}var Xe=function(e,t,r){this.pos=this.start=0;this.string=e;this.tabSize=t||8;this.lastColumnPos=this.lastColumnValue=0;this.lineStart=0;this.lineOracle=r};Xe.prototype.eol=function(){return this.pos>=this.string.length};Xe.prototype.sol=function(){return this.pos==this.lineStart};Xe.prototype.peek=function(){return this.string.charAt(this.pos)||undefined};Xe.prototype.next=function(){if(this.post};Xe.prototype.eatSpace=function(){var e=this;var t=this.pos;while(/[\s\u00a0]/.test(this.string.charAt(this.pos))){++e.pos}return this.pos>t};Xe.prototype.skipToEnd=function(){this.pos=this.string.length};Xe.prototype.skipTo=function(e){var t=this.string.indexOf(e,this.pos);if(t>-1){this.pos=t;return true}};Xe.prototype.backUp=function(e){this.pos-=e};Xe.prototype.column=function(){if(this.lastColumnPos0){return null}if(a&&t!==false){this.pos+=a[0].length}return a}};Xe.prototype.current=function(){return this.string.slice(this.start,this.pos)};Xe.prototype.hideFirstChars=function(e,t){this.lineStart+=e;try{return t()}finally{this.lineStart-=e}};Xe.prototype.lookAhead=function(e){var t=this.lineOracle;return t&&t.lookAhead(e)};Xe.prototype.baseToken=function(){var e=this.lineOracle;return e&&e.baseToken(this.pos)};function Ye(e,t){t-=e.first;if(t<0||t>=e.size){throw new Error("There is no line "+(t+e.first)+" in the document.")}var r=e;while(!r.lines){for(var i=0;;++i){var n=r.children[i],a=n.chunkSize();if(t=e.first&&tr){return nt(r,Ye(e,r).text.length)}return ht(t,Ye(e,t.line).text.length)}function ht(e,t){var r=e.ch;if(r==null||r>t){return nt(e.line,t)}else if(r<0){return nt(e.line,0)}else{return e}}function pt(e,t){var r=[];for(var i=0;ithis.maxLookAhead){this.maxLookAhead=e}return t};mt.prototype.baseToken=function(e){var t=this;if(!this.baseTokens){return null}while(this.baseTokens[this.baseTokenPos]<=e){t.baseTokenPos+=2}var r=this.baseTokens[this.baseTokenPos+1];return{type:r&&r.replace(/( |^)overlay .*/,""),size:this.baseTokens[this.baseTokenPos]-e}};mt.prototype.nextLine=function(){this.line++;if(this.maxLookAhead>0){this.maxLookAhead--}};mt.fromSaved=function(e,t,r){if(t instanceof dt){return new mt(e,qe(e.mode,t.state),r,t.lookAhead)}else{return new mt(e,qe(e.mode,t),r)}};mt.prototype.save=function(e){var t=e!==false?qe(this.doc.mode,this.state):this.state;return this.maxLookAhead>0?new dt(t,this.maxLookAhead):t};function vt(e,t,r,i){var n=[e.state.modeGen],a={};Lt(e,t.text,e.doc.mode,r,function(e,t){return n.push(e,t)},a,i);var s=r.state;var o=function(i){r.baseTokens=n;var o=e.state.overlays[i],l=1,u=0;r.state=true;Lt(e,t.text,o.mode,r,function(e,t){var r=l;while(ue){n.splice(l,1,e,n[l+1],i)}l+=2;u=Math.min(e,i)}if(!t){return}if(o.opaque){n.splice(r,l-r,e,"overlay "+t);l=r+2}else{for(;re.options.maxHighlightLength&&qe(e.doc.mode,i.state);var a=vt(e,t,i);if(n){i.state=n}t.stateAfter=i.save(!n);t.styles=a.styles;if(a.classes){t.styleClasses=a.classes}else if(t.styleClasses){t.styleClasses=null}if(r===e.doc.highlightFrontier){e.doc.modeFrontier=Math.max(e.doc.modeFrontier,++e.doc.highlightFrontier)}}return t.styles}function yt(e,t,r){var i=e.doc,n=e.display;if(!i.mode.startState){return new mt(i,true,t)}var a=Tt(e,t,r);var s=a>i.first&&Ye(i,a-1).stateAfter;var o=s?mt.fromSaved(i,s,a):new mt(i,$e(i.mode),a);i.iter(a,t,function(r){xt(e,r.text,o);var i=o.line;r.stateAfter=i==t-1||i%5==0||i>=n.viewFrom&&it.start){return a}}throw new Error("Mode "+e.name+" failed to advance stream.")}var kt=function(e,t,r){this.start=e.start;this.end=e.pos;this.string=e.current();this.type=t||null;this.state=r};function Ct(e,t,r,i){var n=e.doc,a=n.mode,s;t=ft(n,t);var o=Ye(n,t.line),l=yt(e,t.line,r);var u=new Xe(o.text,e.options.tabSize,l),c;if(i){c=[]}while((i||u.pose.options.maxHighlightLength){o=false;if(s){xt(e,t,i,c.pos)}c.pos=t.length;f=null}else{f=St(wt(r,c,i.state,h),a)}if(h){var p=h[0].name;if(p){f="m-"+(f?p+" "+f:p)}}if(!o||u!=f){while(ls;--o){if(o<=a.first){return a.first}var l=Ye(a,o-1),u=l.stateAfter;if(u&&(!r||o+(u instanceof dt?u.lookAhead:0)<=a.modeFrontier)){return o}var c=F(l.text,null,e.options.tabSize);if(n==null||i>c){n=o-1;i=c}}return n}function At(e,t){e.modeFrontier=Math.min(e.modeFrontier,t);if(e.highlightFrontierr;i--){var n=Ye(e,i).stateAfter;if(n&&(!(n instanceof dt)||i+n.lookAhead=t:a.to>t);(i||(i=[])).push(new Pt(s,a.from,l?null:a.to))}}}return i}function Vt(e,t,r){var i;if(e){for(var n=0;n=t:a.to>t);if(o||a.from==t&&s.type=="bookmark"&&(!r||a.marker.insertLeft)){var l=a.from==null||(s.inclusiveLeft?a.from<=t:a.from0&&o){for(var b=0;b0){continue}var c=[l,1],f=at(u.from,o.from),h=at(u.to,o.to);if(f<0||!s.inclusiveLeft&&!f){c.push({from:u.from,to:o.from})}if(h>0||!s.inclusiveRight&&!h){c.push({from:o.to,to:u.to})}n.splice.apply(n,c);l+=c.length-3}}return n}function Bt(e){var t=e.markedSpans;if(!t){return}for(var r=0;rt)&&(!i||Gt(i,a.marker)<0)){i=a.marker}}}return i}function Yt(e,t,r,i,n){var a=Ye(e,t);var s=Mt&&a.markedSpans;if(s){for(var o=0;o=0&&f<=0||c<=0&&f>=0){continue}if(c<=0&&(l.marker.inclusiveRight&&n.inclusiveLeft?at(u.to,r)>=0:at(u.to,r)>0)||c>=0&&(l.marker.inclusiveRight&&n.inclusiveLeft?at(u.from,i)<=0:at(u.from,i)<0)){return true}}}}function Qt(e){var t;while(t=Kt(e)){e=t.find(-1,true).line}return e}function Zt(e){var t;while(t=$t(e)){e=t.find(1,true).line}return e}function Jt(e){var t,r;while(t=$t(e)){e=t.find(1,true).line;(r||(r=[])).push(e)}return r}function er(e,t){var r=Ye(e,t),i=Qt(r);if(r==i){return t}return et(i)}function tr(e,t){if(t>e.lastLine()){return t}var r=Ye(e,t),i;if(!rr(e,r)){return t}while(i=$t(r)){r=i.find(1,true).line}return et(r)+1}function rr(e,t){var r=Mt&&t.markedSpans;if(r){for(var i=void 0,n=0;nt.maxLineLength){t.maxLineLength=r;t.maxLine=e}})}var or=function(e,t,r){this.text=e;Ht(this,t);this.height=r?r(this):1};or.prototype.lineNo=function(){return et(this)};we(or);function lr(e,t,r,i){e.text=t;if(e.stateAfter){e.stateAfter=null}if(e.styles){e.styles=null}if(e.order!=null){e.order=null}Bt(e);Ht(e,r);var n=i?i(e):1;if(n!=e.height){Je(e,n)}}function ur(e){e.parent=null;Bt(e)}var cr={},fr={};function hr(e,t){if(!e||/^\s*$/.test(e)){return null}var r=t.addModeClass?fr:cr;return r[e]||(r[e]=e.replace(/\S+/g,"cm-$&"))}function pr(e,t){var r=M("span",null,null,l?"padding-right: .1px":null);var i={pre:M("pre",[r],"CodeMirror-line"),content:r,col:0,pos:0,cm:e,trailingSpace:false,splitSpaces:e.getOption("lineWrapping")};t.measure={};for(var n=0;n<=(t.rest?t.rest.length:0);n++){var a=n?t.rest[n-1]:t.line,s=void 0;i.pos=0;i.addToken=mr;if(Pe(e.display.measure)&&(s=he(a,e.doc.direction))){i.addToken=gr(i.addToken,s)}i.map=[];var o=t!=e.display.externalMeasured&&et(a);xr(a,i,gt(e,a,o));if(a.styleClasses){if(a.styleClasses.bgClass){i.bgClass=O(a.styleClasses.bgClass,i.bgClass||"")}if(a.styleClasses.textClass){i.textClass=O(a.styleClasses.textClass,i.textClass||"")}}if(i.map.length==0){i.map.push(0,0,i.content.appendChild(Ne(e.display.measure)))}if(n==0){t.measure.map=i.map;t.measure.cache={}}else{(t.measure.maps||(t.measure.maps=[])).push(i.map);(t.measure.caches||(t.measure.caches=[])).push({})}}if(l){var u=i.content.lastChild;if(/\bcm-tab\b/.test(u.className)||u.querySelector&&u.querySelector(".cm-tab")){i.content.className="cm-tab-wrap-hack"}}ge(e,"renderLine",e,t.line,i.pre);if(i.pre.className){i.textClass=O(i.pre.className,i.textClass||"")}return i}function dr(e){var t=E("span","•","cm-invalidchar");t.title="\\u"+e.charCodeAt(0).toString(16);t.setAttribute("aria-label",t.title);return t}function mr(e,t,r,i,n,a,l){if(!t){return}var u=e.splitSpaces?vr(t,e.trailingSpace):t;var c=e.cm.state.specialChars,f=false;var h;if(!c.test(t)){e.col+=t.length;h=document.createTextNode(u);e.map.push(e.pos,e.pos+t.length,h);if(s&&o<9){f=true}e.pos+=t.length}else{h=document.createDocumentFragment();var p=0;while(true){c.lastIndex=p;var d=c.exec(t);var m=d?d.index-p:t.length-p;if(m){var v=document.createTextNode(u.slice(p,p+m));if(s&&o<9){h.appendChild(E("span",[v]))}else{h.appendChild(v)}e.map.push(e.pos,e.pos+m,v);e.col+=m;e.pos+=m}if(!d){break}p+=m+1;var g=void 0;if(d[0]=="\t"){var y=e.cm.options.tabSize,x=y-e.col%y;g=h.appendChild(E("span",$(x),"cm-tab"));g.setAttribute("role","presentation");g.setAttribute("cm-text","\t");e.col+=x}else if(d[0]=="\r"||d[0]=="\n"){g=h.appendChild(E("span",d[0]=="\r"?"␍":"␤","cm-invalidchar"));g.setAttribute("cm-text",d[0]);e.col+=1}else{g=e.cm.options.specialCharPlaceholder(d[0]);g.setAttribute("cm-text",d[0]);if(s&&o<9){h.appendChild(E("span",[g]))}else{h.appendChild(g)}e.col+=1}e.map.push(e.pos,e.pos+1,g);e.pos++}}e.trailingSpace=u.charCodeAt(t.length-1)==32;if(r||i||n||f||a){var b=r||"";if(i){ +b+=i}if(n){b+=n}var w=E("span",[h],b,a);if(l){for(var k in l){if(l.hasOwnProperty(k)&&k!="style"&&k!="class"){w.setAttribute(k,l[k])}}}return e.content.appendChild(w)}e.content.appendChild(h)}function vr(e,t){if(e.length>1&&!/ /.test(e)){return e}var r=t,i="";for(var n=0;nu&&f.from<=u){break}}if(f.to>=c){return e(r,i,n,a,s,o,l)}e(r,i.slice(0,f.to-u),n,a,null,o,l);a=null;i=i.slice(f.to-u);u=f.to}}}function yr(e,t,r,i){var n=!i&&r.widgetNode;if(n){e.map.push(e.pos,e.pos+t,n)}if(!i&&e.cm.display.input.needsContentAttribute){if(!n){n=e.content.appendChild(document.createElement("span"))}n.setAttribute("cm-marker",r.id)}if(n){e.cm.display.input.setUneditable(n);e.content.appendChild(n)}e.pos+=t;e.trailingSpace=false}function xr(e,t,r){var i=e.markedSpans,n=e.text,a=0;if(!i){for(var s=1;sl||C.collapsed&&k.to==l&&k.from==l)){if(k.to!=null&&k.to!=l&&p>k.to){p=k.to;m=""}if(C.className){d+=" "+C.className}if(C.css){h=(h?h+";":"")+C.css}if(C.startStyle&&k.from==l){v+=" "+C.startStyle}if(C.endStyle&&k.to==p){(b||(b=[])).push(C.endStyle,k.to)}if(C.title){(y||(y={})).title=C.title}if(C.attributes){for(var S in C.attributes){(y||(y={}))[S]=C.attributes[S]}}if(C.collapsed&&(!g||Gt(g.marker,C)<0)){g=k}}else if(k.from>l&&p>k.from){p=k.from}}if(b){for(var L=0;L=o){break}var A=Math.min(o,p);while(true){if(c){var E=l+c.length;if(!g){var M=E>A?c.slice(0,A-l):c;t.addToken(t,M,f?f+d:d,v,l+M.length==p?m:"",h,y)}if(E>=A){c=c.slice(A-l);l=A;break}l=E;v=""}c=n.slice(a,a=r[u++]);f=hr(r[u++],t.cm.options)}}}function br(e,t,r){this.line=t;this.rest=Jt(t);this.size=this.rest?et(X(this.rest))-r+1:1;this.node=this.text=null;this.hidden=rr(e,t)}function wr(e,t,r){var i=[],n;for(var a=t;a2){a.push((l.bottom+u.top)/2-r.top)}}}a.push(r.bottom-r.top)}}function Yr(e,t,r){if(e.line==t){return{map:e.measure.map,cache:e.measure.cache}}for(var i=0;ir){return{map:e.measure.maps[n],cache:e.measure.caches[n],before:true}}}}function Qr(e,t){t=Qt(t);var r=et(t);var i=e.display.externalMeasured=new br(e.doc,t,r);i.lineN=r;var n=i.built=pr(e,i);i.text=n.pre;A(e.display.lineMeasure,n.pre);return i}function Zr(e,t,r,i){return ti(e,ei(e,t),r,i)}function Jr(e,t){if(t>=e.display.viewFrom&&t=r.lineN&&tt){a=l-o;n=a-1;if(t>=l){s="right"}}if(n!=null){i=e[u+2];if(o==l&&r==(i.insertLeft?"left":"right")){s=r}if(r=="left"&&n==0){while(u&&e[u-2]==e[u-3]&&e[u-1].insertLeft){i=e[(u-=3)+2];s="left"}}if(r=="right"&&n==l-o){while(u=0;n--){if((r=e[n]).left!=r.right){break}}}return r}function ai(e,t,r,i){var n=ii(t.map,r,i);var a=n.node,l=n.start,u=n.end,c=n.collapse;var f;if(a.nodeType==3){for(var h=0;h<4;h++){while(l&&ae(t.line.text.charAt(n.coverStart+l))){--l}while(n.coverStart+u0){c=i="right"}var p;if(e.options.lineWrapping&&(p=a.getClientRects()).length>1){f=p[i=="right"?p.length-1:0]}else{f=a.getBoundingClientRect()}}if(s&&o<9&&!l&&(!f||!f.left&&!f.right)){var d=a.parentNode.getClientRects()[0];if(d){f={left:d.left,right:d.left+Ei(e.display),top:d.top,bottom:d.bottom}}else{f=ri}}var m=f.top-t.rect.top,v=f.bottom-t.rect.top;var g=(m+v)/2;var y=t.view.measure.heights;var x=0;for(;x=i.text.length){l=i.text.length;u="before"}else if(l<=0){l=0;u="after"}if(!o){return s(u=="before"?l-1:l,u=="before")}function c(e,t,r){var i=o[t],n=i.level==1;return s(r?e-1:e,n!=r)}var f=ce(o,l,u);var h=ue;var p=c(l,f,u=="before");if(h!=null){p.other=c(l,h,u!="before")}return p}function gi(e,t){var r=0;t=ft(e.doc,t);if(!e.options.lineWrapping){r=Ei(e.display)*t.ch}var i=Ye(e.doc,t.line);var n=nr(i)+jr(e.display);return{left:r,right:r,top:n,bottom:n+i.height}}function yi(e,t,r,i,n){var a=nt(e,t,r);a.xRel=n;if(i){a.outside=i}return a}function xi(e,t,r){var i=e.doc;r+=e.display.viewOffset;if(r<0){return yi(i.first,0,null,-1,-1)}var n=tt(i,r),a=i.first+i.size-1;if(n>a){return yi(i.first+i.size-1,Ye(i,a).text.length,null,1,1)}if(t<0){t=0}var s=Ye(i,n);for(;;){var o=Ci(e,s,n,t,r);var l=Xt(s,o.ch+(o.xRel>0||o.outside>0?1:0));if(!l){return o}var u=l.find(1);if(u.line==n){return u}s=Ye(i,n=u.line)}}function bi(e,t,r,i){i-=hi(t);var n=t.text.length;var a=oe(function(t){return ti(e,r,t-1).bottom<=i},n,0);n=oe(function(t){return ti(e,r,t).top>i},a,n);return{begin:a,end:n}}function wi(e,t,r,i){if(!r){r=ei(e,t)}var n=pi(e,t,ti(e,r,i),"line").top;return bi(e,t,r,n)}function ki(e,t,r,i){return e.bottom<=r?false:e.top>r?true:(i?e.left:e.right)>t}function Ci(e,t,r,i,n){n-=nr(t);var a=ei(e,t);var s=hi(t);var o=0,l=t.text.length,u=true;var c=he(t,e.doc.direction);if(c){var f=(e.options.lineWrapping?Li:Si)(e,t,r,a,c,i,n);u=f.level!=1;o=u?f.from:f.to-1;l=u?f.to:f.from-1}var h=null,p=null;var d=oe(function(t){var r=ti(e,a,t);r.top+=s;r.bottom+=s;if(!ki(r,i,n,false)){return false}if(r.top<=n&&r.left<=i){h=t;p=r}return true},o,l);var m,v,g=false;if(p){var y=i-p.left=b.bottom?1:0}d=se(t.text,d,1);return yi(r,d,v,g,i-m)}function Si(e,t,r,i,n,a,s){var o=oe(function(o){var l=n[o],u=l.level!=1;return ki(vi(e,nt(r,u?l.to:l.from,u?"before":"after"),"line",t,i),a,s,true)},0,n.length-1);var l=n[o];if(o>0){var u=l.level!=1;var c=vi(e,nt(r,u?l.from:l.to,u?"after":"before"),"line",t,i);if(ki(c,a,s,true)&&c.top>s){l=n[o-1]}}return l}function Li(e,t,r,i,n,a,s){var o=bi(e,t,i,s);var l=o.begin;var u=o.end;if(/\s/.test(t.text.charAt(u-1))){u--}var c=null,f=null;for(var h=0;h=u||p.to<=l){continue}var d=p.level!=1;var m=ti(e,i,d?Math.min(u,p.to)-1:Math.max(l,p.from)).right;var v=mv){c=p;f=v}}if(!c){c=n[n.length-1]}if(c.fromu){c={from:c.from,to:u,level:c.level}}return c}var Ti;function Ai(e){if(e.cachedTextHeight!=null){return e.cachedTextHeight}if(Ti==null){Ti=E("pre",null,"CodeMirror-line-like");for(var t=0;t<49;++t){Ti.appendChild(document.createTextNode("x"));Ti.appendChild(E("br"))}Ti.appendChild(document.createTextNode("x"))}A(e.measure,Ti);var r=Ti.offsetHeight/50;if(r>3){e.cachedTextHeight=r}T(e.measure);return r||1}function Ei(e){if(e.cachedCharWidth!=null){return e.cachedCharWidth}var t=E("span","xxxxxxxxxx");var r=E("pre",[t],"CodeMirror-line-like");A(e.measure,r);var i=t.getBoundingClientRect(),n=(i.right-i.left)/10;if(n>2){e.cachedCharWidth=n}return n||10}function Mi(e){var t=e.display,r={},i={};var n=t.gutters.clientLeft;for(var a=t.gutters.firstChild,s=0;a;a=a.nextSibling,++s){var o=e.display.gutterSpecs[s].className;r[o]=a.offsetLeft+a.clientLeft+n;i[o]=a.clientWidth}return{fixedPos:Ni(t),gutterTotalWidth:t.gutters.offsetWidth,gutterLeft:r,gutterWidth:i,wrapperWidth:t.wrapper.clientWidth}}function Ni(e){return e.scroller.getBoundingClientRect().left-e.sizer.getBoundingClientRect().left}function _i(e){var t=Ai(e.display),r=e.options.lineWrapping;var i=r&&Math.max(5,e.display.scroller.clientWidth/Ei(e.display)-3);return function(n){if(rr(e.doc,n)){return 0}var a=0;if(n.widgets){for(var s=0;s=e.display.viewTo){return null}t-=e.display.viewFrom;if(t<0){return null}var r=e.display.view;for(var i=0;it)){n.updateLineNumbers=t}e.curOp.viewChanged=true;if(t>=n.viewTo){if(Mt&&er(e.doc,t)n.viewFrom){Vi(e)}else{n.viewFrom+=i;n.viewTo+=i}}else if(t<=n.viewFrom&&r>=n.viewTo){Vi(e)}else if(t<=n.viewFrom){var a=Fi(e,r,r+i,1);if(a){n.view=n.view.slice(a.index);n.viewFrom=a.lineN;n.viewTo+=i}else{Vi(e)}}else if(r>=n.viewTo){var s=Fi(e,t,t,-1);if(s){n.view=n.view.slice(0,s.index);n.viewTo=s.lineN}else{Vi(e)}}else{var o=Fi(e,t,t,-1);var l=Fi(e,r,r+i,1);if(o&&l){n.view=n.view.slice(0,o.index).concat(wr(e,o.lineN,l.lineN)).concat(n.view.slice(l.index));n.viewTo+=i}else{Vi(e)}}var u=n.externalMeasured;if(u){if(r=n.lineN&&t=i.viewTo){return}var a=i.view[Oi(e,t)];if(a.node==null){return}var s=a.changes||(a.changes=[]);if(z(s,r)==-1){s.push(r)}}function Vi(e){e.display.viewFrom=e.display.viewTo=e.doc.first;e.display.view=[];e.display.viewOffset=0}function Fi(e,t,r,i){var n=Oi(e,t),a,s=e.display.view;if(!Mt||r==e.doc.first+e.doc.size){return{index:n,lineN:r}}var o=e.display.viewFrom;for(var l=0;l0){if(n==s.length-1){return null}a=o+s[n].size-t;n++}else{a=o-t}t+=a;r+=a}while(er(e.doc,r)!=r){if(n==(i<0?0:s.length-1)){return null}r+=i*s[n-(i<0?1:0)].size;n+=i}return{index:n,lineN:r}}function Wi(e,t,r){var i=e.display,n=i.view;if(n.length==0||t>=i.viewTo||r<=i.viewFrom){i.view=wr(e,t,r);i.viewFrom=t}else{if(i.viewFrom>t){i.view=wr(e,t,i.viewFrom).concat(i.view)}else if(i.viewFromr){i.view=i.view.slice(0,Oi(e,r))}}i.viewTo=r}function zi(e){var t=e.display.view,r=0;for(var i=0;i=e.display.viewTo||o.to().line0){t.blinker=setInterval(function(){return t.cursorDiv.style.visibility=(r=!r)?"":"hidden"},e.options.cursorBlinkRate)}else if(e.options.cursorBlinkRate<0){t.cursorDiv.style.visibility="hidden"}}function Ki(e){if(!e.state.focused){e.display.input.focus();Xi(e)}}function $i(e){e.state.delayingBlurEvent=true;setTimeout(function(){if(e.state.delayingBlurEvent){e.state.delayingBlurEvent=false;Yi(e)}},100)}function Xi(e,t){if(e.state.delayingBlurEvent){e.state.delayingBlurEvent=false}if(e.options.readOnly=="nocursor"){return}if(!e.state.focused){ge(e,"focus",e,t);e.state.focused=true;I(e.display.wrapper,"CodeMirror-focused");if(!e.curOp&&e.display.selForContextMenu!=e.doc.sel){e.display.input.reset();if(l){setTimeout(function(){return e.display.input.reset(true)},20)}}e.display.input.receivedFocus()}qi(e)}function Yi(e,t){if(e.state.delayingBlurEvent){return}if(e.state.focused){ge(e,"blur",e,t);e.state.focused=false;L(e.display.wrapper,"CodeMirror-focused")}clearInterval(e.display.blinker);setTimeout(function(){if(!e.state.focused){e.display.shift=false}},150)}function Qi(e){var t=e.display;var r=t.lineDiv.offsetTop;for(var i=0;i.005||h<-.005){Je(n.line,l);Zi(n.line);if(n.rest){for(var p=0;pe.display.sizerWidth){var d=Math.ceil(u/Ei(e.display));if(d>e.display.maxLineLength){e.display.maxLineLength=d;e.display.maxLine=n.line;e.display.maxLineChanged=true}}}}function Zi(e){if(e.widgets){for(var t=0;t=s){a=tt(t,nr(Ye(t,l))-e.wrapper.clientHeight);s=l}}return{from:a,to:Math.max(s,a+1)}}function en(e,t){if(ye(e,"scrollCursorIntoView")){return}var r=e.display,i=r.sizer.getBoundingClientRect(),n=null;if(t.top+i.top<0){n=true}else if(t.bottom+i.top>(window.innerHeight||document.documentElement.clientHeight)){n=false}if(n!=null&&!d){var a=E("div","​",null,"position: absolute;\n top: "+(t.top-r.viewOffset-jr(e.display))+"px;\n height: "+(t.bottom-t.top+qr(e)+r.barHeight)+"px;\n left: "+t.left+"px; width: "+Math.max(2,t.right-t.left)+"px;");e.display.lineSpace.appendChild(a);a.scrollIntoView(n);e.display.lineSpace.removeChild(a)}}function tn(e,t,r,i){if(i==null){i=0}var n;if(!e.options.lineWrapping&&t==r){t=t.ch?nt(t.line,t.sticky=="before"?t.ch-1:t.ch,"after"):t;r=t.sticky=="before"?nt(t.line,t.ch+1,"before"):t}for(var a=0;a<5;a++){var s=false;var o=vi(e,t);var l=!r||r==t?o:vi(e,r);n={left:Math.min(o.left,l.left),top:Math.min(o.top,l.top)-i,right:Math.max(o.left,l.left),bottom:Math.max(o.bottom,l.bottom)+i};var u=nn(e,n);var c=e.doc.scrollTop,f=e.doc.scrollLeft;if(u.scrollTop!=null){fn(e,u.scrollTop);if(Math.abs(e.doc.scrollTop-c)>1){s=true}}if(u.scrollLeft!=null){pn(e,u.scrollLeft);if(Math.abs(e.doc.scrollLeft-f)>1){s=true}}if(!s){break}}return n}function rn(e,t){var r=nn(e,t);if(r.scrollTop!=null){fn(e,r.scrollTop)}if(r.scrollLeft!=null){pn(e,r.scrollLeft)}}function nn(e,t){var r=e.display,i=Ai(e.display);if(t.top<0){t.top=0}var n=e.curOp&&e.curOp.scrollTop!=null?e.curOp.scrollTop:r.scroller.scrollTop;var a=$r(e),s={};if(t.bottom-t.top>a){t.bottom=t.top+a}var o=e.doc.height+Ur(r);var l=t.topo-i;if(t.topn+a){var c=Math.min(t.top,(u?o:t.bottom)-a);if(c!=n){s.scrollTop=c}}var f=e.curOp&&e.curOp.scrollLeft!=null?e.curOp.scrollLeft:r.scroller.scrollLeft;var h=Kr(e)-(e.options.fixedGutter?r.gutters.offsetWidth:0);var p=t.right-t.left>h;if(p){t.right=t.left+h}if(t.left<10){s.scrollLeft=0}else if(t.lefth+f-3){s.scrollLeft=t.right+(p?0:10)-h}return s}function an(e,t){if(t==null){return}un(e);e.curOp.scrollTop=(e.curOp.scrollTop==null?e.doc.scrollTop:e.curOp.scrollTop)+t}function sn(e){un(e);var t=e.getCursor();e.curOp.scrollToPos={from:t,to:t,margin:e.options.cursorScrollMargin}}function on(e,t,r){if(t!=null||r!=null){un(e)}if(t!=null){e.curOp.scrollLeft=t}if(r!=null){e.curOp.scrollTop=r}}function ln(e,t){un(e);e.curOp.scrollToPos=t}function un(e){var t=e.curOp.scrollToPos;if(t){e.curOp.scrollToPos=null;var r=gi(e,t.from),i=gi(e,t.to);cn(e,r,i,t.margin)}}function cn(e,t,r,i){var n=nn(e,{left:Math.min(t.left,r.left),top:Math.min(t.top,r.top)-i,right:Math.max(t.right,r.right),bottom:Math.max(t.bottom,r.bottom)+i});on(e,n.scrollLeft,n.scrollTop)}function fn(e,t){if(Math.abs(e.doc.scrollTop-t)<2){return}if(!r){Hn(e,{top:t})}hn(e,t,true);if(r){Hn(e)}On(e,100)}function hn(e,t,r){t=Math.min(e.display.scroller.scrollHeight-e.display.scroller.clientHeight,t);if(e.display.scroller.scrollTop==t&&!r){return}e.doc.scrollTop=t;e.display.scrollbars.setScrollTop(t);if(e.display.scroller.scrollTop!=t){e.display.scroller.scrollTop=t}}function pn(e,t,r,i){t=Math.min(t,e.display.scroller.scrollWidth-e.display.scroller.clientWidth);if((r?t==e.doc.scrollLeft:Math.abs(e.doc.scrollLeft-t)<2)&&!i){return}e.doc.scrollLeft=t;qn(e);if(e.display.scroller.scrollLeft!=t){e.display.scroller.scrollLeft=t}e.display.scrollbars.setScrollLeft(t)}function dn(e){var t=e.display,r=t.gutters.offsetWidth;var i=Math.round(e.doc.height+Ur(e.display));return{clientHeight:t.scroller.clientHeight,viewHeight:t.wrapper.clientHeight,scrollWidth:t.scroller.scrollWidth,clientWidth:t.scroller.clientWidth,viewWidth:t.wrapper.clientWidth,barLeft:e.options.fixedGutter?r:0,docHeight:i,scrollHeight:i+qr(e)+t.barHeight,nativeBarWidth:t.nativeBarWidth,gutterWidth:r}}var mn=function(e,t,r){this.cm=r;var i=this.vert=E("div",[E("div",null,null,"min-width: 1px")],"CodeMirror-vscrollbar");var n=this.horiz=E("div",[E("div",null,null,"height: 100%; min-height: 1px")],"CodeMirror-hscrollbar");i.tabIndex=n.tabIndex=-1;e(i);e(n);de(i,"scroll",function(){if(i.clientHeight){t(i.scrollTop,"vertical")}});de(n,"scroll",function(){if(n.clientWidth){t(n.scrollLeft,"horizontal")}});this.checkedZeroWidth=false;if(s&&o<8){this.horiz.style.minHeight=this.vert.style.minWidth="18px"}};mn.prototype.update=function(e){var t=e.scrollWidth>e.clientWidth+1;var r=e.scrollHeight>e.clientHeight+1;var i=e.nativeBarWidth;if(r){this.vert.style.display="block";this.vert.style.bottom=t?i+"px":"0";var n=e.viewHeight-(t?i:0);this.vert.firstChild.style.height=Math.max(0,e.scrollHeight-e.clientHeight+n)+"px"}else{this.vert.style.display="";this.vert.firstChild.style.height="0"}if(t){this.horiz.style.display="block";this.horiz.style.right=r?i+"px":"0";this.horiz.style.left=e.barLeft+"px";var a=e.viewWidth-e.barLeft-(r?i:0);this.horiz.firstChild.style.width=Math.max(0,e.scrollWidth-e.clientWidth+a)+"px"}else{this.horiz.style.display="";this.horiz.firstChild.style.width="0"}if(!this.checkedZeroWidth&&e.clientHeight>0){if(i==0){this.zeroWidthHack()}this.checkedZeroWidth=true}return{right:r?i:0,bottom:t?i:0}};mn.prototype.setScrollLeft=function(e){if(this.horiz.scrollLeft!=e){ +this.horiz.scrollLeft=e}if(this.disableHoriz){this.enableZeroWidthBar(this.horiz,this.disableHoriz,"horiz")}};mn.prototype.setScrollTop=function(e){if(this.vert.scrollTop!=e){this.vert.scrollTop=e}if(this.disableVert){this.enableZeroWidthBar(this.vert,this.disableVert,"vert")}};mn.prototype.zeroWidthHack=function(){var e=y&&!p?"12px":"18px";this.horiz.style.height=this.vert.style.width=e;this.horiz.style.pointerEvents=this.vert.style.pointerEvents="none";this.disableHoriz=new W;this.disableVert=new W};mn.prototype.enableZeroWidthBar=function(e,t,r){e.style.pointerEvents="auto";function i(){var n=e.getBoundingClientRect();var a=r=="vert"?document.elementFromPoint(n.right-1,(n.top+n.bottom)/2):document.elementFromPoint((n.right+n.left)/2,n.bottom-1);if(a!=e){e.style.pointerEvents="none"}else{t.set(1e3,i)}}t.set(1e3,i)};mn.prototype.clear=function(){var e=this.horiz.parentNode;e.removeChild(this.horiz);e.removeChild(this.vert)};var vn=function(){};vn.prototype.update=function(){return{bottom:0,right:0}};vn.prototype.setScrollLeft=function(){};vn.prototype.setScrollTop=function(){};vn.prototype.clear=function(){};function gn(e,t){if(!t){t=dn(e)}var r=e.display.barWidth,i=e.display.barHeight;yn(e,t);for(var n=0;n<4&&r!=e.display.barWidth||i!=e.display.barHeight;n++){if(r!=e.display.barWidth&&e.options.lineWrapping){Qi(e)}yn(e,dn(e));r=e.display.barWidth;i=e.display.barHeight}}function yn(e,t){var r=e.display;var i=r.scrollbars.update(t);r.sizer.style.paddingRight=(r.barWidth=i.right)+"px";r.sizer.style.paddingBottom=(r.barHeight=i.bottom)+"px";r.heightForcer.style.borderBottom=i.bottom+"px solid transparent";if(i.right&&i.bottom){r.scrollbarFiller.style.display="block";r.scrollbarFiller.style.height=i.bottom+"px";r.scrollbarFiller.style.width=i.right+"px"}else{r.scrollbarFiller.style.display=""}if(i.bottom&&e.options.coverGutterNextToScrollbar&&e.options.fixedGutter){r.gutterFiller.style.display="block";r.gutterFiller.style.height=i.bottom+"px";r.gutterFiller.style.width=t.gutterWidth+"px"}else{r.gutterFiller.style.display=""}}var xn={native:mn,null:vn};function bn(e){if(e.display.scrollbars){e.display.scrollbars.clear();if(e.display.scrollbars.addClass){L(e.display.wrapper,e.display.scrollbars.addClass)}}e.display.scrollbars=new xn[e.options.scrollbarStyle](function(t){e.display.wrapper.insertBefore(t,e.display.scrollbarFiller);de(t,"mousedown",function(){if(e.state.focused){setTimeout(function(){return e.display.input.focus()},0)}});t.setAttribute("cm-not-content","true")},function(t,r){if(r=="horizontal"){pn(e,t)}else{fn(e,t)}},e);if(e.display.scrollbars.addClass){I(e.display.wrapper,e.display.scrollbars.addClass)}}var wn=0;function kn(e){e.curOp={cm:e,viewChanged:false,startHeight:e.doc.height,forceUpdate:false,updateInput:0,typing:false,changeObjs:null,cursorActivityHandlers:null,cursorActivityCalled:0,selectionChanged:false,updateMaxLine:false,scrollLeft:null,scrollTop:null,scrollToPos:null,focus:false,id:++wn};Cr(e.curOp)}function Cn(e){var t=e.curOp;if(t){Lr(t,function(e){for(var t=0;t=r.viewTo)||r.maxLineChanged&&t.options.lineWrapping;e.update=e.mustUpdate&&new Rn(t,e.mustUpdate&&{top:e.scrollTop,ensure:e.scrollToPos},e.forceUpdate)}function Tn(e){e.updatedDisplay=e.mustUpdate&&zn(e.cm,e.update)}function An(e){var t=e.cm,r=t.display;if(e.updatedDisplay){Qi(t)}e.barMeasure=dn(t);if(r.maxLineChanged&&!t.options.lineWrapping){e.adjustWidthTo=Zr(t,r.maxLine,r.maxLine.text.length).left+3;t.display.sizerWidth=e.adjustWidthTo;e.barMeasure.scrollWidth=Math.max(r.scroller.clientWidth,r.sizer.offsetLeft+e.adjustWidthTo+qr(t)+t.display.barWidth);e.maxScrollLeft=Math.max(0,r.sizer.offsetLeft+e.adjustWidthTo-Kr(t))}if(e.updatedDisplay||e.selectionChanged){e.preparedSelection=r.input.prepareSelection()}}function En(e){var t=e.cm;if(e.adjustWidthTo!=null){t.display.sizer.style.minWidth=e.adjustWidthTo+"px";if(e.maxScrollLeft=e.display.viewTo){return}var r=+new Date+e.options.workTime;var i=yt(e,t.highlightFrontier);var n=[];t.iter(i.line,Math.min(t.first+t.size,e.display.viewTo+500),function(a){if(i.line>=e.display.viewFrom){var s=a.styles;var o=a.text.length>e.options.maxHighlightLength?qe(t.mode,i.state):null;var l=vt(e,a,i,true);if(o){i.state=o}a.styles=l.styles;var u=a.styleClasses,c=l.classes;if(c){a.styleClasses=c}else if(u){a.styleClasses=null}var f=!s||s.length!=a.styles.length||u!=c&&(!u||!c||u.bgClass!=c.bgClass||u.textClass!=c.textClass);for(var h=0;!f&&hr){On(e,e.options.workDelay);return true}});t.highlightFrontier=i.line;t.modeFrontier=Math.max(t.modeFrontier,i.line);if(n.length){Nn(e,function(){for(var t=0;t=r.viewFrom&&t.visible.to<=r.viewTo&&(r.updateLineNumbers==null||r.updateLineNumbers>=r.viewTo)&&r.renderedView==r.view&&zi(e)==0){return false}if(Kn(e)){Vi(e);t.dims=Mi(e)}var n=i.first+i.size;var a=Math.max(t.visible.from-e.options.viewportMargin,i.first);var s=Math.min(n,t.visible.to+e.options.viewportMargin);if(r.viewFroms&&r.viewTo-s<20){s=Math.min(n,r.viewTo)}if(Mt){a=er(e.doc,a);s=tr(e.doc,s)}var o=a!=r.viewFrom||s!=r.viewTo||r.lastWrapHeight!=t.wrapperHeight||r.lastWrapWidth!=t.wrapperWidth;Wi(e,a,s);r.viewOffset=nr(Ye(e.doc,r.viewFrom));e.display.mover.style.top=r.viewOffset+"px";var l=zi(e);if(!o&&l==0&&!t.force&&r.renderedView==r.view&&(r.updateLineNumbers==null||r.updateLineNumbers>=r.viewTo)){return false}var u=Fn(e);if(l>4){r.lineDiv.style.display="none"}jn(e,r.updateLineNumbers,t.dims);if(l>4){r.lineDiv.style.display=""}r.renderedView=r.view;Wn(u);T(r.cursorDiv);T(r.selectionDiv);r.gutters.style.height=r.sizer.style.minHeight=0;if(o){r.lastWrapHeight=t.wrapperHeight;r.lastWrapWidth=t.wrapperWidth;On(e,400)}r.updateLineNumbers=null;return true}function Bn(e,t){var r=t.viewport;for(var i=true;;i=false){if(!i||!e.options.lineWrapping||t.oldDisplayWidth==Kr(e)){if(r&&r.top!=null){r={top:Math.min(e.doc.height+Ur(e.display)-$r(e),r.top)}}t.visible=Ji(e.display,e.doc,r);if(t.visible.from>=e.display.viewFrom&&t.visible.to<=e.display.viewTo){break}}if(!zn(e,t)){break}Qi(e);var n=dn(e);Bi(e);gn(e,n);Gn(e,n);t.force=false}t.signal(e,"update",e);if(e.display.viewFrom!=e.display.reportedViewFrom||e.display.viewTo!=e.display.reportedViewTo){t.signal(e,"viewportChange",e,e.display.viewFrom,e.display.viewTo);e.display.reportedViewFrom=e.display.viewFrom;e.display.reportedViewTo=e.display.viewTo}}function Hn(e,t){var r=new Rn(e,t);if(zn(e,r)){Qi(e);Bn(e,r);var i=dn(e);Bi(e);gn(e,i);Gn(e,i);r.finish()}}function jn(e,t,r){var i=e.display,n=e.options.lineNumbers;var a=i.lineDiv,s=a.firstChild;function o(t){var r=t.nextSibling;if(l&&y&&e.display.currentWheelTarget==t){t.style.display="none"}else{t.parentNode.removeChild(t)}return r}var u=i.view,c=i.viewFrom;for(var f=0;f-1){d=false}Mr(e,h,c,r)}if(d){T(h.lineNumber);h.lineNumber.appendChild(document.createTextNode(it(e.options,c)))}s=h.node.nextSibling}c+=h.size}while(s){s=o(s)}}function Un(e){var t=e.gutters.offsetWidth;e.sizer.style.marginLeft=t+"px"}function Gn(e,t){e.display.sizer.style.minHeight=t.docHeight+"px";e.display.heightForcer.style.top=t.docHeight+"px";e.display.gutters.style.height=t.docHeight+e.display.barHeight+qr(e)+"px"}function qn(e){var t=e.display,r=t.view;if(!t.alignWidgets&&(!t.gutters.firstChild||!e.options.fixedGutter)){return}var i=Ni(t)-t.scroller.scrollLeft+e.doc.scrollLeft;var n=t.gutters.offsetWidth,a=i+"px";for(var s=0;so.clientWidth;var c=o.scrollHeight>o.clientHeight;if(!(n&&u||a&&c)){return}if(a&&y&&l){e:for(var h=t.target,p=s.view;h!=o;h=h.parentNode){for(var d=0;d=0&&at(e,n.to())<=0){return i}}return-1};var na=function(e,t){this.anchor=e;this.head=t};na.prototype.from=function(){return ut(this.anchor,this.head)};na.prototype.to=function(){return lt(this.anchor,this.head)};na.prototype.empty=function(){return this.head.line==this.anchor.line&&this.head.ch==this.anchor.ch};function aa(e,t,r){var i=e&&e.options.selectionsMayTouch;var n=t[r];t.sort(function(e,t){return at(e.from(),t.from())});r=z(t,n);for(var a=1;a0:l>=0){var u=ut(o.from(),s.from()),c=lt(o.to(),s.to());var f=o.empty()?s.from()==s.head:o.from()==o.head;if(a<=r){--r}t.splice(--a,2,new na(f?c:u,f?u:c))}}return new ia(t,r)}function sa(e,t){return new ia([new na(e,t||e)],0)}function oa(e){if(!e.text){return e.to}return nt(e.from.line+e.text.length-1,X(e.text).length+(e.text.length==1?e.from.ch:0))}function la(e,t){if(at(e,t.from)<0){return e}if(at(e,t.to)<=0){return oa(t)}var r=e.line+t.text.length-(t.to.line-t.from.line)-1,i=e.ch;if(e.line==t.to.line){i+=oa(t).ch-t.to.ch}return nt(r,i)}function ua(e,t){var r=[];for(var i=0;i1){e.remove(o.line+1,d-1)}e.insert(o.line+1,g)}Ar(e,"change",e,t)}function va(e,t,r){function i(e,n,a){if(e.linked){for(var s=0;s1&&!e.done[e.done.length-2].ranges){e.done.pop();return X(e.done)}}function Sa(e,t,r,i){var n=e.history;n.undone.length=0;var a=+new Date,s;var o;if((n.lastOp==i||n.lastOrigin==t.origin&&t.origin&&(t.origin.charAt(0)=="+"&&n.lastModTime>a-(e.cm?e.cm.options.historyEventDelay:500)||t.origin.charAt(0)=="*"))&&(s=Ca(n,n.lastOp==i))){o=X(s.changes);if(at(t.from,t.to)==0&&at(t.from,o.to)==0){o.to=oa(t)}else{s.changes.push(wa(e,t))}}else{var l=X(n.done);if(!l||!l.ranges){Aa(e.sel,n.done)}s={changes:[wa(e,t)],generation:n.generation};n.done.push(s);while(n.done.length>n.undoDepth){n.done.shift();if(!n.done[0].ranges){n.done.shift()}}}n.done.push(r);n.generation=++n.maxGeneration;n.lastModTime=n.lastSelTime=a;n.lastOp=n.lastSelOp=i;n.lastOrigin=n.lastSelOrigin=t.origin;if(!o){ge(e,"historyAdded")}}function La(e,t,r,i){var n=t.charAt(0);return n=="*"||n=="+"&&r.ranges.length==i.ranges.length&&r.somethingSelected()==i.somethingSelected()&&new Date-e.history.lastSelTime<=(e.cm?e.cm.options.historyEventDelay:500)}function Ta(e,t,r,i){var n=e.history,a=i&&i.origin;if(r==n.lastSelOp||a&&n.lastSelOrigin==a&&(n.lastModTime==n.lastSelTime&&n.lastOrigin==a||La(e,a,X(n.done),t))){n.done[n.done.length-1]=t}else{Aa(t,n.done)}n.lastSelTime=+new Date;n.lastSelOrigin=a;n.lastSelOp=r;if(i&&i.clearRedo!==false){ka(n.undone)}}function Aa(e,t){var r=X(t);if(!(r&&r.ranges&&r.equals(e))){t.push(e)}}function Ea(e,t,r,i){var n=t["spans_"+e.id],a=0;e.iter(Math.max(e.first,r),Math.min(e.first+e.size,i),function(r){if(r.markedSpans){(n||(n=t["spans_"+e.id]={}))[a]=r.markedSpans}++a})}function Ma(e){if(!e){return null}var t;for(var r=0;r-1){X(o)[f]=u[f];delete u[f]}}}}}}return i}function Ia(e,t,r,i){if(i){var n=e.anchor;if(r){var a=at(t,n)<0;if(a!=at(r,n)<0){n=t;t=r}else if(a!=at(t,r)<0){t=r}}return new na(n,t)}else{return new na(r||t,t)}}function Oa(e,t,r,i,n){if(n==null){n=e.cm&&(e.cm.display.shift||e.extend)}za(e,new ia([Ia(e.sel.primary(),t,r,n)],0),i)}function Da(e,t,r){var i=[];var n=e.cm&&(e.cm.display.shift||e.extend);for(var a=0;a=t.ch:o.to>t.ch))){if(n){ge(l,"beforeCursorEnter");if(l.explicitlyCleared){if(!a.markedSpans){break}else{--s;continue}}}if(!l.atomic){continue}if(r){var f=l.find(i<0?1:-1),h=void 0;if(i<0?c:u){f=Ka(e,f,-i,f&&f.line==t.line?a:null)}if(f&&f.line==t.line&&(h=at(f,r))&&(i<0?h<0:h>0)){return Ga(e,f,t,i,n)}}var p=l.find(i<0?-1:1);if(i<0?u:c){p=Ka(e,p,i,p.line==t.line?a:null)}return p?Ga(e,p,t,i,n):null}}}return t}function qa(e,t,r,i,n){var a=i||1;var s=Ga(e,t,r,a,n)||!n&&Ga(e,t,r,a,true)||Ga(e,t,r,-a,n)||!n&&Ga(e,t,r,-a,true);if(!s){e.cantEdit=true;return nt(e.first,0)}return s}function Ka(e,t,r,i){if(r<0&&t.ch==0){if(t.line>e.first){return ft(e,nt(t.line-1))}else{return null}}else if(r>0&&t.ch==(i||Ye(e,t.line)).text.length){if(t.line=0;--n){Qa(e,{from:i[n].from,to:i[n].to,text:n?[""]:t.text,origin:t.origin})}}else{Qa(e,t)}}function Qa(e,t){if(t.text.length==1&&t.text[0]==""&&at(t.from,t.to)==0){return}var r=ua(e,t);Sa(e,t,r,e.cm?e.cm.curOp.id:NaN);es(e,t,r,Ft(e,t));var i=[];va(e,function(e,r){if(!r&&z(i,e.history)==-1){as(e.history,t);i.push(e.history)}es(e,t,null,Ft(e,t))})}function Za(e,t,r){var i=e.cm&&e.cm.state.suppressEdits;if(i&&!r){return}var n=e.history,a,s=e.sel;var o=t=="undo"?n.done:n.undone,l=t=="undo"?n.undone:n.done;var u=0;for(;u=0;--p){var d=h(p);if(d)return d.v}}function Ja(e,t){if(t==0){return}e.first+=t;e.sel=new ia(Y(e.sel.ranges,function(e){return new na(nt(e.anchor.line+t,e.anchor.ch),nt(e.head.line+t,e.head.ch))}),e.sel.primIndex);if(e.cm){Di(e.cm,e.first,e.first-t,t);for(var r=e.cm.display,i=r.viewFrom;ie.lastLine()){return}if(t.from.linea){t={from:t.from,to:nt(a,Ye(e,a).text.length),text:[t.text[0]],origin:t.origin}}t.removed=Qe(e,t.from,t.to);if(!r){r=ua(e,t)}if(e.cm){ts(e.cm,t,i)}else{ma(e,t,i)}Ba(e,r,j);if(e.cantEdit&&qa(e,nt(e.firstLine(),0))){e.cantEdit=false}}function ts(e,t,r){var i=e.doc,n=e.display,a=t.from,s=t.to;var o=false,l=a.line;if(!e.options.lineWrapping){l=et(Qt(Ye(i,a.line)));i.iter(l,s.line+1,function(e){if(e==n.maxLine){o=true;return true}})}if(i.sel.contains(t.from,t.to)>-1){xe(e)}ma(i,t,r,_i(e));if(!e.options.lineWrapping){i.iter(l,a.line+t.text.length,function(e){var t=ar(e);if(t>n.maxLineLength){n.maxLine=e;n.maxLineLength=t;n.maxLineChanged=true;o=false}});if(o){e.curOp.updateMaxLine=true}}At(i,a.line);On(e,400);var u=t.text.length-(s.line-a.line)-1;if(t.full){Di(e)}else if(a.line==s.line&&t.text.length==1&&!da(e.doc,t)){Ri(e,a.line,"text")}else{Di(e,a.line,s.line+1,u)}var c=be(e,"changes"),f=be(e,"change");if(f||c){var h={from:a,to:s,text:t.text,removed:t.removed,origin:t.origin};if(f){Ar(e,"change",e,h)}if(c){(e.curOp.changeObjs||(e.curOp.changeObjs=[])).push(h)}}e.display.selForContextMenu=null}function rs(e,t,r,i,n){var a;if(!i){i=r}if(at(i,r)<0){a=[i,r],r=a[0],i=a[1]}if(typeof t=="string"){t=e.splitLines(t)}Ya(e,{from:r,to:i,text:t,origin:n})}function is(e,t,r,i){if(r1||!(this.children[0]instanceof os))){var l=[];this.collapse(l);this.children=[new os(l)];this.children[0].parent=this}},collapse:function(e){var t=this;for(var r=0;r50){var o=a.lines.length%25+25;for(var l=o;l10);e.parent.maybeSpill()},iterN:function(e,t,r){var i=this;for(var n=0;nt.display.maxLineLength){t.display.maxLine=c;t.display.maxLineLength=f;t.display.maxLineChanged=true}}}if(n!=null&&t&&this.collapsed){Di(t,n,a+1)}this.lines.length=0;this.explicitlyCleared=true;if(this.atomic&&this.doc.cantEdit){this.doc.cantEdit=false;if(t){ja(t.doc)}}if(t){Ar(t,"markerCleared",t,this,n,a)}if(r){Cn(t)}if(this.parent){this.parent.clear()}};ps.prototype.find=function(e,t){var r=this;if(e==null&&this.type=="bookmark"){e=1}var i,n;for(var a=0;a0||s==0&&a.clearWhenEmpty!==false){return a}if(a.replacedWith){a.collapsed=true;a.widgetNode=M("span",[a.replacedWith],"CodeMirror-widget");if(!i.handleMouseEvents){a.widgetNode.setAttribute("cm-ignore-events","true")}if(i.insertLeft){a.widgetNode.insertLeft=true}}if(a.collapsed){if(Yt(e,t.line,t,r,a)||t.line!=r.line&&Yt(e,r.line,t,r,a)){throw new Error("Inserting collapsed marker partially overlapping an existing one")}_t()}if(a.addToHistory){Sa(e,{from:t,to:r,origin:"markText"},e.sel,NaN)}var o=t.line,l=e.cm,u;e.iter(o,r.line+1,function(e){if(l&&a.collapsed&&!l.options.lineWrapping&&Qt(e)==l.display.maxLine){u=true}if(a.collapsed&&o!=t.line){Je(e,0)}Dt(e,new Pt(a,o==t.line?t.ch:null,o==r.line?r.ch:null));++o});if(a.collapsed){e.iter(t.line,r.line+1,function(t){if(rr(e,t)){Je(t,0)}})}if(a.clearOnEnter){de(a,"beforeCursorEnter",function(){return a.clear()})}if(a.readOnly){Nt();if(e.history.done.length||e.history.undone.length){e.clearHistory()}}if(a.collapsed){a.id=++hs;a.atomic=true}if(l){if(u){l.curOp.updateMaxLine=true}if(a.collapsed){Di(l,t.line,r.line+1)}else if(a.className||a.startStyle||a.endStyle||a.css||a.attributes||a.title){for(var c=t.line;c<=r.line;c++){Ri(l,c,"text")}}if(a.atomic){ja(l.doc)}Ar(l,"markerAdded",l,a)}return a}var ms=function(e,t){var r=this;this.markers=e;this.primary=t;for(var i=0;i=0;u--){Ya(i,n[u])}if(l){Wa(this,l)}else if(this.cm){sn(this.cm)}}),undo:In(function(){Za(this,"undo")}),redo:In(function(){Za(this,"redo")}),undoSelection:In(function(){Za(this,"undo",true)}),redoSelection:In(function(){Za(this,"redo",true)}),setExtending:function(e){this.extend=e},getExtending:function(){return this.extend},historySize:function(){var e=this.history,t=0,r=0;for(var i=0;i=e.ch)){t.push(n.marker.parent||n.marker)}}}return t},findMarks:function(e,t,r){e=ft(this,e);t=ft(this,t);var i=[],n=e.line;this.iter(e.line,t.line+1,function(a){var s=a.markedSpans;if(s){for(var o=0;o=l.to||l.from==null&&n!=e.line||l.from!=null&&n==t.line&&l.from>=t.ch)&&(!r||r(l.marker))){i.push(l.marker.parent||l.marker)}}}++n});return i},getAllMarks:function(){var e=[];this.iter(function(t){var r=t.markedSpans;if(r){for(var i=0;ie){t=e;return true}e-=a;++r});return ft(this,nt(r,t))},indexFromPos:function(e){e=ft(this,e);var t=e.ch;if(e.linet){t=e.from}if(e.to!=null&&e.to-1){t.state.draggingText(e);setTimeout(function(){return t.display.input.focus()},20);return}try{var c=e.dataTransfer.getData("Text");if(c){var f;if(t.state.draggingText&&!t.state.draggingText.copy){f=t.listSelections()}Ba(t.doc,sa(r,r));if(f){for(var h=0;h=0;t--){rs(e.doc,"",i[t].from,i[t].to,"+delete")}sn(e)})}function Gs(e,t,r){var i=se(e.text,t+r,r);return i<0||i>e.text.length?null:i}function qs(e,t,r){var i=Gs(e,t.ch,r);return i==null?null:new nt(t.line,i,r<0?"after":"before")}function Ks(e,t,r,i,n){if(e){var a=he(r,t.doc.direction);if(a){var s=n<0?X(a):a[0];var o=n<0==(s.level==1);var l=o?"after":"before";var u;if(s.level>0||t.doc.direction=="rtl"){var c=ei(t,r);u=n<0?r.text.length-1:0;var f=ti(t,c,u).top;u=oe(function(e){return ti(t,c,e).top==f},n<0==(s.level==1)?s.from:s.to-1,u);if(l=="before"){u=Gs(r,u,1)}}else{u=n<0?s.to:s.from}return new nt(i,u,l)}}return new nt(i,n<0?r.text.length:0,n<0?"before":"after")}function $s(e,t,r,i){var n=he(t,e.doc.direction);if(!n){return qs(t,r,i)}if(r.ch>=t.text.length){r.ch=t.text.length;r.sticky="before"}else if(r.ch<=0){r.ch=0;r.sticky="after"}var a=ce(n,r.ch,r.sticky),s=n[a];if(e.doc.direction=="ltr"&&s.level%2==0&&(i>0?s.to>r.ch:s.from=s.from&&h>=c.begin:h<=s.to&&h<=c.end)){var p=f?"before":"after";return new nt(r.line,h,p)}}var d=function(e,t,i){var a=function(e,t){return t?new nt(r.line,o(e,1),"before"):new nt(r.line,e,"after")};for(;e>=0&&e0==(s.level!=1);var u=l?i.begin:o(i.end,-1);if(s.from<=u&&u0?c.end:o(c.begin,-1);if(v!=null&&!(i>0&&v==t.text.length)){m=d(i>0?0:n.length-1,i,u(v));if(m){return m}}return null}var Xs={selectAll:$a,singleSelection:function(e){return e.setSelection(e.getCursor("anchor"),e.getCursor("head"),j)},killLine:function(e){return Us(e,function(t){if(t.empty()){var r=Ye(e.doc,t.head.line).text.length;if(t.head.ch==r&&t.head.line0){n=new nt(n.line,n.ch+1);e.replaceRange(a.charAt(n.ch-1)+a.charAt(n.ch-2),nt(n.line,n.ch-2),n,"+transpose")}else if(n.line>e.doc.first){var s=Ye(e.doc,n.line-1).text;if(s){n=new nt(n.line,1);e.replaceRange(a.charAt(0)+e.doc.lineSeparator()+s.charAt(s.length-1),nt(n.line-1,s.length-1),n,"+transpose")}}}r.push(new na(n,n))}e.setSelections(r)})},newlineAndIndent:function(e){return Nn(e,function(){var t=e.listSelections();for(var r=t.length-1;r>=0;r--){e.replaceRange(e.doc.lineSeparator(),t[r].anchor,t[r].head,"+input")}t=e.listSelections();for(var i=0;ie&&at(t,this.pos)==0&&r==this.button};var po,mo;function vo(e,t){var r=+new Date;if(mo&&mo.compare(r,e,t)){po=mo=null;return"triple"}else if(po&&po.compare(r,e,t)){mo=new ho(r,e,t);po=null;return"double"}else{po=new ho(r,e,t);mo=null;return"single"}}function go(e){var t=this,r=t.display;if(ye(t,e)||r.activeTouch&&r.input.supportsTouch()){return}r.input.ensurePolled();r.shift=e.shiftKey;if(Hr(r,e)){if(!l){r.scroller.draggable=false;setTimeout(function(){return r.scroller.draggable=true},100)}return}if(To(t,e)){return}var i=Ii(t,e),n=Ae(e),a=i?vo(i,n):"single";window.focus();if(n==1&&t.state.selectingText){t.state.selectingText(e)}if(i&&yo(t,n,i,a,e)){return}if(n==1){if(i){bo(t,i,a,e)}else if(Te(e)==r.scroller){ke(e)}}else if(n==2){if(i){Oa(t.doc,i)}setTimeout(function(){return r.input.focus()},20)}else if(n==3){if(C){t.display.input.onContextMenu(e)}else{$i(t)}}}function yo(e,t,r,i,n){var a="Click";if(i=="double"){a="Double"+a}else if(i=="triple"){a="Triple"+a}a=(t==1?"Left":t==2?"Middle":"Right")+a;return ro(e,Bs(a,n),n,function(t){if(typeof t=="string"){t=Xs[t]}if(!t){return false}var i=false;try{if(e.isReadOnly()){e.state.suppressEdits=true}i=t(e,r)!=H}finally{e.state.suppressEdits=false}return i})}function xo(e,t,r){var i=e.getOption("configureMouse");var n=i?i(e,t,r):{};if(n.unit==null){var a=x?r.shiftKey&&r.metaKey:r.altKey;n.unit=a?"rectangle":t=="single"?"char":t=="double"?"word":"line"}if(n.extend==null||e.doc.extend){n.extend=e.doc.extend||r.shiftKey}if(n.addNew==null){n.addNew=y?r.metaKey:r.ctrlKey}if(n.moveOnDrag==null){n.moveOnDrag=!(y?r.altKey:r.ctrlKey)}return n}function bo(e,t,r,i){if(s){setTimeout(R(Ki,e),0)}else{e.curOp.focus=P()}var n=xo(e,r,i);var a=e.doc.sel,o;if(e.options.dragDrop&&Ee&&!e.isReadOnly()&&r=="single"&&(o=a.contains(t))>-1&&(at((o=a.ranges[o]).from(),t)<0||t.xRel>0)&&(at(o.to(),t)>0||t.xRel<0)){wo(e,i,t,n)}else{Co(e,i,t,n)}}function wo(e,t,r,i){var n=e.display,a=false;var u=_n(e,function(t){if(l){n.scroller.draggable=false}e.state.draggingText=false;ve(n.wrapper.ownerDocument,"mouseup",u);ve(n.wrapper.ownerDocument,"mousemove",c);ve(n.scroller,"dragstart",f);ve(n.scroller,"drop",u);if(!a){ke(t);if(!i.addNew){Oa(e.doc,r,null,null,i.extend)}if(l||s&&o==9){setTimeout(function(){n.wrapper.ownerDocument.body.focus();n.input.focus()},20)}else{n.input.focus()}}});var c=function(e){a=a||Math.abs(t.clientX-e.clientX)+Math.abs(t.clientY-e.clientY)>=10};var f=function(){return a=true};if(l){n.scroller.draggable=true}e.state.draggingText=u;u.copy=!i.moveOnDrag;if(n.scroller.dragDrop){n.scroller.dragDrop()}de(n.wrapper.ownerDocument,"mouseup",u);de(n.wrapper.ownerDocument,"mousemove",c);de(n.scroller,"dragstart",f);de(n.scroller,"drop",u);$i(e);setTimeout(function(){return n.input.focus()},20)}function ko(e,t,r){if(r=="char"){return new na(t,t)}if(r=="word"){return e.findWordAt(t)}if(r=="line"){return new na(nt(t.line,0),ft(e.doc,nt(t.line+1,0)))}var i=r(e,t);return new na(i.from,i.to)}function Co(e,t,r,i){var n=e.display,a=e.doc;ke(t);var s,o,l=a.sel,u=l.ranges;if(i.addNew&&!i.extend){o=a.sel.contains(r);if(o>-1){s=u[o]}else{s=new na(r,r)}}else{s=a.sel.primary();o=a.sel.primIndex}if(i.unit=="rectangle"){if(!i.addNew){s=new na(r,r)}r=Ii(e,t,true,true);o=-1}else{var c=ko(e,r,i.unit);if(i.extend){s=Ia(s,c.anchor,c.head,i.extend)}else{s=c}}if(!i.addNew){o=0;za(a,new ia([s],0),U);l=a.sel}else if(o==-1){o=u.length;za(a,aa(e,u.concat([s]),o),{scroll:false,origin:"*mouse"})}else if(u.length>1&&u[o].empty()&&i.unit=="char"&&!i.extend){za(a,aa(e,u.slice(0,o).concat(u.slice(o+1)),0),{scroll:false,origin:"*mouse"});l=a.sel}else{Ra(a,o,s,U)}var f=r;function h(t){if(at(f,t)==0){return}f=t;if(i.unit=="rectangle"){var n=[],u=e.options.tabSize;var c=F(Ye(a,r.line).text,r.ch,u);var h=F(Ye(a,t.line).text,t.ch,u);var p=Math.min(c,h),d=Math.max(c,h);for(var m=Math.min(r.line,t.line),v=Math.min(e.lastLine(),Math.max(r.line,t.line));m<=v;m++){var g=Ye(a,m).text,y=q(g,p,u);if(p==d){n.push(new na(nt(m,y),nt(m,y)))}else if(g.length>y){n.push(new na(nt(m,y),nt(m,q(g,d,u))))}}if(!n.length){n.push(new na(r,r))}za(a,aa(e,l.ranges.slice(0,o).concat(n),o),{origin:"*mouse",scroll:false});e.scrollIntoView(t)}else{var x=s;var b=ko(e,t,i.unit);var w=x.anchor,k;if(at(b.anchor,w)>0){k=b.head;w=ut(x.from(),b.anchor)}else{k=b.anchor;w=lt(x.to(),b.head)}var C=l.ranges.slice(0);C[o]=So(e,new na(ft(a,w),k));za(a,aa(e,C,o),U)}}var p=n.wrapper.getBoundingClientRect();var d=0;function m(t){var r=++d;var s=Ii(e,t,true,i.unit=="rectangle");if(!s){return}if(at(s,f)!=0){e.curOp.focus=P();h(s);var o=Ji(n,a);if(s.line>=o.to||s.linep.bottom?20:0;if(l){setTimeout(_n(e,function(){if(d!=r){return}n.scroller.scrollTop+=l;m(t)}),50)}}}function v(t){e.state.selectingText=false;d=Infinity;if(t){ke(t);n.input.focus()}ve(n.wrapper.ownerDocument,"mousemove",g);ve(n.wrapper.ownerDocument,"mouseup",y);a.history.lastSelOrigin=null}var g=_n(e,function(e){if(e.buttons===0||!Ae(e)){v(e)}else{m(e)}});var y=_n(e,v);e.state.selectingText=y;de(n.wrapper.ownerDocument,"mousemove",g);de(n.wrapper.ownerDocument,"mouseup",y)}function So(e,t){var r=t.anchor;var i=t.head;var n=Ye(e.doc,r.line);if(at(r,i)==0&&r.sticky==i.sticky){return t}var a=he(n);if(!a){return t}var s=ce(a,r.ch,r.sticky),o=a[s];if(o.from!=r.ch&&o.to!=r.ch){return t}var l=s+(o.from==r.ch==(o.level!=1)?0:1);if(l==0||l==a.length){return t}var u;if(i.line!=r.line){u=(i.line-r.line)*(e.doc.direction=="ltr"?1:-1)>0}else{var c=ce(a,i.ch,i.sticky);var f=c-s||(i.ch-r.ch)*(o.level==1?-1:1);if(c==l-1||c==l){u=f<0}else{u=f>0}}var h=a[l+(u?-1:0)];var p=u==(h.level==1);var d=p?h.from:h.to,m=p?"after":"before";return r.ch==d&&r.sticky==m?t:new na(new nt(r.line,d,m),i)}function Lo(e,t,r,i){var n,a;if(t.touches){n=t.touches[0].clientX;a=t.touches[0].clientY}else{try{n=t.clientX;a=t.clientY}catch(t){return false}}if(n>=Math.floor(e.display.gutters.getBoundingClientRect().right)){return false}if(i){ke(t)}var s=e.display;var o=s.lineDiv.getBoundingClientRect();if(a>o.bottom||!be(e,r)){return Se(t)}a-=o.top-s.viewOffset;for(var l=0;l=n){var c=tt(e.doc,a);var f=e.display.gutterSpecs[l];ge(e,r,e,c,f.className,t);return Se(t)}}}function To(e,t){return Lo(e,t,"gutterClick",true)}function Ao(e,t){if(Hr(e.display,t)||Eo(e,t)){return}if(ye(e,t,"contextmenu")){return}if(!C){e.display.input.onContextMenu(t)}}function Eo(e,t){if(!be(e,"gutterContextMenu")){return false}return Lo(e,t,"gutterContextMenu",false)}function Mo(e){e.display.wrapper.className=e.display.wrapper.className.replace(/\s*cm-s-\S+/g,"")+e.options.theme.replace(/(^|\s)\s*/g," cm-s-");ui(e)}var No={toString:function(){return"CodeMirror.Init"}};var _o={};var Po={};function Io(e){var t=e.optionHandlers;function r(r,i,n,a){e.defaults[r]=i;if(n){t[r]=a?function(e,t,r){if(r!=No){n(e,t,r)}}:n}}e.defineOption=r;e.Init=No;r("value","",function(e,t){return e.setValue(t)},true);r("mode",null,function(e,t){e.doc.modeOption=t;ha(e)},true);r("indentUnit",2,ha,true);r("indentWithTabs",false);r("smartIndent",true);r("tabSize",4,function(e){pa(e);ui(e);Di(e)},true);r("lineSeparator",null,function(e,t){e.doc.lineSep=t;if(!t){return}var r=[],i=e.doc.first;e.doc.iter(function(e){for(var n=0;;){var a=e.text.indexOf(t,n);if(a==-1){break}n=a+t.length;r.push(nt(i,a))}i++});for(var n=r.length-1;n>=0;n--){rs(e.doc,t,r[n],nt(r[n].line,r[n].ch+t.length))}});r("specialChars",/[\u0000-\u001f\u007f-\u009f\u00ad\u061c\u200b-\u200f\u2028\u2029\ufeff\ufff9-\ufffc]/g,function(e,t,r){e.state.specialChars=new RegExp(t.source+(t.test("\t")?"":"|\t"),"g");if(r!=No){e.refresh()}});r("specialCharPlaceholder",dr,function(e){return e.refresh()},true);r("electricChars",true);r("inputStyle",g?"contenteditable":"textarea",function(){throw new Error("inputStyle can not (yet) be changed in a running editor")},true);r("spellcheck",false,function(e,t){return e.getInputField().spellcheck=t},true);r("autocorrect",false,function(e,t){return e.getInputField().autocorrect=t},true);r("autocapitalize",false,function(e,t){return e.getInputField().autocapitalize=t},true);r("rtlMoveVisually",!b);r("wholeLineUpdateBefore",true);r("theme","default",function(e){Mo(e);Yn(e)},true);r("keyMap","default",function(e,t,r){var i=js(t);var n=r!=No&&js(r);if(n&&n.detach){n.detach(e,i)}if(i.attach){i.attach(e,n||null)}});r("extraKeys",null);r("configureMouse",null);r("lineWrapping",false,Do,true);r("gutters",[],function(e,t){e.display.gutterSpecs=$n(t,e.options.lineNumbers);Yn(e)},true);r("fixedGutter",true,function(e,t){e.display.gutters.style.left=t?Ni(e.display)+"px":"0";e.refresh()},true);r("coverGutterNextToScrollbar",false,function(e){return gn(e)},true);r("scrollbarStyle","native",function(e){bn(e);gn(e);e.display.scrollbars.setScrollTop(e.doc.scrollTop);e.display.scrollbars.setScrollLeft(e.doc.scrollLeft)},true);r("lineNumbers",false,function(e,t){e.display.gutterSpecs=$n(e.options.gutters,t);Yn(e)},true);r("firstLineNumber",1,Yn,true);r("lineNumberFormatter",function(e){return e},Yn,true);r("showCursorWhenSelecting",false,Bi,true);r("resetSelectionOnContextMenu",true);r("lineWiseCopyCut",true);r("pasteLinesPerSelection",true);r("selectionsMayTouch",false);r("readOnly",false,function(e,t){if(t=="nocursor"){Yi(e);e.display.input.blur()}e.display.input.readOnlyChanged(t)});r("disableInput",false,function(e,t){if(!t){e.display.input.reset()}},true);r("dragDrop",true,Oo);r("allowDropFileTypes",null);r("cursorBlinkRate",530);r("cursorScrollMargin",0);r("cursorHeight",1,Bi,true);r("singleCursorHeightPerLine",true,Bi,true);r("workTime",100);r("workDelay",100);r("flattenSpans",true,pa,true);r("addModeClass",false,pa,true);r("pollInterval",100);r("undoDepth",200,function(e,t){return e.doc.history.undoDepth=t});r("historyEventDelay",1250);r("viewportMargin",10,function(e){return e.refresh()},true);r("maxHighlightLength",1e4,pa,true);r("moveInputWithCursor",true,function(e,t){if(!t){e.display.input.resetPosition()}});r("tabindex",null,function(e,t){return e.display.input.getField().tabIndex=t||""});r("autofocus",null);r("direction","ltr",function(e,t){return e.doc.setDirection(t)},true);r("phrases",null)}function Oo(e,t,r){var i=r&&r!=No;if(!t!=!i){var n=e.display.dragFunctions;var a=t?de:ve;a(e.display.scroller,"dragstart",n.start);a(e.display.scroller,"dragenter",n.enter);a(e.display.scroller,"dragover",n.over);a(e.display.scroller,"dragleave",n.leave);a(e.display.scroller,"drop",n.drop)}}function Do(e){if(e.options.lineWrapping){I(e.display.wrapper,"CodeMirror-wrap");e.display.sizer.style.minWidth="";e.display.sizerWidth=null}else{L(e.display.wrapper,"CodeMirror-wrap");sr(e)}Pi(e);Di(e);ui(e);setTimeout(function(){return gn(e)},100)}function Ro(e,t){var r=this;if(!(this instanceof Ro)){return new Ro(e,t)}this.options=t=t?V(t):{};V(_o,t,false);var i=t.value;if(typeof i=="string"){i=new ws(i,t.mode,null,t.lineSeparator,t.direction)}else if(t.mode){i.modeOption=t.mode}this.doc=i;var n=new Ro.inputStyles[t.inputStyle](this);var a=this.display=new Qn(e,i,n,t);a.wrapper.CodeMirror=this;Mo(this);if(t.lineWrapping){this.display.wrapper.className+=" CodeMirror-wrap"}bn(this);this.state={keyMaps:[],overlays:[],modeGen:0,overwrite:false,delayingBlurEvent:false,focused:false,suppressEdits:false,pasteIncoming:-1,cutIncoming:-1,selectingText:false,draggingText:false,highlight:new W,keySeq:null,specialChars:null};if(t.autofocus&&!g){a.input.focus()}if(s&&o<11){setTimeout(function(){return r.display.input.reset(true)},20)}Vo(this);Ms();kn(this);this.curOp.forceUpdate=true;ga(this,i);if(t.autofocus&&!g||this.hasFocus()){setTimeout(R(Xi,this),20)}else{Yi(this)}for(var u in Po){if(Po.hasOwnProperty(u)){Po[u](r,t[u],No)}}Kn(this);if(t.finishInit){t.finishInit(this)}for(var c=0;c20*20}de(t.scroller,"touchstart",function(n){if(!ye(e,n)&&!a(n)&&!To(e,n)){t.input.ensurePolled();clearTimeout(r);var s=+new Date;t.activeTouch={start:s,moved:false,prev:s-i.end<=300?i:null};if(n.touches.length==1){t.activeTouch.left=n.touches[0].pageX;t.activeTouch.top=n.touches[0].pageY}}});de(t.scroller,"touchmove",function(){if(t.activeTouch){t.activeTouch.moved=true}});de(t.scroller,"touchend",function(r){var i=t.activeTouch;if(i&&!Hr(t,r)&&i.left!=null&&!i.moved&&new Date-i.start<300){var a=e.coordsChar(t.activeTouch,"page"),s;if(!i.prev||l(i,i.prev)){s=new na(a,a)}else if(!i.prev.prev||l(i,i.prev.prev)){s=e.findWordAt(a)}else{s=new na(nt(a.line,0),ft(e.doc,nt(a.line+1,0)))}e.setSelection(s.anchor,s.head);e.focus();ke(r)}n()});de(t.scroller,"touchcancel",n);de(t.scroller,"scroll",function(){if(t.scroller.clientHeight){fn(e,t.scroller.scrollTop);pn(e,t.scroller.scrollLeft,true);ge(e,"scroll",e)}});de(t.scroller,"mousewheel",function(t){return ra(e,t)});de(t.scroller,"DOMMouseScroll",function(t){return ra(e,t)});de(t.wrapper,"scroll",function(){return t.wrapper.scrollTop=t.wrapper.scrollLeft=0});t.dragFunctions={enter:function(t){if(!ye(e,t)){Le(t)}},over:function(t){if(!ye(e,t)){Ls(e,t);Le(t)}},start:function(t){return Ss(e,t)},drop:_n(e,Cs),leave:function(t){if(!ye(e,t)){Ts(e)}}};var u=t.input.getField();de(u,"keyup",function(t){return uo.call(e,t)});de(u,"keydown",_n(e,oo));de(u,"keypress",_n(e,co));de(u,"focus",function(t){return Xi(e,t)});de(u,"blur",function(t){return Yi(e,t)})}var Fo=[];Ro.defineInitHook=function(e){return Fo.push(e)};function Wo(e,t,r,i){var n=e.doc,a;if(r==null){r="add"}if(r=="smart"){if(!n.mode.indent){r="prev"}else{a=yt(e,t).state}}var s=e.options.tabSize;var o=Ye(n,t),l=F(o.text,null,s);if(o.stateAfter){o.stateAfter=null}var u=o.text.match(/^\s*/)[0],c;if(!i&&!/\S/.test(o.text)){c=0;r="not"}else if(r=="smart"){c=n.mode.indent(a,o.text.slice(u.length),o.text);if(c==H||c>150){if(!i){return}r="prev"}}if(r=="prev"){if(t>n.first){c=F(Ye(n,t-1).text,null,s)}else{c=0}}else if(r=="add"){c=l+e.options.indentUnit}else if(r=="subtract"){c=l-e.options.indentUnit}else if(typeof r=="number"){c=l+r}c=Math.max(0,c);var f="",h=0;if(e.options.indentWithTabs){for(var p=Math.floor(c/s);p;--p){h+=s;f+="\t"}}if(hs;var l=Ie(t),u=null;if(o&&i.ranges.length>1){if(zo&&zo.text.join("\n")==t){if(i.ranges.length%zo.text.length==0){u=[];for(var c=0;c=0;h--){var p=i.ranges[h];var d=p.from(),m=p.to();if(p.empty()){if(r&&r>0){d=nt(d.line,d.ch-r)}else if(e.state.overwrite&&!o){m=nt(m.line,Math.min(Ye(a,m.line).text.length,m.ch+X(l).length))}else if(o&&zo&&zo.lineWise&&zo.text.join("\n")==t){d=m=nt(d.line,0)}}var v={from:d,to:m,text:u?u[h%u.length]:l,origin:n||(o?"paste":e.state.cutIncoming>s?"cut":"+input")};Ya(e.doc,v);Ar(e,"inputRead",e,v)}if(t&&!o){Uo(e,t)}sn(e);if(e.curOp.updateInput<2){e.curOp.updateInput=f}e.curOp.typing=true;e.state.pasteIncoming=e.state.cutIncoming=-1}function jo(e,t){var r=e.clipboardData&&e.clipboardData.getData("Text");if(r){e.preventDefault();if(!t.isReadOnly()&&!t.options.disableInput){Nn(t,function(){return Ho(t,r,0,null,"paste")})}return true}}function Uo(e,t){if(!e.options.electricChars||!e.options.smartIndent){return}var r=e.doc.sel;for(var i=r.ranges.length-1;i>=0;i--){var n=r.ranges[i];if(n.head.ch>100||i&&r.ranges[i-1].head.line==n.head.line){continue}var a=e.getModeAt(n.head);var s=false;if(a.electricChars){for(var o=0;o-1){s=Wo(e,n.head.line,"smart");break}}}else if(a.electricInput){if(a.electricInput.test(Ye(e.doc,n.head.line).text.slice(0,n.head.ch))){s=Wo(e,n.head.line,"smart")}}if(s){Ar(e,"electricInput",e,n.head.line)}}}function Go(e){var t=[],r=[];for(var i=0;i0){Ra(t.doc,n,new na(s,c[n].to()),j)}}else if(a.head.line>i){Wo(t,a.head.line,e,true);i=a.head.line;if(n==t.doc.sel.primIndex){sn(t)}}}}),getTokenAt:function(e,t){return Ct(this,e,t)},getLineTokens:function(e,t){return Ct(this,nt(e),t,true)},getTokenTypeAt:function(e){e=ft(this.doc,e);var t=gt(this,Ye(this.doc,e.line));var r=0,i=(t.length-1)/2,n=e.ch;var a;if(n==0){a=t[2]}else{for(;;){var s=r+i>>1;if((s?t[s*2-1]:0)>=n){i=s}else if(t[s*2+1]a){e=a;i=true}n=Ye(this.doc,e)}else{n=e}return pi(this,n,{top:0,left:0},t||"page",r||i).top+(i?this.doc.height-nr(n):0)},defaultTextHeight:function(){return Ai(this.display)},defaultCharWidth:function(){return Ei(this.display)},getViewport:function(){return{from:this.display.viewFrom,to:this.display.viewTo}},addWidget:function(e,t,r,i,n){var a=this.display;e=vi(this,ft(this.doc,e));var s=e.bottom,o=e.left;t.style.position="absolute";t.setAttribute("cm-ignore-events","true");this.display.input.setUneditable(t);a.sizer.appendChild(t);if(i=="over"){s=e.top}else if(i=="above"||i=="near"){var l=Math.max(a.wrapper.clientHeight,this.doc.height),u=Math.max(a.sizer.clientWidth,a.lineSpace.clientWidth);if((i=="above"||e.bottom+t.offsetHeight>l)&&e.top>t.offsetHeight){s=e.top-t.offsetHeight}else if(e.bottom+t.offsetHeight<=l){s=e.bottom}if(o+t.offsetWidth>u){o=u-t.offsetWidth}}t.style.top=s+"px";t.style.left=t.style.right="";if(n=="right"){o=a.sizer.clientWidth-t.offsetWidth;t.style.right="0px"}else{if(n=="left"){o=0}else if(n=="middle"){o=(a.sizer.clientWidth-t.offsetWidth)/2}t.style.left=o+"px"}if(r){rn(this,{left:o,top:s,right:o+t.offsetWidth,bottom:s+t.offsetHeight})}},triggerOnKeyDown:Pn(oo),triggerOnKeyPress:Pn(co),triggerOnKeyUp:uo,triggerOnMouseDown:Pn(go),execCommand:function(e){if(Xs.hasOwnProperty(e)){return Xs[e].call(null,this)}},triggerElectric:Pn(function(e){Uo(this,e)}),findPosH:function(e,t,r,i){var n=this;var a=1;if(t<0){a=-1;t=-t}var s=ft(this.doc,e);for(var o=0;o0&&o(r.charAt(i-1))){--i}while(n.5){Pi(this)}ge(this,"refresh",this)}),swapDoc:Pn(function(e){var t=this.doc;t.cm=null;if(this.state.selectingText){this.state.selectingText()}ga(this,e);ui(this);this.display.input.reset();on(this,e.scrollLeft,e.scrollTop);this.curOp.forceScroll=true;Ar(this,"swapDoc",this,t);return t}),phrase:function(e){var t=this.options.phrases;return t&&Object.prototype.hasOwnProperty.call(t,e)?t[e]:e},getInputField:function(){return this.display.input.getField()},getWrapperElement:function(){return this.display.wrapper},getScrollerElement:function(){return this.display.scroller},getGutterElement:function(){return this.display.gutters}};we(e);e.registerHelper=function(t,i,n){if(!r.hasOwnProperty(t)){r[t]=e[t]={_global:[]}}r[t][i]=n};e.registerGlobalHelper=function(t,i,n,a){e.registerHelper(t,i,a);r[t]._global.push({pred:n,val:a})}}function Xo(e,t,r,i,n){var a=t;var s=r;var o=Ye(e,t.line);function l(){var i=t.line+r;if(i=e.first+e.size){return false}t=new nt(i,t.ch,t.sticky);return o=Ye(e,i)}function u(i){var a;if(n){a=$s(e.cm,o,t,r)}else{a=qs(o,t,r)}if(a==null){if(!i&&l()){t=Ks(n,e.cm,o,t.line,r)}else{return false}}else{t=a}return true}if(i=="char"){u()}else if(i=="column"){u(true)}else if(i=="word"||i=="group"){var c=null,f=i=="group";var h=e.cm&&e.cm.getHelper(t,"wordChars");for(var p=true;;p=false){if(r<0&&!u(!p)){break}var d=o.text.charAt(t.ch)||"\n";var m=re(d,h)?"w":f&&d=="\n"?"n":!f||/\s/.test(d)?null:"p";if(f&&!p&&!m){m="s"}if(c&&c!=m){if(r<0){r=1;u();t.sticky="after"}break}if(m){c=m}if(r>0&&!u(!p)){break}}}var v=qa(e,t,a,s,true);if(st(a,v)){v.hitSide=true}return v}function Yo(e,t,r,i){var n=e.doc,a=t.left,s;if(i=="page"){var o=Math.min(e.display.wrapper.clientHeight,window.innerHeight||document.documentElement.clientHeight);var l=Math.max(o-.5*Ai(e.display),3);s=(r>0?t.bottom:t.top)+r*l}else if(i=="line"){s=r>0?t.bottom+3:t.top-3}var u;for(;;){u=xi(e,a,s);if(!u.outside){break}if(r<0?s<=0:s>=n.height){u.hitSide=true;break}s+=r*5}return u}var Qo=function(e){this.cm=e;this.lastAnchorNode=this.lastAnchorOffset=this.lastFocusNode=this.lastFocusOffset=null;this.polling=new W;this.composing=null;this.gracePeriod=false;this.readDOMTimeout=null};Qo.prototype.init=function(e){var t=this;var r=this,i=r.cm;var n=r.div=e.lineDiv;qo(n,i.options.spellcheck,i.options.autocorrect,i.options.autocapitalize);de(n,"paste",function(e){if(ye(i,e)||jo(e,i)){return}if(o<=11){setTimeout(_n(i,function(){return t.updateFromDOM()}),20)}});de(n,"compositionstart",function(e){t.composing={data:e.data,done:false}});de(n,"compositionupdate",function(e){if(!t.composing){t.composing={data:e.data,done:false}}});de(n,"compositionend",function(e){if(t.composing){if(e.data!=t.composing.data){t.readFromDOMSoon()}t.composing.done=true}});de(n,"touchstart",function(){return r.forceCompositionEnd()});de(n,"input",function(){if(!t.composing){t.readFromDOMSoon()}});function a(e){if(ye(i,e)){return}if(i.somethingSelected()){Bo({lineWise:false,text:i.getSelections()});if(e.type=="cut"){i.replaceSelection("",null,"cut")}}else if(!i.options.lineWiseCopyCut){return}else{var t=Go(i);Bo({lineWise:true,text:t.text});if(e.type=="cut"){i.operation(function(){i.setSelections(t.ranges,0,j) +;i.replaceSelection("",null,"cut")})}}if(e.clipboardData){e.clipboardData.clearData();var a=zo.text.join("\n");e.clipboardData.setData("Text",a);if(e.clipboardData.getData("Text")==a){e.preventDefault();return}}var s=Ko(),o=s.firstChild;i.display.lineSpace.insertBefore(s,i.display.lineSpace.firstChild);o.value=zo.text.join("\n");var l=document.activeElement;D(o);setTimeout(function(){i.display.lineSpace.removeChild(s);l.focus();if(l==n){r.showPrimarySelection()}},50)}de(n,"copy",a);de(n,"cut",a)};Qo.prototype.prepareSelection=function(){var e=Hi(this.cm,false);e.focus=this.cm.state.focused;return e};Qo.prototype.showSelection=function(e,t){if(!e||!this.cm.display.view.length){return}if(e.focus||t){this.showPrimarySelection()}this.showMultipleSelections(e)};Qo.prototype.getSelection=function(){return this.cm.display.wrapper.ownerDocument.getSelection()};Qo.prototype.showPrimarySelection=function(){var e=this.getSelection(),t=this.cm,i=t.doc.sel.primary();var n=i.from(),a=i.to();if(t.display.viewTo==t.display.viewFrom||n.line>=t.display.viewTo||a.line=t.display.viewFrom&&Zo(t,n)||{node:l[0].measure.map[2],offset:0};var c=a.linee.firstLine()){i=nt(i.line-1,Ye(e.doc,i.line-1).length)}if(n.ch==Ye(e.doc,n.line).text.length&&n.linet.viewTo-1){return false}var a,s,o;if(i.line==t.viewFrom||(a=Oi(e,i.line))==0){s=et(t.view[0].line);o=t.view[0].node}else{s=et(t.view[a].line);o=t.view[a-1].node.nextSibling}var l=Oi(e,n.line);var u,c;if(l==t.view.length-1){u=t.viewTo-1;c=t.lineDiv.lastChild}else{u=et(t.view[l+1].line)-1;c=t.view[l+1].node.previousSibling}if(!o){return false}var f=e.doc.splitLines(tl(e,o,c,s,u));var h=Qe(e.doc,nt(s,0),nt(u,Ye(e.doc,u).text.length));while(f.length>1&&h.length>1){if(X(f)==X(h)){f.pop();h.pop();u--}else if(f[0]==h[0]){f.shift();h.shift();s++}else{break}}var p=0,d=0;var m=f[0],v=h[0],g=Math.min(m.length,v.length);while(pi.ch&&y.charCodeAt(y.length-d-1)==x.charCodeAt(x.length-d-1)){p--;d++}}f[f.length-1]=y.slice(0,y.length-d).replace(/^\u200b+/,"");f[0]=f[0].slice(p).replace(/\u200b+$/,"");var w=nt(s,p);var k=nt(u,h.length?X(h).length-d:0);if(f.length>1||f[0]||at(w,k)){rs(e.doc,f,w,k,"+input");return true}};Qo.prototype.ensurePolled=function(){this.forceCompositionEnd()};Qo.prototype.reset=function(){this.forceCompositionEnd()};Qo.prototype.forceCompositionEnd=function(){if(!this.composing){return}clearTimeout(this.readDOMTimeout);this.composing=null;this.updateFromDOM();this.div.blur();this.div.focus()};Qo.prototype.readFromDOMSoon=function(){var e=this;if(this.readDOMTimeout!=null){return}this.readDOMTimeout=setTimeout(function(){e.readDOMTimeout=null;if(e.composing){if(e.composing.done){e.composing=null}else{return}}e.updateFromDOM()},80)};Qo.prototype.updateFromDOM=function(){var e=this;if(this.cm.isReadOnly()||!this.pollContent()){Nn(this.cm,function(){return Di(e.cm)})}};Qo.prototype.setUneditable=function(e){e.contentEditable="false"};Qo.prototype.onKeyPress=function(e){if(e.charCode==0||this.composing){return}e.preventDefault();if(!this.cm.isReadOnly()){_n(this.cm,Ho)(this.cm,String.fromCharCode(e.charCode==null?e.keyCode:e.charCode),0)}};Qo.prototype.readOnlyChanged=function(e){this.div.contentEditable=String(e!="nocursor")};Qo.prototype.onContextMenu=function(){};Qo.prototype.resetPosition=function(){};Qo.prototype.needsContentAttribute=true;function Zo(e,t){var r=Jr(e,t.line);if(!r||r.hidden){return null}var i=Ye(e.doc,t.line);var n=Yr(r,i,t.line);var a=he(i,e.doc.direction),s="left";if(a){var o=ce(a,t.ch);s=o%2?"right":"left"}var l=ii(n.map,t.ch,s);l.offset=l.collapse=="right"?l.end:l.start;return l}function Jo(e){for(var t=e;t;t=t.parentNode){if(/CodeMirror-gutter-wrapper/.test(t.className)){return true}}return false}function el(e,t){if(t){e.bad=true}return e}function tl(e,t,r,i,n){var a="",s=false,o=e.doc.lineSeparator(),l=false;function u(e){return function(t){return t.id==e}}function c(){if(s){a+=o;if(l){a+=o}s=l=false}}function f(e){if(e){c();a+=e}}function h(t){if(t.nodeType==1){var r=t.getAttribute("cm-text");if(r){f(r);return}var a=t.getAttribute("cm-marker"),p;if(a){var d=e.findMarks(nt(i,0),nt(n+1,0),u(+a));if(d.length&&(p=d[0].find(0))){f(Qe(e.doc,p.from,p.to).join(o))}return}if(t.getAttribute("contenteditable")=="false"){return}var m=/^(pre|div|p|li|table|br)$/i.test(t.nodeName);if(!/^br$/i.test(t.nodeName)&&t.textContent.length==0){return}if(m){c()}for(var v=0;v=9&&t.hasSelection){t.hasSelection=null}r.poll()});de(n,"paste",function(e){if(ye(i,e)||jo(e,i)){return}i.state.pasteIncoming=+new Date;r.fastPoll()});function a(e){if(ye(i,e)){return}if(i.somethingSelected()){Bo({lineWise:false,text:i.getSelections()})}else if(!i.options.lineWiseCopyCut){return}else{var t=Go(i);Bo({lineWise:true,text:t.text});if(e.type=="cut"){i.setSelections(t.ranges,null,j)}else{r.prevInput="";n.value=t.text.join("\n");D(n)}}if(e.type=="cut"){i.state.cutIncoming=+new Date}}de(n,"cut",a);de(n,"copy",a);de(e.scroller,"paste",function(t){if(Hr(e,t)||ye(i,t)){return}if(!n.dispatchEvent){i.state.pasteIncoming=+new Date;r.focus();return}var a=new Event("paste");a.clipboardData=t.clipboardData;n.dispatchEvent(a)});de(e.lineSpace,"selectstart",function(t){if(!Hr(e,t)){ke(t)}});de(n,"compositionstart",function(){var e=i.getCursor("from");if(r.composing){r.composing.range.clear()}r.composing={start:e,range:i.markText(e,i.getCursor("to"),{className:"CodeMirror-composing"})}});de(n,"compositionend",function(){if(r.composing){r.poll();r.composing.range.clear();r.composing=null}})};nl.prototype.createField=function(e){this.wrapper=Ko();this.textarea=this.wrapper.firstChild};nl.prototype.prepareSelection=function(){var e=this.cm,t=e.display,r=e.doc;var i=Hi(e);if(e.options.moveInputWithCursor){var n=vi(e,r.sel.primary().head,"div");var a=t.wrapper.getBoundingClientRect(),s=t.lineDiv.getBoundingClientRect();i.teTop=Math.max(0,Math.min(t.wrapper.clientHeight-10,n.top+s.top-a.top));i.teLeft=Math.max(0,Math.min(t.wrapper.clientWidth-10,n.left+s.left-a.left))}return i};nl.prototype.showSelection=function(e){var t=this.cm,r=t.display;A(r.cursorDiv,e.cursors);A(r.selectionDiv,e.selection);if(e.teTop!=null){this.wrapper.style.top=e.teTop+"px";this.wrapper.style.left=e.teLeft+"px"}};nl.prototype.reset=function(e){if(this.contextMenuPending||this.composing){return}var t=this.cm;if(t.somethingSelected()){this.prevInput="";var r=t.getSelection();this.textarea.value=r;if(t.state.focused){D(this.textarea)}if(s&&o>=9){this.hasSelection=r}}else if(!e){this.prevInput=this.textarea.value="";if(s&&o>=9){this.hasSelection=null}}};nl.prototype.getField=function(){return this.textarea};nl.prototype.supportsTouch=function(){return false};nl.prototype.focus=function(){if(this.cm.options.readOnly!="nocursor"&&(!g||P()!=this.textarea)){try{this.textarea.focus()}catch(e){}}};nl.prototype.blur=function(){this.textarea.blur()};nl.prototype.resetPosition=function(){this.wrapper.style.top=this.wrapper.style.left=0};nl.prototype.receivedFocus=function(){this.slowPoll()};nl.prototype.slowPoll=function(){var e=this;if(this.pollingFast){return}this.polling.set(this.cm.options.pollInterval,function(){e.poll();if(e.cm.state.focused){e.slowPoll()}})};nl.prototype.fastPoll=function(){var e=false,t=this;t.pollingFast=true;function r(){var i=t.poll();if(!i&&!e){e=true;t.polling.set(60,r)}else{t.pollingFast=false;t.slowPoll()}}t.polling.set(20,r)};nl.prototype.poll=function(){var e=this;var t=this.cm,r=this.textarea,i=this.prevInput;if(this.contextMenuPending||!t.state.focused||Oe(r)&&!i&&!this.composing||t.isReadOnly()||t.options.disableInput||t.state.keySeq){return false}var n=r.value;if(n==i&&!t.somethingSelected()){return false}if(s&&o>=9&&this.hasSelection===n||y&&/[\uf700-\uf7ff]/.test(n)){t.display.input.reset();return false}if(t.doc.sel==t.display.selForContextMenu){var a=n.charCodeAt(0);if(a==8203&&!i){i="​"}if(a==8666){this.reset();return this.cm.execCommand("undo")}}var l=0,u=Math.min(i.length,n.length);while(l1e3||n.indexOf("\n")>-1){r.value=e.prevInput=""}else{e.prevInput=n}if(e.composing){e.composing.range.clear();e.composing.range=t.markText(e.composing.start,t.getCursor("to"),{className:"CodeMirror-composing"})}});return true};nl.prototype.ensurePolled=function(){if(this.pollingFast&&this.poll()){this.pollingFast=false}};nl.prototype.onKeyPress=function(){if(s&&o>=9){this.hasSelection=null}this.fastPoll()};nl.prototype.onContextMenu=function(e){var t=this,r=t.cm,i=r.display,n=t.textarea;if(t.contextMenuPending){t.contextMenuPending()}var a=Ii(r,e),u=i.scroller.scrollTop;if(!a||f){return}var c=r.options.resetSelectionOnContextMenu;if(c&&r.doc.sel.contains(a)==-1){_n(r,za)(r.doc,sa(a),j)}var h=n.style.cssText,p=t.wrapper.style.cssText;var d=t.wrapper.offsetParent.getBoundingClientRect();t.wrapper.style.cssText="position: static";n.style.cssText="position: absolute; width: 30px; height: 30px;\n top: "+(e.clientY-d.top-5)+"px; left: "+(e.clientX-d.left-5)+"px;\n z-index: 1000; background: "+(s?"rgba(255, 255, 255, .05)":"transparent")+";\n outline: none; border-width: 0; outline: none; overflow: hidden; opacity: .05; filter: alpha(opacity=5);";var m;if(l){m=window.scrollY}i.input.focus();if(l){window.scrollTo(null,m)}i.input.reset();if(!r.somethingSelected()){n.value=t.prevInput=" "}t.contextMenuPending=g;i.selForContextMenu=r.doc.sel;clearTimeout(i.detectingSelectAll);function v(){if(n.selectionStart!=null){var e=r.somethingSelected();var a="​"+(e?n.value:"");n.value="⇚";n.value=a;t.prevInput=e?"":"​";n.selectionStart=1;n.selectionEnd=a.length;i.selForContextMenu=r.doc.sel}}function g(){if(t.contextMenuPending!=g){return}t.contextMenuPending=false;t.wrapper.style.cssText=p;n.style.cssText=h;if(s&&o<9){i.scrollbars.setScrollTop(i.scroller.scrollTop=u)}if(n.selectionStart!=null){if(!s||s&&o<9){v()}var e=0,a=function(){if(i.selForContextMenu==r.doc.sel&&n.selectionStart==0&&n.selectionEnd>0&&t.prevInput=="​"){_n(r,$a)(r)}else if(e++<10){i.detectingSelectAll=setTimeout(a,500)}else{i.selForContextMenu=null;i.input.reset()}};i.detectingSelectAll=setTimeout(a,200)}}if(s&&o>=9){v()}if(C){Le(e);var y=function(){ve(window,"mouseup",y);setTimeout(g,20)};de(window,"mouseup",y)}else{setTimeout(g,50)}};nl.prototype.readOnlyChanged=function(e){if(!e){this.reset()}this.textarea.disabled=e=="nocursor"};nl.prototype.setUneditable=function(){};nl.prototype.needsContentAttribute=false;function al(e,t){t=t?V(t):{};t.value=e.value;if(!t.tabindex&&e.tabIndex){t.tabindex=e.tabIndex}if(!t.placeholder&&e.placeholder){t.placeholder=e.placeholder}if(t.autofocus==null){var r=P();t.autofocus=r==e||e.getAttribute("autofocus")!=null&&r==document.body}function i(){e.value=o.getValue()}var n;if(e.form){de(e.form,"submit",i);if(!t.leaveSubmitMethodAlone){var a=e.form;n=a.submit;try{var s=a.submit=function(){i();a.submit=n;a.submit();a.submit=s}}catch(e){}}}t.finishInit=function(r){r.save=i;r.getTextArea=function(){return e};r.toTextArea=function(){r.toTextArea=isNaN;i();e.parentNode.removeChild(r.getWrapperElement());e.style.display="";if(e.form){ve(e.form,"submit",i);if(!t.leaveSubmitMethodAlone&&typeof e.form.submit=="function"){e.form.submit=n}}}};e.style.display="none";var o=Ro(function(t){return e.parentNode.insertBefore(t,e.nextSibling)},t);return o}function sl(e){e.off=ve;e.on=de;e.wheelEventPixels=ta;e.Doc=ws;e.splitLines=Ie;e.countColumn=F;e.findColumn=q;e.isWordChar=te;e.Pass=H;e.signal=ge;e.Line=or;e.changeEnd=oa;e.scrollbarModel=xn;e.Pos=nt;e.cmpPos=at;e.modes=Fe;e.mimeModes=We;e.resolveMode=He;e.getMode=je;e.modeExtensions=Ue;e.extendMode=Ge;e.copyState=qe;e.startState=$e;e.innerMode=Ke;e.commands=Xs;e.keyMap=Rs;e.keyName=Hs;e.isModifierKey=zs;e.lookupKey=Ws;e.normalizeKeyMap=Fs;e.StringStream=Xe;e.SharedTextMarker=ms;e.TextMarker=ps;e.LineWidget=us;e.e_preventDefault=ke;e.e_stopPropagation=Ce;e.e_stop=Le;e.addClass=I;e.contains=_;e.rmClass=L;e.keyNames=Ps}Io(Ro);$o(Ro);var ol="iter insert remove copy getEditor constructor".split(" ");for(var ll in ws.prototype){if(ws.prototype.hasOwnProperty(ll)&&z(ol,ll)<0){Ro.prototype[ll]=function(e){return function(){return e.apply(this.doc,arguments)}}(ws.prototype[ll])}}we(ws);Ro.inputStyles={textarea:nl,contenteditable:Qo};Ro.defineMode=function(e){if(!Ro.defaults.mode&&e!="null"){Ro.defaults.mode=e}ze.apply(this,arguments)};Ro.defineMIME=Be;Ro.defineMode("null",function(){return{token:function(e){return e.skipToEnd()}}});Ro.defineMIME("text/plain","null");Ro.defineExtension=function(e,t){Ro.prototype[e]=t};Ro.defineDocExtension=function(e,t){ws.prototype[e]=t};Ro.fromTextArea=al;sl(Ro);Ro.version="5.49.2";return Ro});(function(e){if(typeof exports=="object"&&typeof module=="object")e(require("../../lib/codemirror"));else if(typeof define=="function"&&define.amd)define(["../../lib/codemirror"],e);else e(CodeMirror)})(function(e){"use strict";e.defineMode("javascript",function(t,r){var i=t.indentUnit;var n=r.statementIndent;var a=r.jsonld;var s=r.json||a;var o=r.typescript;var l=r.wordCharacters||/[\w$\xa1-\uffff]/;var u=function(){function e(e){return{type:e,style:"keyword"}}var t=e("keyword a"),r=e("keyword b"),i=e("keyword c"),n=e("keyword d");var a=e("operator"),s={type:"atom",style:"atom"};return{if:e("if"),while:t,with:t,else:r,do:r,try:r,finally:r,return:n,break:n,continue:n,new:e("new"),delete:i,void:i,throw:i,debugger:e("debugger"),var:e("var"),const:e("var"),let:e("var"),function:e("function"),catch:e("catch"),for:e("for"),switch:e("switch"),case:e("case"),default:e("default"),in:a,typeof:a,instanceof:a,true:s,false:s,null:s,undefined:s,NaN:s,Infinity:s,this:e("this"),class:e("class"),super:e("atom"),yield:i,export:e("export"),import:e("import"),extends:i,await:i}}();var c=/[+\-*&%=<>!?|~^@]/;var f=/^@(context|id|value|language|type|container|list|set|reverse|index|base|vocab|graph)"/;function h(e){var t=false,r,i=false;while((r=e.next())!=null){if(!t){if(r=="/"&&!i)return;if(r=="[")i=true;else if(i&&r=="]")i=false}t=!t&&r=="\\"}}var p,d;function m(e,t,r){p=e;d=r;return t}function v(e,t){var r=e.next();if(r=='"'||r=="'"){t.tokenize=g(r);return t.tokenize(e,t)}else if(r=="."&&e.match(/^\d[\d_]*(?:[eE][+\-]?[\d_]+)?/)){return m("number","number")}else if(r=="."&&e.match("..")){return m("spread","meta")}else if(/[\[\]{}\(\),;\:\.]/.test(r)){return m(r)}else if(r=="="&&e.eat(">")){return m("=>","operator")}else if(r=="0"&&e.match(/^(?:x[\dA-Fa-f_]+|o[0-7_]+|b[01_]+)n?/)){return m("number","number")}else if(/\d/.test(r)){e.match(/^[\d_]*(?:n|(?:\.[\d_]*)?(?:[eE][+\-]?[\d_]+)?)?/);return m("number","number")}else if(r=="/"){if(e.eat("*")){t.tokenize=y;return y(e,t)}else if(e.eat("/")){e.skipToEnd();return m("comment","comment")}else if(et(e,t,1)){h(e);e.match(/^\b(([gimyus])(?![gimyus]*\2))+\b/);return m("regexp","string-2")}else{e.eat("=");return m("operator","operator",e.current())}}else if(r=="`"){t.tokenize=x;return x(e,t)}else if(r=="#"){e.skipToEnd();return m("error","error")}else if(r=="<"&&e.match("!--")||r=="-"&&e.match("->")){e.skipToEnd();return m("comment","comment")}else if(c.test(r)){if(r!=">"||!t.lexical||t.lexical.type!=">"){if(e.eat("=")){if(r=="!"||r=="=")e.eat("=")}else if(/[<>*+\-]/.test(r)){e.eat(r);if(r==">")e.eat(r)}}return m("operator","operator",e.current())}else if(l.test(r)){e.eatWhile(l);var i=e.current();if(t.lastType!="."){if(u.propertyIsEnumerable(i)){var n=u[i];return m(n.type,n.style,i)}if(i=="async"&&e.match(/^(\s|\/\*.*?\*\/)*[\[\(\w]/,false))return m("async","keyword",i)}return m("variable","variable",i)}}function g(e){return function(t,r){var i=false,n;if(a&&t.peek()=="@"&&t.match(f)){r.tokenize=v;return m("jsonld-keyword","meta")}while((n=t.next())!=null){if(n==e&&!i)break;i=!i&&n=="\\"}if(!i)r.tokenize=v;return m("string","string")}}function y(e,t){var r=false,i;while(i=e.next()){if(i=="/"&&r){t.tokenize=v;break}r=i=="*"}return m("comment","comment")}function x(e,t){var r=false,i;while((i=e.next())!=null){if(!r&&(i=="`"||i=="$"&&e.eat("{"))){t.tokenize=v;break}r=!r&&i=="\\"}return m("quasi","string-2",e.current())}var b="([{}])";function w(e,t){if(t.fatArrowAt)t.fatArrowAt=null;var r=e.string.indexOf("=>",e.start);if(r<0)return;if(o){var i=/:\s*(?:\w+(?:<[^>]*>|\[\])?|\{[^}]*\})\s*$/.exec(e.string.slice(e.start,r));if(i)r=i.index}var n=0,a=false;for(var s=r-1;s>=0;--s){var u=e.string.charAt(s);var c=b.indexOf(u);if(c>=0&&c<3){if(!n){++s;break}if(--n==0){if(u=="(")a=true;break}}else if(c>=3&&c<6){++n}else if(l.test(u)){a=true}else if(/["'\/`]/.test(u)){for(;;--s){if(s==0)return;var f=e.string.charAt(s-1);if(f==u&&e.string.charAt(s-2)!="\\"){s--;break}}}else if(a&&!n){++s;break}}if(a&&!n)t.fatArrowAt=s}var k={atom:true,number:true,variable:true,string:true,regexp:true,this:true,"jsonld-keyword":true};function C(e,t,r,i,n,a){this.indented=e;this.column=t;this.type=r;this.prev=n;this.info=a;if(i!=null)this.align=i}function S(e,t){for(var r=e.localVars;r;r=r.next)if(r.name==t)return true;for(var i=e.context;i;i=i.prev){for(var r=i.vars;r;r=r.next)if(r.name==t)return true}}function L(e,t,r,i,n){var a=e.cc;T.state=e;T.stream=n;T.marked=null,T.cc=a;T.style=t;if(!e.lexical.hasOwnProperty("align"))e.lexical.align=true;while(true){var o=a.length?a.pop():s?U:H;if(o(r,i)){while(a.length&&a[a.length-1].lex)a.pop()();if(T.marked)return T.marked;if(r=="variable"&&S(e,i))return"variable-2";return t}}}var T={state:null,column:null,marked:null,cc:null};function A(){for(var e=arguments.length-1;e>=0;e--)T.cc.push(arguments[e])}function E(){A.apply(null,arguments);return true}function M(e,t){for(var r=t;r;r=r.next)if(r.name==e)return true;return false}function N(e){var t=T.state;T.marked="def";if(t.context){if(t.lexical.info=="var"&&t.context&&t.context.block){var i=_(e,t.context);if(i!=null){t.context=i;return}}else if(!M(e,t.localVars)){t.localVars=new O(e,t.localVars);return}}if(r.globalVars&&!M(e,t.globalVars))t.globalVars=new O(e,t.globalVars)}function _(e,t){if(!t){return null}else if(t.block){var r=_(e,t.prev);if(!r)return null;if(r==t.prev)return t;return new I(r,t.vars,true)}else if(M(e,t.vars)){return t}else{return new I(t.prev,new O(e,t.vars),false)}}function P(e){return e=="public"||e=="private"||e=="protected"||e=="abstract"||e=="readonly"}function I(e,t,r){this.prev=e;this.vars=t;this.block=r}function O(e,t){this.name=e;this.next=t}var D=new O("this",new O("arguments",null));function R(){T.state.context=new I(T.state.context,T.state.localVars,false);T.state.localVars=D}function V(){T.state.context=new I(T.state.context,T.state.localVars,true);T.state.localVars=null}function F(){T.state.localVars=T.state.context.vars;T.state.context=T.state.context.prev}F.lex=true;function W(e,t){var r=function(){var r=T.state,i=r.indented;if(r.lexical.type=="stat")i=r.lexical.indented;else for(var n=r.lexical;n&&n.type==")"&&n.align;n=n.prev)i=n.indented;r.lexical=new C(i,T.stream.column(),e,null,r.lexical,t)};r.lex=true;return r}function z(){var e=T.state;if(e.lexical.prev){if(e.lexical.type==")")e.indented=e.lexical.indented;e.lexical=e.lexical.prev}}z.lex=true;function B(e){function t(r){if(r==e)return E();else if(e==";"||r=="}"||r==")"||r=="]")return A();else return E(t)}return t}function H(e,t){if(e=="var")return E(W("vardef",t),Se,B(";"),z);if(e=="keyword a")return E(W("form"),q,H,z);if(e=="keyword b")return E(W("form"),H,z);if(e=="keyword d")return T.stream.match(/^\s*$/,false)?E():E(W("stat"),$,B(";"),z);if(e=="debugger")return E(B(";"));if(e=="{")return E(W("}"),V,fe,z,F);if(e==";")return E();if(e=="if"){if(T.state.lexical.info=="else"&&T.state.cc[T.state.cc.length-1]==z)T.state.cc.pop()();return E(W("form"),q,H,z,Ne)}if(e=="function")return E(Oe);if(e=="for")return E(W("form"),_e,H,z);if(e=="class"||o&&t=="interface"){T.marked="keyword";return E(W("form",e=="class"?e:t),We,z)}if(e=="variable"){if(o&&t=="declare"){T.marked="keyword";return E(H)}else if(o&&(t=="module"||t=="enum"||t=="type")&&T.stream.match(/^\s*\w/,false)){T.marked="keyword";if(t=="enum")return E(Qe);else if(t=="type")return E(Re,B("operator"),ve,B(";"));else return E(W("form"),Le,B("{"),W("}"),fe,z,z)}else if(o&&t=="namespace"){T.marked="keyword";return E(W("form"),U,H,z)}else if(o&&t=="abstract"){T.marked="keyword";return E(H)}else{return E(W("stat"),ne)}}if(e=="switch")return E(W("form"),q,B("{"),W("}","switch"),V,fe,z,z,F);if(e=="case")return E(U,B(":"));if(e=="default")return E(B(":"));if(e=="catch")return E(W("form"),R,j,H,z,F);if(e=="export")return E(W("stat"),je,z);if(e=="import")return E(W("stat"),Ge,z);if(e=="async")return E(H);if(t=="@")return E(U,H);return A(W("stat"),U,B(";"),z)}function j(e){if(e=="(")return E(Ve,B(")"))}function U(e,t){return K(e,t,false)}function G(e,t){return K(e,t,true)}function q(e){if(e!="(")return A();return E(W(")"),U,B(")"),z)}function K(e,t,r){if(T.state.fatArrowAt==T.stream.start){var i=r?ee:J;if(e=="(")return E(R,W(")"),ue(Ve,")"),z,B("=>"),i,F);else if(e=="variable")return A(R,Le,B("=>"),i,F)}var n=r?Y:X;if(k.hasOwnProperty(e))return E(n);if(e=="function")return E(Oe,n);if(e=="class"||o&&t=="interface"){T.marked="keyword";return E(W("form"),Fe,z)}if(e=="keyword c"||e=="async")return E(r?G:U);if(e=="(")return E(W(")"),$,B(")"),z,n);if(e=="operator"||e=="spread")return E(r?G:U);if(e=="[")return E(W("]"),Ye,z,n);if(e=="{")return ce(se,"}",null,n);if(e=="quasi")return A(Q,n);if(e=="new")return E(te(r));if(e=="import")return E(U);return E()}function $(e){if(e.match(/[;\}\)\],]/))return A();return A(U)}function X(e,t){if(e==",")return E(U);return Y(e,t,false)}function Y(e,t,r){var i=r==false?X:Y;var n=r==false?U:G;if(e=="=>")return E(R,r?ee:J,F);if(e=="operator"){if(/\+\+|--/.test(t)||o&&t=="!")return E(i);if(o&&t=="<"&&T.stream.match(/^([^>]|<.*?>)*>\s*\(/,false))return E(W(">"),ue(ve,">"),z,i);if(t=="?")return E(U,B(":"),n);return E(n)}if(e=="quasi"){return A(Q,i)}if(e==";")return;if(e=="(")return ce(G,")","call",i);if(e==".")return E(ae,i);if(e=="[")return E(W("]"),$,B("]"),z,i);if(o&&t=="as"){T.marked="keyword";return E(ve,i)}if(e=="regexp"){T.state.lastType=T.marked="operator";T.stream.backUp(T.stream.pos-T.stream.start-1);return E(n)}}function Q(e,t){if(e!="quasi")return A();if(t.slice(t.length-2)!="${")return E(Q);return E(U,Z)}function Z(e){if(e=="}"){T.marked="string-2";T.state.tokenize=x;return E(Q)}}function J(e){w(T.stream,T.state);return A(e=="{"?H:U)}function ee(e){w(T.stream,T.state);return A(e=="{"?H:G)}function te(e){return function(t){if(t==".")return E(e?ie:re);else if(t=="variable"&&o)return E(we,e?Y:X);else return A(e?G:U)}}function re(e,t){if(t=="target"){T.marked="keyword";return E(X)}}function ie(e,t){if(t=="target"){T.marked="keyword";return E(Y)}}function ne(e){if(e==":")return E(z,H);return A(X,B(";"),z)}function ae(e){if(e=="variable"){T.marked="property";return E()}}function se(e,t){if(e=="async"){T.marked="property";return E(se)}else if(e=="variable"||T.style=="keyword"){T.marked="property";if(t=="get"||t=="set")return E(oe);var r;if(o&&T.state.fatArrowAt==T.stream.start&&(r=T.stream.match(/^\s*:\s*/,false)))T.state.fatArrowAt=T.stream.pos+r[0].length;return E(le)}else if(e=="number"||e=="string"){T.marked=a?"property":T.style+" property";return E(le)}else if(e=="jsonld-keyword"){return E(le)}else if(o&&P(t)){T.marked="keyword";return E(se)}else if(e=="["){return E(U,he,B("]"),le)}else if(e=="spread"){return E(G,le)}else if(t=="*"){T.marked="keyword";return E(se)}else if(e==":"){return A(le)}}function oe(e){if(e!="variable")return A(le);T.marked="property";return E(Oe)}function le(e){if(e==":")return E(G);if(e=="(")return A(Oe)}function ue(e,t,r){function i(n,a){if(r?r.indexOf(n)>-1:n==","){var s=T.state.lexical;if(s.info=="call")s.pos=(s.pos||0)+1;return E(function(r,i){if(r==t||i==t)return A();return A(e)},i)}if(n==t||a==t)return E();if(r&&r.indexOf(";")>-1)return A(e);return E(B(t))}return function(r,n){if(r==t||n==t)return E();return A(e,i)}}function ce(e,t,r){for(var i=3;i"),ve)}function ge(e){if(e=="=>")return E(ve)}function ye(e,t){if(e=="variable"||T.style=="keyword"){T.marked="property";return E(ye)}else if(t=="?"||e=="number"||e=="string"){return E(ye)}else if(e==":"){return E(ve)}else if(e=="["){return E(B("variable"),pe,B("]"),ye)}else if(e=="("){return A(De,ye)}}function xe(e,t){if(e=="variable"&&T.stream.match(/^\s*[?:]/,false)||t=="?")return E(xe);if(e==":")return E(ve);if(e=="spread")return E(xe);return A(ve)}function be(e,t){if(t=="<")return E(W(">"),ue(ve,">"),z,be);if(t=="|"||e=="."||t=="&")return E(ve);if(e=="[")return E(ve,B("]"),be);if(t=="extends"||t=="implements"){T.marked="keyword";return E(ve)}if(t=="?")return E(ve,B(":"),ve)}function we(e,t){if(t=="<")return E(W(">"),ue(ve,">"),z,be)}function ke(){return A(ve,Ce)}function Ce(e,t){if(t=="=")return E(ve)}function Se(e,t){if(t=="enum"){T.marked="keyword";return E(Qe)}return A(Le,he,Ee,Me)}function Le(e,t){if(o&&P(t)){T.marked="keyword";return E(Le)}if(e=="variable"){N(t);return E()}if(e=="spread")return E(Le);if(e=="[")return ce(Ae,"]");if(e=="{")return ce(Te,"}")}function Te(e,t){if(e=="variable"&&!T.stream.match(/^\s*:/,false)){N(t);return E(Ee)}if(e=="variable")T.marked="property";if(e=="spread")return E(Le);if(e=="}")return A();if(e=="[")return E(U,B("]"),B(":"),Te);return E(B(":"),Le,Ee)}function Ae(){return A(Le,Ee)}function Ee(e,t){if(t=="=")return E(G)}function Me(e){if(e==",")return E(Se)}function Ne(e,t){if(e=="keyword b"&&t=="else")return E(W("form","else"),H,z)}function _e(e,t){if(t=="await")return E(_e);if(e=="(")return E(W(")"),Pe,z)}function Pe(e){if(e=="var")return E(Se,Ie);if(e=="variable")return E(Ie);return A(Ie)}function Ie(e,t){if(e==")")return E();if(e==";")return E(Ie);if(t=="in"||t=="of"){T.marked="keyword";return E(U,Ie)}return A(U,Ie)}function Oe(e,t){if(t=="*"){T.marked="keyword";return E(Oe) +}if(e=="variable"){N(t);return E(Oe)}if(e=="(")return E(R,W(")"),ue(Ve,")"),z,de,H,F);if(o&&t=="<")return E(W(">"),ue(ke,">"),z,Oe)}function De(e,t){if(t=="*"){T.marked="keyword";return E(De)}if(e=="variable"){N(t);return E(De)}if(e=="(")return E(R,W(")"),ue(Ve,")"),z,de,F);if(o&&t=="<")return E(W(">"),ue(ke,">"),z,De)}function Re(e,t){if(e=="keyword"||e=="variable"){T.marked="type";return E(Re)}else if(t=="<"){return E(W(">"),ue(ke,">"),z)}}function Ve(e,t){if(t=="@")E(U,Ve);if(e=="spread")return E(Ve);if(o&&P(t)){T.marked="keyword";return E(Ve)}if(o&&e=="this")return E(he,Ee);return A(Le,he,Ee)}function Fe(e,t){if(e=="variable")return We(e,t);return ze(e,t)}function We(e,t){if(e=="variable"){N(t);return E(ze)}}function ze(e,t){if(t=="<")return E(W(">"),ue(ke,">"),z,ze);if(t=="extends"||t=="implements"||o&&e==","){if(t=="implements")T.marked="keyword";return E(o?ve:U,ze)}if(e=="{")return E(W("}"),Be,z)}function Be(e,t){if(e=="async"||e=="variable"&&(t=="static"||t=="get"||t=="set"||o&&P(t))&&T.stream.match(/^\s+[\w$\xa1-\uffff]/,false)){T.marked="keyword";return E(Be)}if(e=="variable"||T.style=="keyword"){T.marked="property";return E(o?He:Oe,Be)}if(e=="number"||e=="string")return E(o?He:Oe,Be);if(e=="[")return E(U,he,B("]"),o?He:Oe,Be);if(t=="*"){T.marked="keyword";return E(Be)}if(o&&e=="(")return A(De,Be);if(e==";"||e==",")return E(Be);if(e=="}")return E();if(t=="@")return E(U,Be)}function He(e,t){if(t=="?")return E(He);if(e==":")return E(ve,Ee);if(t=="=")return E(G);var r=T.state.lexical.prev,i=r&&r.info=="interface";return A(i?De:Oe)}function je(e,t){if(t=="*"){T.marked="keyword";return E(Xe,B(";"))}if(t=="default"){T.marked="keyword";return E(U,B(";"))}if(e=="{")return E(ue(Ue,"}"),Xe,B(";"));return A(H)}function Ue(e,t){if(t=="as"){T.marked="keyword";return E(B("variable"))}if(e=="variable")return A(G,Ue)}function Ge(e){if(e=="string")return E();if(e=="(")return A(U);return A(qe,Ke,Xe)}function qe(e,t){if(e=="{")return ce(qe,"}");if(e=="variable")N(t);if(t=="*")T.marked="keyword";return E($e)}function Ke(e){if(e==",")return E(qe,Ke)}function $e(e,t){if(t=="as"){T.marked="keyword";return E(qe)}}function Xe(e,t){if(t=="from"){T.marked="keyword";return E(U)}}function Ye(e){if(e=="]")return E();return A(ue(G,"]"))}function Qe(){return A(W("form"),Le,B("{"),W("}"),ue(Ze,"}"),z,z)}function Ze(){return A(Le,Ee)}function Je(e,t){return e.lastType=="operator"||e.lastType==","||c.test(t.charAt(0))||/[,.]/.test(t.charAt(0))}function et(e,t,r){return t.tokenize==v&&/^(?:operator|sof|keyword [bcd]|case|new|export|default|spread|[\[{}\(,;:]|=>)$/.test(t.lastType)||t.lastType=="quasi"&&/\{\s*$/.test(e.string.slice(0,e.pos-(r||0)))}return{startState:function(e){var t={tokenize:v,lastType:"sof",cc:[],lexical:new C((e||0)-i,0,"block",false),localVars:r.localVars,context:r.localVars&&new I(null,null,false),indented:e||0};if(r.globalVars&&typeof r.globalVars=="object")t.globalVars=r.globalVars;return t},token:function(e,t){if(e.sol()){if(!t.lexical.hasOwnProperty("align"))t.lexical.align=false;t.indented=e.indentation();w(e,t)}if(t.tokenize!=y&&e.eatSpace())return null;var r=t.tokenize(e,t);if(p=="comment")return r;t.lastType=p=="operator"&&(d=="++"||d=="--")?"incdec":p;return L(t,r,p,d,e)},indent:function(t,a){if(t.tokenize==y)return e.Pass;if(t.tokenize!=v)return 0;var s=a&&a.charAt(0),o=t.lexical,l;if(!/^\s*else\b/.test(a))for(var u=t.cc.length-1;u>=0;--u){var c=t.cc[u];if(c==z)o=o.prev;else if(c!=Ne)break}while((o.type=="stat"||o.type=="form")&&(s=="}"||(l=t.cc[t.cc.length-1])&&(l==X||l==Y)&&!/^[,\.=+\-*:?[\(]/.test(a)))o=o.prev;if(n&&o.type==")"&&o.prev.type=="stat")o=o.prev;var f=o.type,h=s==f;if(f=="vardef")return o.indented+(t.lastType=="operator"||t.lastType==","?o.info.length+1:0);else if(f=="form"&&s=="{")return o.indented;else if(f=="form")return o.indented+i;else if(f=="stat")return o.indented+(Je(t,a)?n||i:0);else if(o.info=="switch"&&!h&&r.doubleIndentSwitch!=false)return o.indented+(/^(?:case|default)\b/.test(a)?i:2*i);else if(o.align)return o.column+(h?0:1);else return o.indented+(h?0:i)},electricInput:/^\s*(?:case .*?:|default:|\{|\})$/,blockCommentStart:s?null:"/*",blockCommentEnd:s?null:"*/",blockCommentContinue:s?null:" * ",lineComment:s?null:"//",fold:"brace",closeBrackets:"()[]{}''\"\"``",helperType:s?"json":"javascript",jsonldMode:a,jsonMode:s,expressionAllowed:et,skipExpression:function(e){var t=e.cc[e.cc.length-1];if(t==U||t==G)e.cc.pop()}}});e.registerHelper("wordChars","javascript",/[\w$]/);e.defineMIME("text/javascript","javascript");e.defineMIME("text/ecmascript","javascript");e.defineMIME("application/javascript","javascript");e.defineMIME("application/x-javascript","javascript");e.defineMIME("application/ecmascript","javascript");e.defineMIME("application/json",{name:"javascript",json:true});e.defineMIME("application/x-json",{name:"javascript",json:true});e.defineMIME("application/ld+json",{name:"javascript",jsonld:true});e.defineMIME("text/typescript",{name:"javascript",typescript:true});e.defineMIME("application/typescript",{name:"javascript",typescript:true})});(function(e){if(typeof exports=="object"&&typeof module=="object")e(require("../../lib/codemirror"));else if(typeof define=="function"&&define.amd)define(["../../lib/codemirror"],e);else e(CodeMirror)})(function(e){"use strict";e.defineMode("css",function(t,r){var i=r.inline;if(!r.propertyKeywords)r=e.resolveMode("text/css");var n=t.indentUnit,a=r.tokenHooks,s=r.documentTypes||{},o=r.mediaTypes||{},l=r.mediaFeatures||{},u=r.mediaValueKeywords||{},c=r.propertyKeywords||{},f=r.nonStandardPropertyKeywords||{},h=r.fontProperties||{},p=r.counterDescriptors||{},d=r.colorKeywords||{},m=r.valueKeywords||{},v=r.allowNested,g=r.lineComment,y=r.supportsAtComponent===true;var x,b;function w(e,t){x=t;return e}function k(e,t){var r=e.next();if(a[r]){var i=a[r](e,t);if(i!==false)return i}if(r=="@"){e.eatWhile(/[\w\\\-]/);return w("def",e.current())}else if(r=="="||(r=="~"||r=="|")&&e.eat("=")){return w(null,"compare")}else if(r=='"'||r=="'"){t.tokenize=C(r);return t.tokenize(e,t)}else if(r=="#"){e.eatWhile(/[\w\\\-]/);return w("atom","hash")}else if(r=="!"){e.match(/^\s*\w*/);return w("keyword","important")}else if(/\d/.test(r)||r=="."&&e.eat(/\d/)){e.eatWhile(/[\w.%]/);return w("number","unit")}else if(r==="-"){if(/[\d.]/.test(e.peek())){e.eatWhile(/[\w.%]/);return w("number","unit")}else if(e.match(/^-[\w\\\-]*/)){e.eatWhile(/[\w\\\-]/);if(e.match(/^\s*:/,false))return w("variable-2","variable-definition");return w("variable-2","variable")}else if(e.match(/^\w+-/)){return w("meta","meta")}}else if(/[,+>*\/]/.test(r)){return w(null,"select-op")}else if(r=="."&&e.match(/^-?[_a-z][_a-z0-9-]*/i)){return w("qualifier","qualifier")}else if(/[:;{}\[\]\(\)]/.test(r)){return w(null,r)}else if(e.match(/[\w-.]+(?=\()/)){if(/^(url(-prefix)?|domain|regexp)$/.test(e.current().toLowerCase())){t.tokenize=S}return w("variable callee","variable")}else if(/[\w\\\-]/.test(r)){e.eatWhile(/[\w\\\-]/);return w("property","word")}else{return w(null,null)}}function C(e){return function(t,r){var i=false,n;while((n=t.next())!=null){if(n==e&&!i){if(e==")")t.backUp(1);break}i=!i&&n=="\\"}if(n==e||!i&&e!=")")r.tokenize=null;return w("string","string")}}function S(e,t){e.next();if(!e.match(/\s*[\"\')]/,false))t.tokenize=C(")");else t.tokenize=null;return w(null,"(")}function L(e,t,r){this.type=e;this.indent=t;this.prev=r}function T(e,t,r,i){e.context=new L(r,t.indentation()+(i===false?0:n),e.context);return r}function A(e){if(e.context.prev)e.context=e.context.prev;return e.context.type}function E(e,t,r){return _[r.context.type](e,t,r)}function M(e,t,r,i){for(var n=i||1;n>0;n--)r.context=r.context.prev;return E(e,t,r)}function N(e){var t=e.current().toLowerCase();if(m.hasOwnProperty(t))b="atom";else if(d.hasOwnProperty(t))b="keyword";else b="variable"}var _={};_.top=function(e,t,r){if(e=="{"){return T(r,t,"block")}else if(e=="}"&&r.context.prev){return A(r)}else if(y&&/@component/i.test(e)){return T(r,t,"atComponentBlock")}else if(/^@(-moz-)?document$/i.test(e)){return T(r,t,"documentTypes")}else if(/^@(media|supports|(-moz-)?document|import)$/i.test(e)){return T(r,t,"atBlock")}else if(/^@(font-face|counter-style)/i.test(e)){r.stateArg=e;return"restricted_atBlock_before"}else if(/^@(-(moz|ms|o|webkit)-)?keyframes$/i.test(e)){return"keyframes"}else if(e&&e.charAt(0)=="@"){return T(r,t,"at")}else if(e=="hash"){b="builtin"}else if(e=="word"){b="tag"}else if(e=="variable-definition"){return"maybeprop"}else if(e=="interpolation"){return T(r,t,"interpolation")}else if(e==":"){return"pseudo"}else if(v&&e=="("){return T(r,t,"parens")}return r.context.type};_.block=function(e,t,r){if(e=="word"){var i=t.current().toLowerCase();if(c.hasOwnProperty(i)){b="property";return"maybeprop"}else if(f.hasOwnProperty(i)){b="string-2";return"maybeprop"}else if(v){b=t.match(/^\s*:(?:\s|$)/,false)?"property":"tag";return"block"}else{b+=" error";return"maybeprop"}}else if(e=="meta"){return"block"}else if(!v&&(e=="hash"||e=="qualifier")){b="error";return"block"}else{return _.top(e,t,r)}};_.maybeprop=function(e,t,r){if(e==":")return T(r,t,"prop");return E(e,t,r)};_.prop=function(e,t,r){if(e==";")return A(r);if(e=="{"&&v)return T(r,t,"propBlock");if(e=="}"||e=="{")return M(e,t,r);if(e=="(")return T(r,t,"parens");if(e=="hash"&&!/^#([0-9a-fA-f]{3,4}|[0-9a-fA-f]{6}|[0-9a-fA-f]{8})$/.test(t.current())){b+=" error"}else if(e=="word"){N(t)}else if(e=="interpolation"){return T(r,t,"interpolation")}return"prop"};_.propBlock=function(e,t,r){if(e=="}")return A(r);if(e=="word"){b="property";return"maybeprop"}return r.context.type};_.parens=function(e,t,r){if(e=="{"||e=="}")return M(e,t,r);if(e==")")return A(r);if(e=="(")return T(r,t,"parens");if(e=="interpolation")return T(r,t,"interpolation");if(e=="word")N(t);return"parens"};_.pseudo=function(e,t,r){if(e=="meta")return"pseudo";if(e=="word"){b="variable-3";return r.context.type}return E(e,t,r)};_.documentTypes=function(e,t,r){if(e=="word"&&s.hasOwnProperty(t.current())){b="tag";return r.context.type}else{return _.atBlock(e,t,r)}};_.atBlock=function(e,t,r){if(e=="(")return T(r,t,"atBlock_parens");if(e=="}"||e==";")return M(e,t,r);if(e=="{")return A(r)&&T(r,t,v?"block":"top");if(e=="interpolation")return T(r,t,"interpolation");if(e=="word"){var i=t.current().toLowerCase();if(i=="only"||i=="not"||i=="and"||i=="or")b="keyword";else if(o.hasOwnProperty(i))b="attribute";else if(l.hasOwnProperty(i))b="property";else if(u.hasOwnProperty(i))b="keyword";else if(c.hasOwnProperty(i))b="property";else if(f.hasOwnProperty(i))b="string-2";else if(m.hasOwnProperty(i))b="atom";else if(d.hasOwnProperty(i))b="keyword";else b="error"}return r.context.type};_.atComponentBlock=function(e,t,r){if(e=="}")return M(e,t,r);if(e=="{")return A(r)&&T(r,t,v?"block":"top",false);if(e=="word")b="error";return r.context.type};_.atBlock_parens=function(e,t,r){if(e==")")return A(r);if(e=="{"||e=="}")return M(e,t,r,2);return _.atBlock(e,t,r)};_.restricted_atBlock_before=function(e,t,r){if(e=="{")return T(r,t,"restricted_atBlock");if(e=="word"&&r.stateArg=="@counter-style"){b="variable";return"restricted_atBlock_before"}return E(e,t,r)};_.restricted_atBlock=function(e,t,r){if(e=="}"){r.stateArg=null;return A(r)}if(e=="word"){if(r.stateArg=="@font-face"&&!h.hasOwnProperty(t.current().toLowerCase())||r.stateArg=="@counter-style"&&!p.hasOwnProperty(t.current().toLowerCase()))b="error";else b="property";return"maybeprop"}return"restricted_atBlock"};_.keyframes=function(e,t,r){if(e=="word"){b="variable";return"keyframes"}if(e=="{")return T(r,t,"top");return E(e,t,r)};_.at=function(e,t,r){if(e==";")return A(r);if(e=="{"||e=="}")return M(e,t,r);if(e=="word")b="tag";else if(e=="hash")b="builtin";return"at"};_.interpolation=function(e,t,r){if(e=="}")return A(r);if(e=="{"||e==";")return M(e,t,r);if(e=="word")b="variable";else if(e!="variable"&&e!="("&&e!=")")b="error";return"interpolation"};return{startState:function(e){return{tokenize:null,state:i?"block":"top",stateArg:null,context:new L(i?"block":"top",e||0,null)}},token:function(e,t){if(!t.tokenize&&e.eatSpace())return null;var r=(t.tokenize||k)(e,t);if(r&&typeof r=="object"){x=r[1];r=r[0]}b=r;if(x!="comment")t.state=_[t.state](x,e,t);return b},indent:function(e,t){var r=e.context,i=t&&t.charAt(0);var a=r.indent;if(r.type=="prop"&&(i=="}"||i==")"))r=r.prev;if(r.prev){if(i=="}"&&(r.type=="block"||r.type=="top"||r.type=="interpolation"||r.type=="restricted_atBlock")){r=r.prev;a=r.indent}else if(i==")"&&(r.type=="parens"||r.type=="atBlock_parens")||i=="{"&&(r.type=="at"||r.type=="atBlock")){a=Math.max(0,r.indent-n)}}return a},electricChars:"}",blockCommentStart:"/*",blockCommentEnd:"*/",blockCommentContinue:" * ",lineComment:g,fold:"brace"}});function t(e){var t={};for(var r=0;r"));else return null}else if(e.match("--")){return r(d("comment","--\x3e"))}else if(e.match("DOCTYPE",true,true)){e.eatWhile(/[\w\._\-]/);return r(m(1))}else{return null}}else if(e.eat("?")){e.eatWhile(/[\w\._\-]/);t.tokenize=d("meta","?>");return"meta"}else{u=e.eat("/")?"closeTag":"openTag";t.tokenize=h;return"tag bracket"}}else if(i=="&"){var n;if(e.eat("#")){if(e.eat("x")){n=e.eatWhile(/[a-fA-F\d]/)&&e.eat(";")}else{n=e.eatWhile(/[\d]/)&&e.eat(";")}}else{n=e.eatWhile(/[\w\.\-:]/)&&e.eat(";")}return n?"atom":"error"}else{e.eatWhile(/[^&<]/);return null}}f.isInText=true;function h(e,t){var r=e.next();if(r==">"||r=="/"&&e.eat(">")){t.tokenize=f;u=r==">"?"endTag":"selfcloseTag";return"tag bracket"}else if(r=="="){u="equals";return null}else if(r=="<"){t.tokenize=f;t.state=x;t.tagName=t.tagStart=null;var i=t.tokenize(e,t);return i?i+" tag error":"tag error"}else if(/[\'\"]/.test(r)){t.tokenize=p(r);t.stringStartCol=e.column();return t.tokenize(e,t)}else{e.match(/^[^\s\u00a0=<>\"\']*[^\s\u00a0=<>\"\'\/]/);return"word"}}function p(e){var t=function(t,r){while(!t.eol()){if(t.next()==e){r.tokenize=h;break}}return"string"};t.isInAttribute=true;return t}function d(e,t){return function(r,i){while(!r.eol()){if(r.match(t)){i.tokenize=f;break}r.next()}return e}}function m(e){return function(t,r){var i;while((i=t.next())!=null){if(i=="<"){r.tokenize=m(e+1);return r.tokenize(t,r)}else if(i==">"){if(e==1){r.tokenize=f;break}else{r.tokenize=m(e-1);return r.tokenize(t,r)}}}return"meta"}}function v(e,t,r){this.prev=e.context;this.tagName=t;this.indent=e.indented;this.startOfLine=r;if(s.doNotIndent.hasOwnProperty(t)||e.context&&e.context.noIndent)this.noIndent=true}function g(e){if(e.context)e.context=e.context.prev}function y(e,t){var r;while(true){if(!e.context){return}r=e.context.tagName;if(!s.contextGrabbers.hasOwnProperty(r)||!s.contextGrabbers[r].hasOwnProperty(t)){return}g(e)}}function x(e,t,r){if(e=="openTag"){r.tagStart=t.column();return b}else if(e=="closeTag"){return w}else{return x}}function b(e,t,r){if(e=="word"){r.tagName=t.current();c="tag";return S}else if(s.allowMissingTagName&&e=="endTag"){c="tag bracket";return S(e,t,r)}else{c="error";return b}}function w(e,t,r){if(e=="word"){var i=t.current();if(r.context&&r.context.tagName!=i&&s.implicitlyClosed.hasOwnProperty(r.context.tagName))g(r);if(r.context&&r.context.tagName==i||s.matchClosing===false){c="tag";return k}else{c="tag error";return C}}else if(s.allowMissingTagName&&e=="endTag"){c="tag bracket";return k(e,t,r)}else{c="error";return C}}function k(e,t,r){if(e!="endTag"){c="error";return k}g(r);return x}function C(e,t,r){c="error";return k(e,t,r)}function S(e,t,r){if(e=="word"){c="attribute";return L}else if(e=="endTag"||e=="selfcloseTag"){var i=r.tagName,n=r.tagStart;r.tagName=r.tagStart=null;if(e=="selfcloseTag"||s.autoSelfClosers.hasOwnProperty(i)){y(r,i)}else{y(r,i);r.context=new v(r,i,n==r.indented)}return x}c="error";return S}function L(e,t,r){if(e=="equals")return T;if(!s.allowMissing)c="error";return S(e,t,r)}function T(e,t,r){if(e=="string")return A;if(e=="word"&&s.allowUnquoted){c="string";return S}c="error";return S(e,t,r)}function A(e,t,r){if(e=="string")return A;return S(e,t,r)}return{startState:function(e){var t={tokenize:f,state:x,indented:e||0,tagName:null,tagStart:null,context:null};if(e!=null)t.baseIndent=e;return t},token:function(e,t){if(!t.tagName&&e.sol())t.indented=e.indentation();if(e.eatSpace())return null;u=null;var r=t.tokenize(e,t);if((r||u)&&r!="comment"){c=null;t.state=t.state(u||r,e,t);if(c)r=c=="error"?r+" error":c}return r},indent:function(t,r,i){var n=t.context;if(t.tokenize.isInAttribute){if(t.tagStart==t.indented)return t.stringStartCol+1;else return t.indented+a}if(n&&n.noIndent)return e.Pass;if(t.tokenize!=h&&t.tokenize!=f)return i?i.match(/^(\s*)/)[0].length:0;if(t.tagName){if(s.multilineTagIndentPastTag!==false)return t.tagStart+t.tagName.length+2;else return t.tagStart+a*(s.multilineTagIndentFactor||1)}if(s.alignCDATA&&/$/,blockCommentStart:"\x3c!--",blockCommentEnd:"--\x3e",configuration:s.htmlMode?"html":"xml",helperType:s.htmlMode?"html":"xml",skipAttribute:function(e){if(e.state==T)e.state=S},xmlCurrentTag:function(e){return e.tagName?{name:e.tagName,close:e.type=="closeTag"}:null},xmlCurrentContext:function(e){var t=[];for(var r=e.context;r;r=r.prev)if(r.tagName)t.push(r.tagName);return t.reverse()}}});e.defineMIME("text/xml","xml");e.defineMIME("application/xml","xml");if(!e.mimeModes.hasOwnProperty("text/html"))e.defineMIME("text/html",{name:"xml",htmlMode:true})});(function(e){if(typeof exports=="object"&&typeof module=="object")e(require("../../lib/codemirror"),require("../xml/xml"),require("../javascript/javascript"),require("../css/css"));else if(typeof define=="function"&&define.amd)define(["../../lib/codemirror","../xml/xml","../javascript/javascript","../css/css"],e);else e(CodeMirror)})(function(e){"use strict";var t={script:[["lang",/(javascript|babel)/i,"javascript"],["type",/^(?:text|application)\/(?:x-)?(?:java|ecma)script$|^module$|^$/i,"javascript"],["type",/./,"text/plain"],[null,null,"javascript"]],style:[["lang",/^css$/i,"css"],["type",/^(text\/)?(x-)?(stylesheet|css)$/i,"css"],["type",/./,"text/plain"],[null,null,"css"]]};function r(e,t,r){var i=e.current(),n=i.search(t);if(n>-1){e.backUp(i.length-n)}else if(i.match(/<\/?$/)){e.backUp(i.length);if(!e.match(t,false))e.match(i)}return r}var i={};function n(e){var t=i[e];if(t)return t;return i[e]=new RegExp("\\s+"+e+"\\s*=\\s*('|\")?([^'\"]+)('|\")?\\s*")}function a(e,t){var r=e.match(n(t));return r?/^\s*(.*?)\s*$/.exec(r[2])[1]:""}function s(e,t){return new RegExp((t?"^":"")+"","i")}function o(e,t){for(var r in e){var i=t[r]||(t[r]=[]);var n=e[r];for(var a=n.length-1;a>=0;a--)i.unshift(n[a])}}function l(e,t){for(var r=0;r=0;h--)u.script.unshift(["type",f[h].matches,f[h].mode]);function p(t,n){var o=a.token(t,n.htmlState),c=/\btag\b/.test(o),f;if(c&&!/[<>\s\/]/.test(t.current())&&(f=n.htmlState.tagName&&n.htmlState.tagName.toLowerCase())&&u.hasOwnProperty(f)){n.inTag=f+" "}else if(n.inTag&&c&&/>$/.test(t.current())){var h=/^([\S]+) (.*)/.exec(n.inTag);n.inTag=null;var d=t.current()==">"&&l(u[h[1]],h[2]);var m=e.getMode(i,d);var v=s(h[1],true),g=s(h[1],false);n.token=function(e,t){if(e.match(v,false)){t.token=p;t.localState=t.localMode=null;return null}return r(e,g,t.localMode.token(e,t.localState))};n.localMode=m;n.localState=e.startState(m,a.indent(n.htmlState,"",""))}else if(n.inTag){n.inTag+=t.current();if(t.eol())n.inTag+=" "}return o}return{startState:function(){var t=e.startState(a);return{token:p,inTag:null,localMode:null,localState:null,htmlState:t}},copyState:function(t){var r;if(t.localState){r=e.copyState(t.localMode,t.localState)}return{token:t.token,inTag:t.inTag,localMode:t.localMode,localState:r,htmlState:e.copyState(a,t.htmlState)}},token:function(e,t){return t.token(e,t)},indent:function(t,r,i){if(!t.localMode||/^\s*<\//.test(r))return a.indent(t.htmlState,r,i);else if(t.localMode.indent)return t.localMode.indent(t.localState,r,i);else return e.Pass},innerMode:function(e){return{state:e.localState||e.htmlState,mode:e.localMode||a}}}},"xml","javascript","css");e.defineMIME("text/html","htmlmixed")});(function(e){if(typeof exports=="object"&&typeof module=="object")e(require("../../lib/codemirror"));else if(typeof define=="function"&&define.amd)define(["../../lib/codemirror"],e);else e(CodeMirror)})(function(e){var t=/MSIE \d/.test(navigator.userAgent)&&(document.documentMode==null||document.documentMode<8);var r=e.Pos;var i={"(":")>",")":"(<","[":"]>","]":"[<","{":"}>","}":"{<","<":">>",">":"<<"};function n(e){return e&&e.bracketRegex||/[(){}[\]]/}function a(e,t,a){var o=e.getLineHandle(t.line),l=t.ch-1;var u=a&&a.afterCursor;if(u==null)u=/(^| )cm-fat-cursor($| )/.test(e.getWrapperElement().className);var c=n(a);var f=!u&&l>=0&&c.test(o.text.charAt(l))&&i[o.text.charAt(l)]||c.test(o.text.charAt(l+1))&&i[o.text.charAt(++l)];if(!f)return null;var h=f.charAt(1)==">"?1:-1;if(a&&a.strict&&h>0!=(l==t.ch))return null;var p=e.getTokenTypeAt(r(t.line,l+1));var d=s(e,r(t.line,l+(h>0?1:0)),h,p||null,a);if(d==null)return null;return{from:r(t.line,l),to:d&&d.pos,match:d&&d.ch==f.charAt(0),forward:h>0}}function s(e,t,a,s,o){var l=o&&o.maxScanLineLength||1e4;var u=o&&o.maxScanLines||1e3;var c=[];var f=n(o);var h=a>0?Math.min(t.line+u,e.lastLine()+1):Math.max(e.firstLine()-1,t.line-u);for(var p=t.line;p!=h;p+=a){var d=e.getLine(p);if(!d)continue;var m=a>0?0:d.length-1,v=a>0?d.length:-1;if(d.length>l)continue;if(p==t.line)m=t.ch-(a<0?1:0);for(;m!=v;m+=a){var g=d.charAt(m);if(f.test(g)&&(s===undefined||e.getTokenTypeAt(r(p,m+1))==s)){var y=i[g];if(y&&y.charAt(1)==">"==a>0)c.push(g);else if(!c.length)return{pos:r(p,m),ch:g};else c.pop()}}}return p-a==(a>0?e.lastLine():e.firstLine())?false:null}function o(e,i,n){var s=e.state.matchBrackets.maxHighlightLineLength||1e3;var o=[],l=e.listSelections();for(var u=0;ue){return false}r+=t[i+1];if(r>=e){return true}}}function h(e,t){if(e<65){return e===36}if(e<91){return true}if(e<97){return e===95}if(e<123){return true}if(e<=65535){return e>=170&&o.test(String.fromCharCode(e))}if(t===false){return false}return f(e,u)}function p(e,t){if(e<48){return e===36}if(e<58){return true}if(e<65){return false}if(e<91){return true}if(e<97){return e===95}if(e<123){return true}if(e<=65535){return e>=170&&l.test(String.fromCharCode(e))}if(t===false){return false}return f(e,u)||f(e,c)}var d=function e(t,r){if(r===void 0)r={};this.label=t;this.keyword=r.keyword;this.beforeExpr=!!r.beforeExpr;this.startsExpr=!!r.startsExpr;this.isLoop=!!r.isLoop;this.isAssign=!!r.isAssign;this.prefix=!!r.prefix;this.postfix=!!r.postfix;this.binop=r.binop||null;this.updateContext=null};function m(e,t){return new d(e,{beforeExpr:true,binop:t})}var v={beforeExpr:true};var g={startsExpr:true};var y={};function x(e,t){if(t===void 0)t={};t.keyword=e;return y[e]=new d(e,t)}var b={num:new d("num",g),regexp:new d("regexp",g),string:new d("string",g),name:new d("name",g),eof:new d("eof"),bracketL:new d("[",{beforeExpr:true,startsExpr:true}),bracketR:new d("]"),braceL:new d("{",{beforeExpr:true,startsExpr:true}),braceR:new d("}"),parenL:new d("(",{beforeExpr:true,startsExpr:true}),parenR:new d(")"),comma:new d(",",v),semi:new d(";",v),colon:new d(":",v),dot:new d("."),question:new d("?",v),arrow:new d("=>",v),template:new d("template"),invalidTemplate:new d("invalidTemplate"),ellipsis:new d("...",v),backQuote:new d("`",g),dollarBraceL:new d("${",{beforeExpr:true,startsExpr:true}),eq:new d("=",{beforeExpr:true,isAssign:true}),assign:new d("_=",{beforeExpr:true,isAssign:true}),incDec:new d("++/--",{prefix:true,postfix:true,startsExpr:true}),prefix:new d("!/~",{beforeExpr:true,prefix:true,startsExpr:true}),logicalOR:m("||",1),logicalAND:m("&&",2),bitwiseOR:m("|",3),bitwiseXOR:m("^",4),bitwiseAND:m("&",5),equality:m("==/!=/===/!==",6),relational:m("/<=/>=",7),bitShift:m("<>/>>>",8),plusMin:new d("+/-",{beforeExpr:true,binop:9,prefix:true,startsExpr:true}),modulo:m("%",10),star:m("*",10),slash:m("/",10),starstar:new d("**",{beforeExpr:true}),_break:x("break"),_case:x("case",v),_catch:x("catch"),_continue:x("continue"),_debugger:x("debugger"),_default:x("default",v),_do:x("do",{isLoop:true,beforeExpr:true}),_else:x("else",v),_finally:x("finally"),_for:x("for",{isLoop:true}),_function:x("function",g),_if:x("if"),_return:x("return",v),_switch:x("switch"),_throw:x("throw",v),_try:x("try"),_var:x("var"),_const:x("const"),_while:x("while",{isLoop:true}),_with:x("with"),_new:x("new",{beforeExpr:true,startsExpr:true}),_this:x("this",g),_super:x("super",g),_class:x("class",g),_extends:x("extends",v),_export:x("export"),_import:x("import"),_null:x("null",g),_true:x("true",g),_false:x("false",g),_in:x("in",{beforeExpr:true,binop:7}),_instanceof:x("instanceof",{beforeExpr:true,binop:7}),_typeof:x("typeof",{beforeExpr:true,prefix:true,startsExpr:true}),_void:x("void",{beforeExpr:true,prefix:true,startsExpr:true}),_delete:x("delete",{beforeExpr:true,prefix:true,startsExpr:true})};var w=/\r\n?|\n|\u2028|\u2029/;var k=new RegExp(w.source,"g");function C(e,t){return e===10||e===13||!t&&(e===8232||e===8233)}var S=/[\u1680\u180e\u2000-\u200a\u202f\u205f\u3000\ufeff]/;var L=/(?:\s|\/\/.*|\/\*[^]*?\*\/)*/g;var T=Object.prototype;var A=T.hasOwnProperty;var E=T.toString;function M(e,t){return A.call(e,t)}var N=Array.isArray||function(e){return E.call(e)==="[object Array]"};var _=function e(t,r){this.line=t;this.column=r};_.prototype.offset=function e(t){return new _(this.line,this.column+t)};var P=function e(t,r,i){this.start=r;this.end=i;if(t.sourceFile!==null){this.source=t.sourceFile}};function I(e,t){for(var r=1,i=0;;){k.lastIndex=i;var n=k.exec(e);if(n&&n.index=2015){t.ecmaVersion-=2009}if(t.allowReserved==null){t.allowReserved=t.ecmaVersion<5}if(N(t.onToken)){var i=t.onToken;t.onToken=function(e){return i.push(e)}}if(N(t.onComment)){t.onComment=R(t,t.onComment)}return t}function R(e,t){return function(r,i,n,a,s,o){var l={type:r?"Block":"Line",value:i,start:n,end:a};if(e.locations){l.loc=new P(this,s,o)}if(e.ranges){l.range=[n,a]}t.push(l)}}var V={};function F(e){return new RegExp("^(?:"+e.replace(/ /g,"|")+")$")}var W=function e(r,n,a){this.options=r=D(r);this.sourceFile=r.sourceFile;this.keywords=F(i[r.ecmaVersion>=6?6:5]);var s="";if(!r.allowReserved){for(var o=r.ecmaVersion;;o--){if(s=t[o]){break}}if(r.sourceType==="module"){s+=" await"}}this.reservedWords=F(s);var l=(s?s+" ":"")+t.strict;this.reservedWordsStrict=F(l);this.reservedWordsStrictBind=F(l+" "+t.strictBind);this.input=String(n);this.containsEsc=false;this.loadPlugins(r.plugins);if(a){this.pos=a;this.lineStart=this.input.lastIndexOf("\n",a-1)+1;this.curLine=this.input.slice(0,this.lineStart).split(w).length}else{this.pos=this.lineStart=0;this.curLine=1}this.type=b.eof;this.value=null;this.start=this.end=this.pos;this.startLoc=this.endLoc=this.curPosition();this.lastTokEndLoc=this.lastTokStartLoc=null;this.lastTokStart=this.lastTokEnd=this.pos;this.context=this.initialContext();this.exprAllowed=true;this.inModule=r.sourceType==="module";this.strict=this.inModule||this.strictDirective(this.pos);this.potentialArrowAt=-1;this.inFunction=this.inGenerator=this.inAsync=false;this.yieldPos=this.awaitPos=0;this.labels=[];if(this.pos===0&&r.allowHashBang&&this.input.slice(0,2)==="#!"){this.skipLineComment(2)}this.scopeStack=[];this.enterFunctionScope();this.regexpState=null};W.prototype.isKeyword=function e(t){return this.keywords.test(t)};W.prototype.isReservedWord=function e(t){return this.reservedWords.test(t)};W.prototype.extend=function e(t,r){this[t]=r(this[t])};W.prototype.loadPlugins=function e(t){var r=this;for(var i in t){var n=V[i];if(!n){throw new Error("Plugin '"+i+"' not found")}n(r,t[i])}};W.prototype.parse=function e(){var t=this.options.program||this.startNode();this.nextToken();return this.parseTopLevel(t)};var z=W.prototype;var B=/^(?:'((?:\\.|[^'])*?)'|"((?:\\.|[^"])*?)"|;)/;z.strictDirective=function(e){var t=this;for(;;){L.lastIndex=e;e+=L.exec(t.input)[0].length;var r=B.exec(t.input.slice(e));if(!r){return false}if((r[1]||r[2])==="use strict"){return true}e+=r[0].length}};z.eat=function(e){if(this.type===e){this.next();return true}else{return false}};z.isContextual=function(e){return this.type===b.name&&this.value===e&&!this.containsEsc};z.eatContextual=function(e){if(!this.isContextual(e)){return false}this.next();return true};z.expectContextual=function(e){if(!this.eatContextual(e)){this.unexpected()}};z.canInsertSemicolon=function(){return this.type===b.eof||this.type===b.braceR||w.test(this.input.slice(this.lastTokEnd,this.start))};z.insertSemicolon=function(){if(this.canInsertSemicolon()){if(this.options.onInsertedSemicolon){this.options.onInsertedSemicolon(this.lastTokEnd,this.lastTokEndLoc)}return true}};z.semicolon=function(){if(!this.eat(b.semi)&&!this.insertSemicolon()){this.unexpected()}};z.afterTrailingComma=function(e,t){if(this.type===e){if(this.options.onTrailingComma){this.options.onTrailingComma(this.lastTokStart,this.lastTokStartLoc)}if(!t){this.next()}return true}};z.expect=function(e){this.eat(e)||this.unexpected()};z.unexpected=function(e){this.raise(e!=null?e:this.start,"Unexpected token")};function H(){this.shorthandAssign=this.trailingComma=this.parenthesizedAssign=this.parenthesizedBind=this.doubleProto=-1}z.checkPatternErrors=function(e,t){if(!e){return}if(e.trailingComma>-1){this.raiseRecoverable(e.trailingComma,"Comma is not permitted after the rest element")}var r=t?e.parenthesizedAssign:e.parenthesizedBind;if(r>-1){this.raiseRecoverable(r,"Parenthesized pattern")}};z.checkExpressionErrors=function(e,t){if(!e){return false}var r=e.shorthandAssign;var i=e.doubleProto;if(!t){return r>=0||i>=0}if(r>=0){this.raise(r,"Shorthand property assignments are valid only in destructuring patterns")}if(i>=0){this.raiseRecoverable(i,"Redefinition of __proto__ property")}};z.checkYieldAwaitInDefaultParams=function(){if(this.yieldPos&&(!this.awaitPos||this.yieldPos=6){e.sourceType=this.options.sourceType}return this.finishNode(e,"Program")};var U={kind:"loop"};var G={kind:"switch"};j.isLet=function(){if(this.options.ecmaVersion<6||!this.isContextual("let")){return false}L.lastIndex=this.pos;var e=L.exec(this.input);var t=this.pos+e[0].length,r=this.input.charCodeAt(t);if(r===91||r===123){return true}if(h(r,true)){var i=t+1;while(p(this.input.charCodeAt(i),true)){++i}var a=this.input.slice(t,i);if(!n.test(a)){return true}}return false};j.isAsyncFunction=function(){if(this.options.ecmaVersion<8||!this.isContextual("async")){return false}L.lastIndex=this.pos;var e=L.exec(this.input);var t=this.pos+e[0].length;return!w.test(this.input.slice(this.pos,t))&&this.input.slice(t,t+8)==="function"&&(t+8===this.input.length||!p(this.input.charAt(t+8)))};j.parseStatement=function(e,t,r){var i=this.type,n=this.startNode(),a;if(this.isLet()){i=b._var;a="let"}switch(i){case b._break:case b._continue:return this.parseBreakContinueStatement(n,i.keyword);case b._debugger:return this.parseDebuggerStatement(n);case b._do:return this.parseDoStatement(n);case b._for:return this.parseForStatement(n);case b._function:if(!e&&this.options.ecmaVersion>=6){this.unexpected()}return this.parseFunctionStatement(n,false);case b._class:if(!e){this.unexpected()}return this.parseClass(n,true);case b._if:return this.parseIfStatement(n);case b._return:return this.parseReturnStatement(n);case b._switch:return this.parseSwitchStatement(n);case b._throw:return this.parseThrowStatement(n);case b._try:return this.parseTryStatement(n);case b._const:case b._var:a=a||this.value;if(!e&&a!=="var"){this.unexpected()}return this.parseVarStatement(n,a);case b._while:return this.parseWhileStatement(n);case b._with:return this.parseWithStatement(n);case b.braceL:return this.parseBlock();case b.semi:return this.parseEmptyStatement(n);case b._export:case b._import:if(!this.options.allowImportExportEverywhere){if(!t){this.raise(this.start,"'import' and 'export' may only appear at the top level")}if(!this.inModule){this.raise(this.start,"'import' and 'export' may appear only with 'sourceType: module'")}}return i===b._import?this.parseImport(n):this.parseExport(n,r);default:if(this.isAsyncFunction()){if(!e){this.unexpected()}this.next();return this.parseFunctionStatement(n,true)}var s=this.value,o=this.parseExpression();if(i===b.name&&o.type==="Identifier"&&this.eat(b.colon)){return this.parseLabeledStatement(n,s,o)}else{return this.parseExpressionStatement(n,o)}}};j.parseBreakContinueStatement=function(e,t){var r=this;var i=t==="break";this.next();if(this.eat(b.semi)||this.insertSemicolon()){e.label=null}else if(this.type!==b.name){this.unexpected()}else{e.label=this.parseIdent();this.semicolon()}var n=0;for(;n=6){this.eat(b.semi)}else{this.semicolon()}return this.finishNode(e,"DoWhileStatement")};j.parseForStatement=function(e){this.next();var t=this.options.ecmaVersion>=9&&(this.inAsync||!this.inFunction&&this.options.allowAwaitOutsideFunction)&&this.eatContextual("await")?this.lastTokStart:-1;this.labels.push(U);this.enterLexicalScope();this.expect(b.parenL);if(this.type===b.semi){if(t>-1){this.unexpected(t)}return this.parseFor(e,null)}var r=this.isLet();if(this.type===b._var||this.type===b._const||r){var i=this.startNode(),n=r?"let":this.value;this.next();this.parseVar(i,true,n);this.finishNode(i,"VariableDeclaration");if((this.type===b._in||this.options.ecmaVersion>=6&&this.isContextual("of"))&&i.declarations.length===1&&!(n!=="var"&&i.declarations[0].init)){if(this.options.ecmaVersion>=9){if(this.type===b._in){if(t>-1){this.unexpected(t)}}else{e.await=t>-1}}return this.parseForIn(e,i)}if(t>-1){this.unexpected(t)}return this.parseFor(e,i)}var a=new H;var s=this.parseExpression(true,a);if(this.type===b._in||this.options.ecmaVersion>=6&&this.isContextual("of")){if(this.options.ecmaVersion>=9){if(this.type===b._in){if(t>-1){this.unexpected(t)}}else{e.await=t>-1}}this.toAssignable(s,false,a);this.checkLVal(s);return this.parseForIn(e,s)}else{this.checkExpressionErrors(a,true)}if(t>-1){this.unexpected(t)}return this.parseFor(e,s)};j.parseFunctionStatement=function(e,t){this.next();return this.parseFunction(e,true,false,t)};j.parseIfStatement=function(e){this.next();e.test=this.parseParenExpression();e.consequent=this.parseStatement(!this.strict&&this.type===b._function);e.alternate=this.eat(b._else)?this.parseStatement(!this.strict&&this.type===b._function):null;return this.finishNode(e,"IfStatement")};j.parseReturnStatement=function(e){if(!this.inFunction&&!this.options.allowReturnOutsideFunction){this.raise(this.start,"'return' outside of function")}this.next();if(this.eat(b.semi)||this.insertSemicolon()){e.argument=null}else{e.argument=this.parseExpression();this.semicolon()}return this.finishNode(e,"ReturnStatement")};j.parseSwitchStatement=function(e){var t=this;this.next();e.discriminant=this.parseParenExpression();e.cases=[];this.expect(b.braceL);this.labels.push(G);this.enterLexicalScope();var r;for(var i=false;this.type!==b.braceR;){if(t.type===b._case||t.type===b._default){var n=t.type===b._case;if(r){t.finishNode(r,"SwitchCase")}e.cases.push(r=t.startNode());r.consequent=[];t.next();if(n){r.test=t.parseExpression()}else{if(i){t.raiseRecoverable(t.lastTokStart,"Multiple default clauses")}i=true;r.test=null}t.expect(b.colon)}else{if(!r){t.unexpected()}r.consequent.push(t.parseStatement(true))}}this.exitLexicalScope();if(r){this.finishNode(r,"SwitchCase")}this.next();this.labels.pop();return this.finishNode(e,"SwitchStatement")};j.parseThrowStatement=function(e){this.next();if(w.test(this.input.slice(this.lastTokEnd,this.start))){this.raise(this.lastTokEnd,"Illegal newline after throw")}e.argument=this.parseExpression();this.semicolon();return this.finishNode(e,"ThrowStatement")};var q=[];j.parseTryStatement=function(e){this.next();e.block=this.parseBlock();e.handler=null;if(this.type===b._catch){var t=this.startNode();this.next();if(this.eat(b.parenL)){t.param=this.parseBindingAtom();this.enterLexicalScope();this.checkLVal(t.param,"let");this.expect(b.parenR)}else{if(this.options.ecmaVersion<10){this.unexpected()}t.param=null;this.enterLexicalScope()}t.body=this.parseBlock(false);this.exitLexicalScope();e.handler=this.finishNode(t,"CatchClause")} +e.finalizer=this.eat(b._finally)?this.parseBlock():null;if(!e.handler&&!e.finalizer){this.raise(e.start,"Missing catch or finally clause")}return this.finishNode(e,"TryStatement")};j.parseVarStatement=function(e,t){this.next();this.parseVar(e,false,t);this.semicolon();return this.finishNode(e,"VariableDeclaration")};j.parseWhileStatement=function(e){this.next();e.test=this.parseParenExpression();this.labels.push(U);e.body=this.parseStatement(false);this.labels.pop();return this.finishNode(e,"WhileStatement")};j.parseWithStatement=function(e){if(this.strict){this.raise(this.start,"'with' in strict mode")}this.next();e.object=this.parseParenExpression();e.body=this.parseStatement(false);return this.finishNode(e,"WithStatement")};j.parseEmptyStatement=function(e){this.next();return this.finishNode(e,"EmptyStatement")};j.parseLabeledStatement=function(e,t,r){var i=this;for(var n=0,a=i.labels;n=0;l--){var u=i.labels[l];if(u.statementStart===e.start){u.statementStart=i.start;u.kind=o}else{break}}this.labels.push({name:t,kind:o,statementStart:this.start});e.body=this.parseStatement(true);if(e.body.type==="ClassDeclaration"||e.body.type==="VariableDeclaration"&&e.body.kind!=="var"||e.body.type==="FunctionDeclaration"&&(this.strict||e.body.generator||e.body.async)){this.raiseRecoverable(e.body.start,"Invalid labeled declaration")}this.labels.pop();e.label=r;return this.finishNode(e,"LabeledStatement")};j.parseExpressionStatement=function(e,t){e.expression=t;this.semicolon();return this.finishNode(e,"ExpressionStatement")};j.parseBlock=function(e){var t=this;if(e===void 0)e=true;var r=this.startNode();r.body=[];this.expect(b.braceL);if(e){this.enterLexicalScope()}while(!this.eat(b.braceR)){var i=t.parseStatement(true);r.body.push(i)}if(e){this.exitLexicalScope()}return this.finishNode(r,"BlockStatement")};j.parseFor=function(e,t){e.init=t;this.expect(b.semi);e.test=this.type===b.semi?null:this.parseExpression();this.expect(b.semi);e.update=this.type===b.parenR?null:this.parseExpression();this.expect(b.parenR);this.exitLexicalScope();e.body=this.parseStatement(false);this.labels.pop();return this.finishNode(e,"ForStatement")};j.parseForIn=function(e,t){var r=this.type===b._in?"ForInStatement":"ForOfStatement";this.next();if(r==="ForInStatement"){if(t.type==="AssignmentPattern"||t.type==="VariableDeclaration"&&t.declarations[0].init!=null&&(this.strict||t.declarations[0].id.type!=="Identifier")){this.raise(t.start,"Invalid assignment in for-in loop head")}}e.left=t;e.right=r==="ForInStatement"?this.parseExpression():this.parseMaybeAssign();this.expect(b.parenR);this.exitLexicalScope();e.body=this.parseStatement(false);this.labels.pop();return this.finishNode(e,r)};j.parseVar=function(e,t,r){var i=this;e.declarations=[];e.kind=r;for(;;){var n=i.startNode();i.parseVarId(n,r);if(i.eat(b.eq)){n.init=i.parseMaybeAssign(t)}else if(r==="const"&&!(i.type===b._in||i.options.ecmaVersion>=6&&i.isContextual("of"))){i.unexpected()}else if(n.id.type!=="Identifier"&&!(t&&(i.type===b._in||i.isContextual("of")))){i.raise(i.lastTokEnd,"Complex binding patterns require an initialization value")}else{n.init=null}e.declarations.push(i.finishNode(n,"VariableDeclarator"));if(!i.eat(b.comma)){break}}return e};j.parseVarId=function(e,t){e.id=this.parseBindingAtom(t);this.checkLVal(e.id,t,false)};j.parseFunction=function(e,t,r,i){this.initFunction(e);if(this.options.ecmaVersion>=9||this.options.ecmaVersion>=6&&!i){e.generator=this.eat(b.star)}if(this.options.ecmaVersion>=8){e.async=!!i}if(t){e.id=t==="nullableID"&&this.type!==b.name?null:this.parseIdent();if(e.id){this.checkLVal(e.id,this.inModule&&!this.inFunction?"let":"var")}}var n=this.inGenerator,a=this.inAsync,s=this.yieldPos,o=this.awaitPos,l=this.inFunction;this.inGenerator=e.generator;this.inAsync=e.async;this.yieldPos=0;this.awaitPos=0;this.inFunction=true;this.enterFunctionScope();if(!t){e.id=this.type===b.name?this.parseIdent():null}this.parseFunctionParams(e);this.parseFunctionBody(e,r);this.inGenerator=n;this.inAsync=a;this.yieldPos=s;this.awaitPos=o;this.inFunction=l;return this.finishNode(e,t?"FunctionDeclaration":"FunctionExpression")};j.parseFunctionParams=function(e){this.expect(b.parenL);e.params=this.parseBindingList(b.parenR,false,this.options.ecmaVersion>=8);this.checkYieldAwaitInDefaultParams()};j.parseClass=function(e,t){var r=this;this.next();this.parseClassId(e,t);this.parseClassSuper(e);var i=this.startNode();var n=false;i.body=[];this.expect(b.braceL);while(!this.eat(b.braceR)){var a=r.parseClassMember(i);if(a&&a.type==="MethodDefinition"&&a.kind==="constructor"){if(n){r.raise(a.start,"Duplicate constructor in the same class")}n=true}}e.body=this.finishNode(i,"ClassBody");return this.finishNode(e,t?"ClassDeclaration":"ClassExpression")};j.parseClassMember=function(e){var t=this;if(this.eat(b.semi)){return null}var r=this.startNode();var i=function(e,i){if(i===void 0)i=false;var n=t.start,a=t.startLoc;if(!t.eatContextual(e)){return false}if(t.type!==b.parenL&&(!i||!t.canInsertSemicolon())){return true}if(r.key){t.unexpected()}r.computed=false;r.key=t.startNodeAt(n,a);r.key.name=e;t.finishNode(r.key,"Identifier");return false};r.kind="method";r.static=i("static");var n=this.eat(b.star);var a=false;if(!n){if(this.options.ecmaVersion>=8&&i("async",true)){a=true;n=this.options.ecmaVersion>=9&&this.eat(b.star)}else if(i("get")){r.kind="get"}else if(i("set")){r.kind="set"}}if(!r.key){this.parsePropertyName(r)}var s=r.key;if(!r.computed&&!r.static&&(s.type==="Identifier"&&s.name==="constructor"||s.type==="Literal"&&s.value==="constructor")){if(r.kind!=="method"){this.raise(s.start,"Constructor can't have get/set modifier")}if(n){this.raise(s.start,"Constructor can't be a generator")}if(a){this.raise(s.start,"Constructor can't be an async method")}r.kind="constructor"}else if(r.static&&s.type==="Identifier"&&s.name==="prototype"){this.raise(s.start,"Classes may not have a static property named prototype")}this.parseClassMethod(e,r,n,a);if(r.kind==="get"&&r.value.params.length!==0){this.raiseRecoverable(r.value.start,"getter should have no params")}if(r.kind==="set"&&r.value.params.length!==1){this.raiseRecoverable(r.value.start,"setter should have exactly one param")}if(r.kind==="set"&&r.value.params[0].type==="RestElement"){this.raiseRecoverable(r.value.params[0].start,"Setter cannot use rest params")}return r};j.parseClassMethod=function(e,t,r,i){t.value=this.parseMethod(r,i);e.body.push(this.finishNode(t,"MethodDefinition"))};j.parseClassId=function(e,t){e.id=this.type===b.name?this.parseIdent():t===true?this.unexpected():null};j.parseClassSuper=function(e){e.superClass=this.eat(b._extends)?this.parseExprSubscripts():null};j.parseExport=function(e,t){var r=this;this.next();if(this.eat(b.star)){this.expectContextual("from");if(this.type!==b.string){this.unexpected()}e.source=this.parseExprAtom();this.semicolon();return this.finishNode(e,"ExportAllDeclaration")}if(this.eat(b._default)){this.checkExport(t,"default",this.lastTokStart);var i;if(this.type===b._function||(i=this.isAsyncFunction())){var n=this.startNode();this.next();if(i){this.next()}e.declaration=this.parseFunction(n,"nullableID",false,i)}else if(this.type===b._class){var a=this.startNode();e.declaration=this.parseClass(a,"nullableID")}else{e.declaration=this.parseMaybeAssign();this.semicolon()}return this.finishNode(e,"ExportDefaultDeclaration")}if(this.shouldParseExportStatement()){e.declaration=this.parseStatement(true);if(e.declaration.type==="VariableDeclaration"){this.checkVariableExport(t,e.declaration.declarations)}else{this.checkExport(t,e.declaration.id.name,e.declaration.id.start)}e.specifiers=[];e.source=null}else{e.declaration=null;e.specifiers=this.parseExportSpecifiers(t);if(this.eatContextual("from")){if(this.type!==b.string){this.unexpected()}e.source=this.parseExprAtom()}else{for(var s=0,o=e.specifiers;s=6&&e){switch(e.type){case"Identifier":if(this.inAsync&&e.name==="await"){this.raise(e.start,"Can not use 'await' as identifier inside an async function")}break;case"ObjectPattern":case"ArrayPattern":case"RestElement":break;case"ObjectExpression":e.type="ObjectPattern";if(r){this.checkPatternErrors(r,true)}for(var n=0,a=e.properties;n=9&&e.type==="SpreadElement"){return}if(this.options.ecmaVersion>=6&&(e.computed||e.method||e.shorthand)){return}var i=e.key;var n;switch(i.type){case"Identifier":n=i.name;break;case"Literal":n=String(i.value);break;default:return}var a=e.kind;if(this.options.ecmaVersion>=6){if(n==="__proto__"&&a==="init"){if(t.proto){if(r&&r.doubleProto<0){r.doubleProto=i.start}else{this.raiseRecoverable(i.start,"Redefinition of __proto__ property")}}t.proto=true}return}n="$"+n;var s=t[n];if(s){var o;if(a==="init"){o=this.strict&&s.init||s.get||s.set}else{o=s.init||s[a]}if(o){this.raiseRecoverable(i.start,"Redefinition of property")}}else{s=t[n]={init:false,get:false,set:false}}s[a]=true};$.parseExpression=function(e,t){var r=this;var i=this.start,n=this.startLoc;var a=this.parseMaybeAssign(e,t);if(this.type===b.comma){var s=this.startNodeAt(i,n);s.expressions=[a];while(this.eat(b.comma)){s.expressions.push(r.parseMaybeAssign(e,t))}return this.finishNode(s,"SequenceExpression")}return a};$.parseMaybeAssign=function(e,t,r){if(this.inGenerator&&this.isContextual("yield")){return this.parseYield()}var i=false,n=-1,a=-1;if(t){n=t.parenthesizedAssign;a=t.trailingComma;t.parenthesizedAssign=t.trailingComma=-1}else{t=new H;i=true}var s=this.start,o=this.startLoc;if(this.type===b.parenL||this.type===b.name){this.potentialArrowAt=this.start}var l=this.parseMaybeConditional(e,t);if(r){l=r.call(this,l,s,o)}if(this.type.isAssign){var u=this.startNodeAt(s,o);u.operator=this.value;u.left=this.type===b.eq?this.toAssignable(l,false,t):l;if(!i){H.call(t)}t.shorthandAssign=-1;this.checkLVal(l);this.next();u.right=this.parseMaybeAssign(e);return this.finishNode(u,"AssignmentExpression")}else{if(i){this.checkExpressionErrors(t,true)}}if(n>-1){t.parenthesizedAssign=n}if(a>-1){t.trailingComma=a}return l};$.parseMaybeConditional=function(e,t){var r=this.start,i=this.startLoc;var n=this.parseExprOps(e,t);if(this.checkExpressionErrors(t)){return n}if(this.eat(b.question)){var a=this.startNodeAt(r,i);a.test=n;a.consequent=this.parseMaybeAssign();this.expect(b.colon);a.alternate=this.parseMaybeAssign(e);return this.finishNode(a,"ConditionalExpression")}return n};$.parseExprOps=function(e,t){var r=this.start,i=this.startLoc;var n=this.parseMaybeUnary(t,false);if(this.checkExpressionErrors(t)){return n}return n.start===r&&n.type==="ArrowFunctionExpression"?n:this.parseExprOp(n,r,i,-1,e)};$.parseExprOp=function(e,t,r,i,n){var a=this.type.binop;if(a!=null&&(!n||this.type!==b._in)){if(a>i){var s=this.type===b.logicalOR||this.type===b.logicalAND;var o=this.value;this.next();var l=this.start,u=this.startLoc;var c=this.parseExprOp(this.parseMaybeUnary(null,false),l,u,a,n);var f=this.buildBinary(t,r,e,c,o,s);return this.parseExprOp(f,t,r,i,n)}}return e};$.buildBinary=function(e,t,r,i,n,a){var s=this.startNodeAt(e,t);s.left=r;s.operator=n;s.right=i;return this.finishNode(s,a?"LogicalExpression":"BinaryExpression")};$.parseMaybeUnary=function(e,t){var r=this;var i=this.start,n=this.startLoc,a;if(this.isContextual("await")&&(this.inAsync||!this.inFunction&&this.options.allowAwaitOutsideFunction)){a=this.parseAwait();t=true}else if(this.type.prefix){var s=this.startNode(),o=this.type===b.incDec;s.operator=this.value;s.prefix=true;this.next();s.argument=this.parseMaybeUnary(null,true);this.checkExpressionErrors(e,true);if(o){this.checkLVal(s.argument)}else if(this.strict&&s.operator==="delete"&&s.argument.type==="Identifier"){this.raiseRecoverable(s.start,"Deleting local variable in strict mode")}else{t=true}a=this.finishNode(s,o?"UpdateExpression":"UnaryExpression")}else{a=this.parseExprSubscripts(e);if(this.checkExpressionErrors(e)){return a}while(this.type.postfix&&!this.canInsertSemicolon()){var l=r.startNodeAt(i,n);l.operator=r.value;l.prefix=false;l.argument=a;r.checkLVal(a);r.next();a=r.finishNode(l,"UpdateExpression")}}if(!t&&this.eat(b.starstar)){return this.buildBinary(i,n,a,this.parseMaybeUnary(null,false),"**",false)}else{return a}};$.parseExprSubscripts=function(e){var t=this.start,r=this.startLoc;var i=this.parseExprAtom(e);var n=i.type==="ArrowFunctionExpression"&&this.input.slice(this.lastTokStart,this.lastTokEnd)!==")";if(this.checkExpressionErrors(e)||n){return i}var a=this.parseSubscripts(i,t,r);if(e&&a.type==="MemberExpression"){if(e.parenthesizedAssign>=a.start){e.parenthesizedAssign=-1}if(e.parenthesizedBind>=a.start){e.parenthesizedBind=-1}}return a};$.parseSubscripts=function(e,t,r,i){var n=this;var a=this.options.ecmaVersion>=8&&e.type==="Identifier"&&e.name==="async"&&this.lastTokEnd===e.end&&!this.canInsertSemicolon()&&this.input.slice(e.start,e.end)==="async";for(var s=void 0;;){if((s=n.eat(b.bracketL))||n.eat(b.dot)){var o=n.startNodeAt(t,r);o.object=e;o.property=s?n.parseExpression():n.parseIdent(true);o.computed=!!s;if(s){n.expect(b.bracketR)}e=n.finishNode(o,"MemberExpression")}else if(!i&&n.eat(b.parenL)){var l=new H,u=n.yieldPos,c=n.awaitPos;n.yieldPos=0;n.awaitPos=0;var f=n.parseExprList(b.parenR,n.options.ecmaVersion>=8,false,l);if(a&&!n.canInsertSemicolon()&&n.eat(b.arrow)){n.checkPatternErrors(l,false);n.checkYieldAwaitInDefaultParams();n.yieldPos=u;n.awaitPos=c;return n.parseArrowExpression(n.startNodeAt(t,r),f,true)}n.checkExpressionErrors(l,true);n.yieldPos=u||n.yieldPos;n.awaitPos=c||n.awaitPos;var h=n.startNodeAt(t,r);h.callee=e;h.arguments=f;e=n.finishNode(h,"CallExpression")}else if(n.type===b.backQuote){var p=n.startNodeAt(t,r);p.tag=e;p.quasi=n.parseTemplate({isTagged:true});e=n.finishNode(p,"TaggedTemplateExpression")}else{return e}}};$.parseExprAtom=function(e){var t,r=this.potentialArrowAt===this.start;switch(this.type){case b._super:if(!this.inFunction){this.raise(this.start,"'super' outside of function or class")}t=this.startNode();this.next();if(this.type!==b.dot&&this.type!==b.bracketL&&this.type!==b.parenL){this.unexpected()}return this.finishNode(t,"Super");case b._this:t=this.startNode();this.next();return this.finishNode(t,"ThisExpression");case b.name:var i=this.start,n=this.startLoc,a=this.containsEsc;var s=this.parseIdent(this.type!==b.name);if(this.options.ecmaVersion>=8&&!a&&s.name==="async"&&!this.canInsertSemicolon()&&this.eat(b._function)){return this.parseFunction(this.startNodeAt(i,n),false,false,true)}if(r&&!this.canInsertSemicolon()){if(this.eat(b.arrow)){return this.parseArrowExpression(this.startNodeAt(i,n),[s],false)}if(this.options.ecmaVersion>=8&&s.name==="async"&&this.type===b.name&&!a){s=this.parseIdent();if(this.canInsertSemicolon()||!this.eat(b.arrow)){this.unexpected()}return this.parseArrowExpression(this.startNodeAt(i,n),[s],true)}}return s;case b.regexp:var o=this.value;t=this.parseLiteral(o.value);t.regex={pattern:o.pattern,flags:o.flags};return t;case b.num:case b.string:return this.parseLiteral(this.value);case b._null:case b._true:case b._false:t=this.startNode();t.value=this.type===b._null?null:this.type===b._true;t.raw=this.type.keyword;this.next();return this.finishNode(t,"Literal");case b.parenL:var l=this.start,u=this.parseParenAndDistinguishExpression(r);if(e){if(e.parenthesizedAssign<0&&!this.isSimpleAssignTarget(u)){e.parenthesizedAssign=l}if(e.parenthesizedBind<0){e.parenthesizedBind=l}}return u;case b.bracketL:t=this.startNode();this.next();t.elements=this.parseExprList(b.bracketR,true,true,e);return this.finishNode(t,"ArrayExpression");case b.braceL:return this.parseObj(false,e);case b._function:t=this.startNode();this.next();return this.parseFunction(t,false);case b._class:return this.parseClass(this.startNode(),false);case b._new:return this.parseNew();case b.backQuote:return this.parseTemplate();default:this.unexpected()}};$.parseLiteral=function(e){var t=this.startNode();t.value=e;t.raw=this.input.slice(this.start,this.end);this.next();return this.finishNode(t,"Literal")};$.parseParenExpression=function(){this.expect(b.parenL);var e=this.parseExpression();this.expect(b.parenR);return e};$.parseParenAndDistinguishExpression=function(e){var t=this;var r=this.start,i=this.startLoc,n,a=this.options.ecmaVersion>=8;if(this.options.ecmaVersion>=6){this.next();var s=this.start,o=this.startLoc;var l=[],u=true,c=false;var f=new H,h=this.yieldPos,p=this.awaitPos,d;this.yieldPos=0;this.awaitPos=0;while(this.type!==b.parenR){u?u=false:t.expect(b.comma);if(a&&t.afterTrailingComma(b.parenR,true)){c=true;break}else if(t.type===b.ellipsis){d=t.start;l.push(t.parseParenItem(t.parseRestBinding()));if(t.type===b.comma){t.raise(t.start,"Comma is not permitted after the rest element")}break}else{l.push(t.parseMaybeAssign(false,f,t.parseParenItem))}}var m=this.start,v=this.startLoc;this.expect(b.parenR);if(e&&!this.canInsertSemicolon()&&this.eat(b.arrow)){this.checkPatternErrors(f,false);this.checkYieldAwaitInDefaultParams();this.yieldPos=h;this.awaitPos=p;return this.parseParenArrowList(r,i,l)}if(!l.length||c){this.unexpected(this.lastTokStart)}if(d){this.unexpected(d)}this.checkExpressionErrors(f,true);this.yieldPos=h||this.yieldPos;this.awaitPos=p||this.awaitPos;if(l.length>1){n=this.startNodeAt(s,o);n.expressions=l;this.finishNodeAt(n,"SequenceExpression",m,v)}else{n=l[0]}}else{n=this.parseParenExpression()}if(this.options.preserveParens){var g=this.startNodeAt(r,i);g.expression=n;return this.finishNode(g,"ParenthesizedExpression")}else{return n}};$.parseParenItem=function(e){return e};$.parseParenArrowList=function(e,t,r){return this.parseArrowExpression(this.startNodeAt(e,t),r)};var X=[];$.parseNew=function(){var e=this.startNode();var t=this.parseIdent(true);if(this.options.ecmaVersion>=6&&this.eat(b.dot)){e.meta=t;var r=this.containsEsc;e.property=this.parseIdent(true);if(e.property.name!=="target"||r){this.raiseRecoverable(e.property.start,"The only valid meta property for new is new.target")}if(!this.inFunction){this.raiseRecoverable(e.start,"new.target can only be used in functions")}return this.finishNode(e,"MetaProperty")}var i=this.start,n=this.startLoc;e.callee=this.parseSubscripts(this.parseExprAtom(),i,n,true);if(this.eat(b.parenL)){e.arguments=this.parseExprList(b.parenR,this.options.ecmaVersion>=8,false)}else{e.arguments=X}return this.finishNode(e,"NewExpression")};$.parseTemplateElement=function(e){var t=e.isTagged;var r=this.startNode();if(this.type===b.invalidTemplate){if(!t){this.raiseRecoverable(this.start,"Bad escape sequence in untagged template literal")}r.value={raw:this.value,cooked:null}}else{r.value={raw:this.input.slice(this.start,this.end).replace(/\r\n?/g,"\n"),cooked:this.value}}this.next();r.tail=this.type===b.backQuote;return this.finishNode(r,"TemplateElement")};$.parseTemplate=function(e){var t=this;if(e===void 0)e={};var r=e.isTagged;if(r===void 0)r=false;var i=this.startNode();this.next();i.expressions=[];var n=this.parseTemplateElement({isTagged:r});i.quasis=[n];while(!n.tail){if(t.type===b.eof){t.raise(t.pos,"Unterminated template literal")}t.expect(b.dollarBraceL);i.expressions.push(t.parseExpression());t.expect(b.braceR);i.quasis.push(n=t.parseTemplateElement({isTagged:r}))}this.next();return this.finishNode(i,"TemplateLiteral")};$.isAsyncProp=function(e){return!e.computed&&e.key.type==="Identifier"&&e.key.name==="async"&&(this.type===b.name||this.type===b.num||this.type===b.string||this.type===b.bracketL||this.type.keyword||this.options.ecmaVersion>=9&&this.type===b.star)&&!w.test(this.input.slice(this.lastTokEnd,this.start))};$.parseObj=function(e,t){var r=this;var i=this.startNode(),n=true,a={};i.properties=[];this.next();while(!this.eat(b.braceR)){if(!n){r.expect(b.comma);if(r.afterTrailingComma(b.braceR)){break}}else{n=false}var s=r.parseProperty(e,t);if(!e){r.checkPropClash(s,a,t)}i.properties.push(s)}return this.finishNode(i,e?"ObjectPattern":"ObjectExpression")};$.parseProperty=function(e,t){var r=this.startNode(),i,n,a,s;if(this.options.ecmaVersion>=9&&this.eat(b.ellipsis)){if(e){r.argument=this.parseIdent(false);if(this.type===b.comma){this.raise(this.start,"Comma is not permitted after the rest element")}return this.finishNode(r,"RestElement")}if(this.type===b.parenL&&t){if(t.parenthesizedAssign<0){t.parenthesizedAssign=this.start}if(t.parenthesizedBind<0){t.parenthesizedBind=this.start}}r.argument=this.parseMaybeAssign(false,t);if(this.type===b.comma&&t&&t.trailingComma<0){t.trailingComma=this.start}return this.finishNode(r,"SpreadElement")}if(this.options.ecmaVersion>=6){r.method=false;r.shorthand=false;if(e||t){a=this.start;s=this.startLoc}if(!e){i=this.eat(b.star)}}var o=this.containsEsc;this.parsePropertyName(r);if(!e&&!o&&this.options.ecmaVersion>=8&&!i&&this.isAsyncProp(r)){n=true;i=this.options.ecmaVersion>=9&&this.eat(b.star);this.parsePropertyName(r,t)}else{n=false}this.parsePropertyValue(r,e,i,n,a,s,t,o);return this.finishNode(r,"Property")};$.parsePropertyValue=function(e,t,r,i,n,a,s,o){if((r||i)&&this.type===b.colon){this.unexpected()}if(this.eat(b.colon)){e.value=t?this.parseMaybeDefault(this.start,this.startLoc):this.parseMaybeAssign(false,s);e.kind="init"}else if(this.options.ecmaVersion>=6&&this.type===b.parenL){if(t){this.unexpected()}e.kind="init";e.method=true;e.value=this.parseMethod(r,i)}else if(!t&&!o&&this.options.ecmaVersion>=5&&!e.computed&&e.key.type==="Identifier"&&(e.key.name==="get"||e.key.name==="set")&&(this.type!==b.comma&&this.type!==b.braceR)){if(r||i){this.unexpected()}e.kind=e.key.name;this.parsePropertyName(e);e.value=this.parseMethod(false);var l=e.kind==="get"?0:1;if(e.value.params.length!==l){var u=e.value.start;if(e.kind==="get"){this.raiseRecoverable(u,"getter should have no params")}else{this.raiseRecoverable(u,"setter should have exactly one param")}}else{if(e.kind==="set"&&e.value.params[0].type==="RestElement"){this.raiseRecoverable(e.value.params[0].start,"Setter cannot use rest params")}}}else if(this.options.ecmaVersion>=6&&!e.computed&&e.key.type==="Identifier"){this.checkUnreserved(e.key);e.kind="init";if(t){e.value=this.parseMaybeDefault(n,a,e.key)}else if(this.type===b.eq&&s){if(s.shorthandAssign<0){s.shorthandAssign=this.start}e.value=this.parseMaybeDefault(n,a,e.key)}else{e.value=e.key}e.shorthand=true}else{this.unexpected()}};$.parsePropertyName=function(e){if(this.options.ecmaVersion>=6){if(this.eat(b.bracketL)){e.computed=true;e.key=this.parseMaybeAssign();this.expect(b.bracketR);return e.key}else{e.computed=false}}return e.key=this.type===b.num||this.type===b.string?this.parseExprAtom():this.parseIdent(true)};$.initFunction=function(e){e.id=null;if(this.options.ecmaVersion>=6){e.generator=false;e.expression=false}if(this.options.ecmaVersion>=8){e.async=false}};$.parseMethod=function(e,t){var r=this.startNode(),i=this.inGenerator,n=this.inAsync,a=this.yieldPos,s=this.awaitPos,o=this.inFunction;this.initFunction(r);if(this.options.ecmaVersion>=6){r.generator=e}if(this.options.ecmaVersion>=8){r.async=!!t}this.inGenerator=r.generator;this.inAsync=r.async;this.yieldPos=0;this.awaitPos=0;this.inFunction=true;this.enterFunctionScope();this.expect(b.parenL);r.params=this.parseBindingList(b.parenR,false,this.options.ecmaVersion>=8);this.checkYieldAwaitInDefaultParams();this.parseFunctionBody(r,false);this.inGenerator=i;this.inAsync=n;this.yieldPos=a;this.awaitPos=s;this.inFunction=o;return this.finishNode(r,"FunctionExpression")};$.parseArrowExpression=function(e,t,r){var i=this.inGenerator,n=this.inAsync,a=this.yieldPos,s=this.awaitPos,o=this.inFunction;this.enterFunctionScope();this.initFunction(e);if(this.options.ecmaVersion>=8){e.async=!!r}this.inGenerator=false;this.inAsync=e.async;this.yieldPos=0;this.awaitPos=0;this.inFunction=true;e.params=this.toAssignableList(t,true);this.parseFunctionBody(e,true);this.inGenerator=i;this.inAsync=n;this.yieldPos=a;this.awaitPos=s;this.inFunction=o;return this.finishNode(e,"ArrowFunctionExpression")};$.parseFunctionBody=function(e,t){var r=t&&this.type!==b.braceL;var i=this.strict,n=false;if(r){e.body=this.parseMaybeAssign();e.expression=true;this.checkParams(e,false)}else{var a=this.options.ecmaVersion>=7&&!this.isSimpleParamList(e.params);if(!i||a){n=this.strictDirective(this.end);if(n&&a){this.raiseRecoverable(e.start,"Illegal 'use strict' directive in function with non-simple parameter list")}}var s=this.labels;this.labels=[];if(n){this.strict=true}this.checkParams(e,!i&&!n&&!t&&this.isSimpleParamList(e.params));e.body=this.parseBlock(false);e.expression=false;this.adaptDirectivePrologue(e.body.body);this.labels=s}this.exitFunctionScope();if(this.strict&&e.id){ +this.checkLVal(e.id,"none")}this.strict=i};$.isSimpleParamList=function(e){for(var t=0,r=e;t0)t[r]=arguments[r+1];for(var i=0,n=t;i=1;t--){var r=e.context[t];if(r.token==="function"){return r.generator}}return false};ne.updateContext=function(e){var t,r=this.type;if(r.keyword&&e===b.dot){this.exprAllowed=false}else if(t=r.updateContext){t.call(this,e)}else{this.exprAllowed=r.beforeExpr}};b.parenR.updateContext=b.braceR.updateContext=function(){if(this.context.length===1){this.exprAllowed=true;return}var e=this.context.pop();if(e===ie.b_stat&&this.curContext().token==="function"){e=this.context.pop()}this.exprAllowed=!e.isExpr};b.braceL.updateContext=function(e){this.context.push(this.braceIsBlock(e)?ie.b_stat:ie.b_expr);this.exprAllowed=true};b.dollarBraceL.updateContext=function(){this.context.push(ie.b_tmpl);this.exprAllowed=true};b.parenL.updateContext=function(e){var t=e===b._if||e===b._for||e===b._with||e===b._while;this.context.push(t?ie.p_stat:ie.p_expr);this.exprAllowed=true};b.incDec.updateContext=function(){};b._function.updateContext=b._class.updateContext=function(e){if(e.beforeExpr&&e!==b.semi&&e!==b._else&&!((e===b.colon||e===b.braceL)&&this.curContext()===ie.b_stat)){this.context.push(ie.f_expr)}else{this.context.push(ie.f_stat)}this.exprAllowed=false};b.backQuote.updateContext=function(){if(this.curContext()===ie.q_tmpl){this.context.pop()}else{this.context.push(ie.q_tmpl)}this.exprAllowed=false};b.star.updateContext=function(e){if(e===b._function){var t=this.context.length-1;if(this.context[t]===ie.f_expr){this.context[t]=ie.f_expr_gen}else{this.context[t]=ie.f_gen}}this.exprAllowed=true};b.name.updateContext=function(e){var t=false;if(this.options.ecmaVersion>=6&&e!==b.dot){if(this.value==="of"&&!this.exprAllowed||this.value==="yield"&&this.inGeneratorContext()){t=true}}this.exprAllowed=t};var ae={$LONE:["ASCII","ASCII_Hex_Digit","AHex","Alphabetic","Alpha","Any","Assigned","Bidi_Control","Bidi_C","Bidi_Mirrored","Bidi_M","Case_Ignorable","CI","Cased","Changes_When_Casefolded","CWCF","Changes_When_Casemapped","CWCM","Changes_When_Lowercased","CWL","Changes_When_NFKC_Casefolded","CWKCF","Changes_When_Titlecased","CWT","Changes_When_Uppercased","CWU","Dash","Default_Ignorable_Code_Point","DI","Deprecated","Dep","Diacritic","Dia","Emoji","Emoji_Component","Emoji_Modifier","Emoji_Modifier_Base","Emoji_Presentation","Extender","Ext","Grapheme_Base","Gr_Base","Grapheme_Extend","Gr_Ext","Hex_Digit","Hex","IDS_Binary_Operator","IDSB","IDS_Trinary_Operator","IDST","ID_Continue","IDC","ID_Start","IDS","Ideographic","Ideo","Join_Control","Join_C","Logical_Order_Exception","LOE","Lowercase","Lower","Math","Noncharacter_Code_Point","NChar","Pattern_Syntax","Pat_Syn","Pattern_White_Space","Pat_WS","Quotation_Mark","QMark","Radical","Regional_Indicator","RI","Sentence_Terminal","STerm","Soft_Dotted","SD","Terminal_Punctuation","Term","Unified_Ideograph","UIdeo","Uppercase","Upper","Variation_Selector","VS","White_Space","space","XID_Continue","XIDC","XID_Start","XIDS"],General_Category:["Cased_Letter","LC","Close_Punctuation","Pe","Connector_Punctuation","Pc","Control","Cc","cntrl","Currency_Symbol","Sc","Dash_Punctuation","Pd","Decimal_Number","Nd","digit","Enclosing_Mark","Me","Final_Punctuation","Pf","Format","Cf","Initial_Punctuation","Pi","Letter","L","Letter_Number","Nl","Line_Separator","Zl","Lowercase_Letter","Ll","Mark","M","Combining_Mark","Math_Symbol","Sm","Modifier_Letter","Lm","Modifier_Symbol","Sk","Nonspacing_Mark","Mn","Number","N","Open_Punctuation","Ps","Other","C","Other_Letter","Lo","Other_Number","No","Other_Punctuation","Po","Other_Symbol","So","Paragraph_Separator","Zp","Private_Use","Co","Punctuation","P","punct","Separator","Z","Space_Separator","Zs","Spacing_Mark","Mc","Surrogate","Cs","Symbol","S","Titlecase_Letter","Lt","Unassigned","Cn","Uppercase_Letter","Lu"],Script:["Adlam","Adlm","Ahom","Anatolian_Hieroglyphs","Hluw","Arabic","Arab","Armenian","Armn","Avestan","Avst","Balinese","Bali","Bamum","Bamu","Bassa_Vah","Bass","Batak","Batk","Bengali","Beng","Bhaiksuki","Bhks","Bopomofo","Bopo","Brahmi","Brah","Braille","Brai","Buginese","Bugi","Buhid","Buhd","Canadian_Aboriginal","Cans","Carian","Cari","Caucasian_Albanian","Aghb","Chakma","Cakm","Cham","Cherokee","Cher","Common","Zyyy","Coptic","Copt","Qaac","Cuneiform","Xsux","Cypriot","Cprt","Cyrillic","Cyrl","Deseret","Dsrt","Devanagari","Deva","Duployan","Dupl","Egyptian_Hieroglyphs","Egyp","Elbasan","Elba","Ethiopic","Ethi","Georgian","Geor","Glagolitic","Glag","Gothic","Goth","Grantha","Gran","Greek","Grek","Gujarati","Gujr","Gurmukhi","Guru","Han","Hani","Hangul","Hang","Hanunoo","Hano","Hatran","Hatr","Hebrew","Hebr","Hiragana","Hira","Imperial_Aramaic","Armi","Inherited","Zinh","Qaai","Inscriptional_Pahlavi","Phli","Inscriptional_Parthian","Prti","Javanese","Java","Kaithi","Kthi","Kannada","Knda","Katakana","Kana","Kayah_Li","Kali","Kharoshthi","Khar","Khmer","Khmr","Khojki","Khoj","Khudawadi","Sind","Lao","Laoo","Latin","Latn","Lepcha","Lepc","Limbu","Limb","Linear_A","Lina","Linear_B","Linb","Lisu","Lycian","Lyci","Lydian","Lydi","Mahajani","Mahj","Malayalam","Mlym","Mandaic","Mand","Manichaean","Mani","Marchen","Marc","Masaram_Gondi","Gonm","Meetei_Mayek","Mtei","Mende_Kikakui","Mend","Meroitic_Cursive","Merc","Meroitic_Hieroglyphs","Mero","Miao","Plrd","Modi","Mongolian","Mong","Mro","Mroo","Multani","Mult","Myanmar","Mymr","Nabataean","Nbat","New_Tai_Lue","Talu","Newa","Nko","Nkoo","Nushu","Nshu","Ogham","Ogam","Ol_Chiki","Olck","Old_Hungarian","Hung","Old_Italic","Ital","Old_North_Arabian","Narb","Old_Permic","Perm","Old_Persian","Xpeo","Old_South_Arabian","Sarb","Old_Turkic","Orkh","Oriya","Orya","Osage","Osge","Osmanya","Osma","Pahawh_Hmong","Hmng","Palmyrene","Palm","Pau_Cin_Hau","Pauc","Phags_Pa","Phag","Phoenician","Phnx","Psalter_Pahlavi","Phlp","Rejang","Rjng","Runic","Runr","Samaritan","Samr","Saurashtra","Saur","Sharada","Shrd","Shavian","Shaw","Siddham","Sidd","SignWriting","Sgnw","Sinhala","Sinh","Sora_Sompeng","Sora","Soyombo","Soyo","Sundanese","Sund","Syloti_Nagri","Sylo","Syriac","Syrc","Tagalog","Tglg","Tagbanwa","Tagb","Tai_Le","Tale","Tai_Tham","Lana","Tai_Viet","Tavt","Takri","Takr","Tamil","Taml","Tangut","Tang","Telugu","Telu","Thaana","Thaa","Thai","Tibetan","Tibt","Tifinagh","Tfng","Tirhuta","Tirh","Ugaritic","Ugar","Vai","Vaii","Warang_Citi","Wara","Yi","Yiii","Zanabazar_Square","Zanb"]};Array.prototype.push.apply(ae.$LONE,ae.General_Category);ae.gc=ae.General_Category;ae.sc=ae.Script_Extensions=ae.scx=ae.Script;var se=W.prototype;var oe=function e(t){this.parser=t;this.validFlags="gim"+(t.options.ecmaVersion>=6?"uy":"")+(t.options.ecmaVersion>=9?"s":"");this.source="";this.flags="";this.start=0;this.switchU=false;this.switchN=false;this.pos=0;this.lastIntValue=0;this.lastStringValue="";this.lastAssertionIsQuantifiable=false;this.numCapturingParens=0;this.maxBackReference=0;this.groupNames=[];this.backReferenceNames=[]};oe.prototype.reset=function e(t,r,i){var n=i.indexOf("u")!==-1;this.start=t|0;this.source=r+"";this.flags=i;this.switchU=n&&this.parser.options.ecmaVersion>=6;this.switchN=n&&this.parser.options.ecmaVersion>=9};oe.prototype.raise=function e(t){this.parser.raiseRecoverable(this.start,"Invalid regular expression: /"+this.source+"/: "+t)};oe.prototype.at=function e(t){var r=this.source;var i=r.length;if(t>=i){return-1}var n=r.charCodeAt(t);if(!this.switchU||n<=55295||n>=57344||t+1>=i){return n}return(n<<10)+r.charCodeAt(t+1)-56613888};oe.prototype.nextIndex=function e(t){var r=this.source;var i=r.length;if(t>=i){return i}var n=r.charCodeAt(t);if(!this.switchU||n<=55295||n>=57344||t+1>=i){return t+1}return t+2};oe.prototype.current=function e(){return this.at(this.pos)};oe.prototype.lookahead=function e(){return this.at(this.nextIndex(this.pos))};oe.prototype.advance=function e(){this.pos=this.nextIndex(this.pos)};oe.prototype.eat=function e(t){if(this.current()===t){this.advance();return true}return false};function le(e){if(e<=65535){return String.fromCharCode(e)}e-=65536;return String.fromCharCode((e>>10)+55296,(e&1023)+56320)}se.validateRegExpFlags=function(e){var t=this;var r=e.validFlags;var i=e.flags;for(var n=0;n-1){t.raise(e.start,"Duplicate regular expression flag")}}};se.validateRegExpPattern=function(e){this.regexp_pattern(e);if(!e.switchN&&this.options.ecmaVersion>=9&&e.groupNames.length>0){e.switchN=true;this.regexp_pattern(e)}};se.regexp_pattern=function(e){e.pos=0;e.lastIntValue=0;e.lastStringValue="";e.lastAssertionIsQuantifiable=false;e.numCapturingParens=0;e.maxBackReference=0;e.groupNames.length=0;e.backReferenceNames.length=0;this.regexp_disjunction(e);if(e.pos!==e.source.length){if(e.eat(41)){e.raise("Unmatched ')'")}if(e.eat(93)||e.eat(125)){e.raise("Lone quantifier brackets")}}if(e.maxBackReference>e.numCapturingParens){e.raise("Invalid escape")}for(var t=0,r=e.backReferenceNames;t=9){r=e.eat(60)}if(e.eat(61)||e.eat(33)){this.regexp_disjunction(e);if(!e.eat(41)){e.raise("Unterminated group")}e.lastAssertionIsQuantifiable=!r;return true}}e.pos=t;return false};se.regexp_eatQuantifier=function(e,t){if(t===void 0)t=false;if(this.regexp_eatQuantifierPrefix(e,t)){e.eat(63);return true}return false};se.regexp_eatQuantifierPrefix=function(e,t){return e.eat(42)||e.eat(43)||e.eat(63)||this.regexp_eatBracedQuantifier(e,t)};se.regexp_eatBracedQuantifier=function(e,t){var r=e.pos;if(e.eat(123)){var i=0,n=-1;if(this.regexp_eatDecimalDigits(e)){i=e.lastIntValue;if(e.eat(44)&&this.regexp_eatDecimalDigits(e)){n=e.lastIntValue}if(e.eat(125)){if(n!==-1&&n=9){this.regexp_groupSpecifier(e)}else if(e.current()===63){e.raise("Invalid group")}this.regexp_disjunction(e);if(e.eat(41)){e.numCapturingParens+=1;return true}e.raise("Unterminated group")}return false};se.regexp_eatExtendedAtom=function(e){return e.eat(46)||this.regexp_eatReverseSolidusAtomEscape(e)||this.regexp_eatCharacterClass(e)||this.regexp_eatUncapturingGroup(e)||this.regexp_eatCapturingGroup(e)||this.regexp_eatInvalidBracedQuantifier(e)||this.regexp_eatExtendedPatternCharacter(e)};se.regexp_eatInvalidBracedQuantifier=function(e){if(this.regexp_eatBracedQuantifier(e,true)){e.raise("Nothing to repeat")}return false};se.regexp_eatSyntaxCharacter=function(e){var t=e.current();if(ue(t)){e.lastIntValue=t;e.advance();return true}return false};function ue(e){return e===36||e>=40&&e<=43||e===46||e===63||e>=91&&e<=94||e>=123&&e<=125}se.regexp_eatPatternCharacters=function(e){var t=e.pos;var r=0;while((r=e.current())!==-1&&!ue(r)){e.advance()}return e.pos!==t};se.regexp_eatExtendedPatternCharacter=function(e){var t=e.current();if(t!==-1&&t!==36&&!(t>=40&&t<=43)&&t!==46&&t!==63&&t!==91&&t!==94&&t!==124){e.advance();return true}return false};se.regexp_groupSpecifier=function(e){if(e.eat(63)){if(this.regexp_eatGroupName(e)){if(e.groupNames.indexOf(e.lastStringValue)!==-1){e.raise("Duplicate capture group name")}e.groupNames.push(e.lastStringValue);return}e.raise("Invalid group")}};se.regexp_eatGroupName=function(e){e.lastStringValue="";if(e.eat(60)){if(this.regexp_eatRegExpIdentifierName(e)&&e.eat(62)){return true}e.raise("Invalid capture group name")}return false};se.regexp_eatRegExpIdentifierName=function(e){e.lastStringValue="";if(this.regexp_eatRegExpIdentifierStart(e)){e.lastStringValue+=le(e.lastIntValue);while(this.regexp_eatRegExpIdentifierPart(e)){e.lastStringValue+=le(e.lastIntValue)}return true}return false};se.regexp_eatRegExpIdentifierStart=function(e){var t=e.pos;var r=e.current();e.advance();if(r===92&&this.regexp_eatRegExpUnicodeEscapeSequence(e)){r=e.lastIntValue}if(ce(r)){e.lastIntValue=r;return true}e.pos=t;return false};function ce(e){return h(e,true)||e===36||e===95}se.regexp_eatRegExpIdentifierPart=function(e){var t=e.pos;var r=e.current();e.advance();if(r===92&&this.regexp_eatRegExpUnicodeEscapeSequence(e)){r=e.lastIntValue}if(fe(r)){e.lastIntValue=r;return true}e.pos=t;return false};function fe(e){return p(e,true)||e===36||e===95||e===8204||e===8205}se.regexp_eatAtomEscape=function(e){if(this.regexp_eatBackReference(e)||this.regexp_eatCharacterClassEscape(e)||this.regexp_eatCharacterEscape(e)||e.switchN&&this.regexp_eatKGroupName(e)){return true}if(e.switchU){if(e.current()===99){e.raise("Invalid unicode escape")}e.raise("Invalid escape")}return false};se.regexp_eatBackReference=function(e){var t=e.pos;if(this.regexp_eatDecimalEscape(e)){var r=e.lastIntValue;if(e.switchU){if(r>e.maxBackReference){e.maxBackReference=r}return true}if(r<=e.numCapturingParens){return true}e.pos=t}return false};se.regexp_eatKGroupName=function(e){if(e.eat(107)){if(this.regexp_eatGroupName(e)){e.backReferenceNames.push(e.lastStringValue);return true}e.raise("Invalid named reference")}return false};se.regexp_eatCharacterEscape=function(e){return this.regexp_eatControlEscape(e)||this.regexp_eatCControlLetter(e)||this.regexp_eatZero(e)||this.regexp_eatHexEscapeSequence(e)||this.regexp_eatRegExpUnicodeEscapeSequence(e)||!e.switchU&&this.regexp_eatLegacyOctalEscapeSequence(e)||this.regexp_eatIdentityEscape(e)};se.regexp_eatCControlLetter=function(e){var t=e.pos;if(e.eat(99)){if(this.regexp_eatControlLetter(e)){return true}e.pos=t}return false};se.regexp_eatZero=function(e){if(e.current()===48&&!ge(e.lookahead())){e.lastIntValue=0;e.advance();return true}return false};se.regexp_eatControlEscape=function(e){var t=e.current();if(t===116){e.lastIntValue=9;e.advance();return true}if(t===110){e.lastIntValue=10;e.advance();return true}if(t===118){e.lastIntValue=11;e.advance();return true}if(t===102){e.lastIntValue=12;e.advance();return true}if(t===114){e.lastIntValue=13;e.advance();return true}return false};se.regexp_eatControlLetter=function(e){var t=e.current();if(he(t)){e.lastIntValue=t%32;e.advance();return true}return false};function he(e){return e>=65&&e<=90||e>=97&&e<=122}se.regexp_eatRegExpUnicodeEscapeSequence=function(e){var t=e.pos;if(e.eat(117)){if(this.regexp_eatFixedHexDigits(e,4)){var r=e.lastIntValue;if(e.switchU&&r>=55296&&r<=56319){var i=e.pos;if(e.eat(92)&&e.eat(117)&&this.regexp_eatFixedHexDigits(e,4)){var n=e.lastIntValue;if(n>=56320&&n<=57343){e.lastIntValue=(r-55296)*1024+(n-56320)+65536;return true}}e.pos=i;e.lastIntValue=r}return true}if(e.switchU&&e.eat(123)&&this.regexp_eatHexDigits(e)&&e.eat(125)&&pe(e.lastIntValue)){return true}if(e.switchU){e.raise("Invalid unicode escape")}e.pos=t}return false};function pe(e){return e>=0&&e<=1114111}se.regexp_eatIdentityEscape=function(e){if(e.switchU){if(this.regexp_eatSyntaxCharacter(e)){return true}if(e.eat(47)){e.lastIntValue=47;return true}return false}var t=e.current();if(t!==99&&(!e.switchN||t!==107)){e.lastIntValue=t;e.advance();return true}return false};se.regexp_eatDecimalEscape=function(e){e.lastIntValue=0;var t=e.current();if(t>=49&&t<=57){do{e.lastIntValue=10*e.lastIntValue+(t-48);e.advance()}while((t=e.current())>=48&&t<=57);return true}return false};se.regexp_eatCharacterClassEscape=function(e){var t=e.current();if(de(t)){e.lastIntValue=-1;e.advance();return true}if(e.switchU&&this.options.ecmaVersion>=9&&(t===80||t===112)){e.lastIntValue=-1;e.advance();if(e.eat(123)&&this.regexp_eatUnicodePropertyValueExpression(e)&&e.eat(125)){return true}e.raise("Invalid property name")}return false};function de(e){return e===100||e===68||e===115||e===83||e===119||e===87}se.regexp_eatUnicodePropertyValueExpression=function(e){var t=e.pos;if(this.regexp_eatUnicodePropertyName(e)&&e.eat(61)){var r=e.lastStringValue;if(this.regexp_eatUnicodePropertyValue(e)){var i=e.lastStringValue;this.regexp_validateUnicodePropertyNameAndValue(e,r,i);return true}}e.pos=t;if(this.regexp_eatLoneUnicodePropertyNameOrValue(e)){var n=e.lastStringValue;this.regexp_validateUnicodePropertyNameOrValue(e,n);return true}return false};se.regexp_validateUnicodePropertyNameAndValue=function(e,t,r){if(!ae.hasOwnProperty(t)||ae[t].indexOf(r)===-1){e.raise("Invalid property name")}};se.regexp_validateUnicodePropertyNameOrValue=function(e,t){if(ae.$LONE.indexOf(t)===-1){e.raise("Invalid property name")}};se.regexp_eatUnicodePropertyName=function(e){var t=0;e.lastStringValue="";while(me(t=e.current())){e.lastStringValue+=le(t);e.advance()}return e.lastStringValue!==""};function me(e){return he(e)||e===95}se.regexp_eatUnicodePropertyValue=function(e){var t=0;e.lastStringValue="";while(ve(t=e.current())){e.lastStringValue+=le(t);e.advance()}return e.lastStringValue!==""};function ve(e){return me(e)||ge(e)}se.regexp_eatLoneUnicodePropertyNameOrValue=function(e){return this.regexp_eatUnicodePropertyValue(e)};se.regexp_eatCharacterClass=function(e){if(e.eat(91)){e.eat(94);this.regexp_classRanges(e);if(e.eat(93)){return true}e.raise("Unterminated character class")}return false};se.regexp_classRanges=function(e){var t=this;while(this.regexp_eatClassAtom(e)){var r=e.lastIntValue;if(e.eat(45)&&t.regexp_eatClassAtom(e)){var i=e.lastIntValue;if(e.switchU&&(r===-1||i===-1)){e.raise("Invalid character class")}if(r!==-1&&i!==-1&&r>i){e.raise("Range out of order in character class")}}}};se.regexp_eatClassAtom=function(e){var t=e.pos;if(e.eat(92)){if(this.regexp_eatClassEscape(e)){return true}if(e.switchU){var r=e.current();if(r===99||be(r)){e.raise("Invalid class escape")}e.raise("Invalid escape")}e.pos=t}var i=e.current();if(i!==93){e.lastIntValue=i;e.advance();return true}return false};se.regexp_eatClassEscape=function(e){var t=e.pos;if(e.eat(98)){e.lastIntValue=8;return true}if(e.switchU&&e.eat(45)){e.lastIntValue=45;return true}if(!e.switchU&&e.eat(99)){if(this.regexp_eatClassControlLetter(e)){return true}e.pos=t}return this.regexp_eatCharacterClassEscape(e)||this.regexp_eatCharacterEscape(e)};se.regexp_eatClassControlLetter=function(e){var t=e.current();if(ge(t)||t===95){e.lastIntValue=t%32;e.advance();return true}return false};se.regexp_eatHexEscapeSequence=function(e){var t=e.pos;if(e.eat(120)){if(this.regexp_eatFixedHexDigits(e,2)){return true}if(e.switchU){e.raise("Invalid escape")}e.pos=t}return false};se.regexp_eatDecimalDigits=function(e){var t=e.pos;var r=0;e.lastIntValue=0;while(ge(r=e.current())){e.lastIntValue=10*e.lastIntValue+(r-48);e.advance()}return e.pos!==t};function ge(e){return e>=48&&e<=57}se.regexp_eatHexDigits=function(e){var t=e.pos;var r=0;e.lastIntValue=0;while(ye(r=e.current())){e.lastIntValue=16*e.lastIntValue+xe(r);e.advance()}return e.pos!==t};function ye(e){return e>=48&&e<=57||e>=65&&e<=70||e>=97&&e<=102}function xe(e){if(e>=65&&e<=70){return 10+(e-65)}if(e>=97&&e<=102){return 10+(e-97)}return e-48}se.regexp_eatLegacyOctalEscapeSequence=function(e){if(this.regexp_eatOctalDigit(e)){var t=e.lastIntValue;if(this.regexp_eatOctalDigit(e)){var r=e.lastIntValue;if(t<=3&&this.regexp_eatOctalDigit(e)){e.lastIntValue=t*64+r*8+e.lastIntValue}else{e.lastIntValue=t*8+r}}else{e.lastIntValue=t}return true}return false};se.regexp_eatOctalDigit=function(e){var t=e.current();if(be(t)){e.lastIntValue=t-48;e.advance();return true}e.lastIntValue=0;return false};function be(e){return e>=48&&e<=55}se.regexp_eatFixedHexDigits=function(e,t){var r=e.pos;e.lastIntValue=0;for(var i=0;i=this.input.length){return this.finishToken(b.eof)}if(e.override){return e.override(this)}else{this.readToken(this.fullCharCodeAtPos())}};ke.readToken=function(e){if(h(e,this.options.ecmaVersion>=6)||e===92){return this.readWord()}return this.getTokenFromCode(e)};ke.fullCharCodeAtPos=function(){var e=this.input.charCodeAt(this.pos);if(e<=55295||e>=57344){return e}var t=this.input.charCodeAt(this.pos+1);return(e<<10)+t-56613888};ke.skipBlockComment=function(){var e=this;var t=this.options.onComment&&this.curPosition();var r=this.pos,i=this.input.indexOf("*/",this.pos+=2);if(i===-1){this.raise(this.pos-2,"Unterminated comment")}this.pos=i+2;if(this.options.locations){k.lastIndex=r;var n;while((n=k.exec(this.input))&&n.index8&&t<14||t>=5760&&S.test(String.fromCharCode(t))){++e.pos}else{break e}}}};ke.finishToken=function(e,t){this.end=this.pos;if(this.options.locations){this.endLoc=this.curPosition()}var r=this.type;this.type=e;this.value=t;this.updateContext(r)};ke.readToken_dot=function(){var e=this.input.charCodeAt(this.pos+1);if(e>=48&&e<=57){return this.readNumber(true)}var t=this.input.charCodeAt(this.pos+2);if(this.options.ecmaVersion>=6&&e===46&&t===46){this.pos+=3;return this.finishToken(b.ellipsis)}else{++this.pos;return this.finishToken(b.dot)}};ke.readToken_slash=function(){var e=this.input.charCodeAt(this.pos+1);if(this.exprAllowed){++this.pos;return this.readRegexp()}if(e===61){return this.finishOp(b.assign,2)}return this.finishOp(b.slash,1)};ke.readToken_mult_modulo_exp=function(e){var t=this.input.charCodeAt(this.pos+1);var r=1;var i=e===42?b.star:b.modulo;if(this.options.ecmaVersion>=7&&e===42&&t===42){++r;i=b.starstar;t=this.input.charCodeAt(this.pos+2)}if(t===61){return this.finishOp(b.assign,r+1)}return this.finishOp(i,r)};ke.readToken_pipe_amp=function(e){var t=this.input.charCodeAt(this.pos+1);if(t===e){return this.finishOp(e===124?b.logicalOR:b.logicalAND,2)}if(t===61){return this.finishOp(b.assign,2)}return this.finishOp(e===124?b.bitwiseOR:b.bitwiseAND,1)};ke.readToken_caret=function(){var e=this.input.charCodeAt(this.pos+1);if(e===61){return this.finishOp(b.assign,2)}return this.finishOp(b.bitwiseXOR,1)};ke.readToken_plus_min=function(e){var t=this.input.charCodeAt(this.pos+1);if(t===e){if(t===45&&!this.inModule&&this.input.charCodeAt(this.pos+2)===62&&(this.lastTokEnd===0||w.test(this.input.slice(this.lastTokEnd,this.pos)))){this.skipLineComment(3);this.skipSpace();return this.nextToken()}return this.finishOp(b.incDec,2)}if(t===61){return this.finishOp(b.assign,2)}return this.finishOp(b.plusMin,1)};ke.readToken_lt_gt=function(e){var t=this.input.charCodeAt(this.pos+1);var r=1;if(t===e){r=e===62&&this.input.charCodeAt(this.pos+2)===62?3:2;if(this.input.charCodeAt(this.pos+r)===61){return this.finishOp(b.assign,r+1)}return this.finishOp(b.bitShift,r)}if(t===33&&e===60&&!this.inModule&&this.input.charCodeAt(this.pos+2)===45&&this.input.charCodeAt(this.pos+3)===45){this.skipLineComment(4);this.skipSpace();return this.nextToken()}if(t===61){r=2}return this.finishOp(b.relational,r)};ke.readToken_eq_excl=function(e){var t=this.input.charCodeAt(this.pos+1);if(t===61){return this.finishOp(b.equality,this.input.charCodeAt(this.pos+2)===61?3:2)}if(e===61&&t===62&&this.options.ecmaVersion>=6){this.pos+=2;return this.finishToken(b.arrow)}return this.finishOp(e===61?b.eq:b.prefix,1)};ke.getTokenFromCode=function(e){switch(e){case 46:return this.readToken_dot();case 40:++this.pos;return this.finishToken(b.parenL);case 41:++this.pos;return this.finishToken(b.parenR);case 59:++this.pos;return this.finishToken(b.semi);case 44:++this.pos;return this.finishToken(b.comma);case 91:++this.pos +;return this.finishToken(b.bracketL);case 93:++this.pos;return this.finishToken(b.bracketR);case 123:++this.pos;return this.finishToken(b.braceL);case 125:++this.pos;return this.finishToken(b.braceR);case 58:++this.pos;return this.finishToken(b.colon);case 63:++this.pos;return this.finishToken(b.question);case 96:if(this.options.ecmaVersion<6){break}++this.pos;return this.finishToken(b.backQuote);case 48:var t=this.input.charCodeAt(this.pos+1);if(t===120||t===88){return this.readRadixNumber(16)}if(this.options.ecmaVersion>=6){if(t===111||t===79){return this.readRadixNumber(8)}if(t===98||t===66){return this.readRadixNumber(2)}}case 49:case 50:case 51:case 52:case 53:case 54:case 55:case 56:case 57:return this.readNumber(false);case 34:case 39:return this.readString(e);case 47:return this.readToken_slash();case 37:case 42:return this.readToken_mult_modulo_exp(e);case 124:case 38:return this.readToken_pipe_amp(e);case 94:return this.readToken_caret();case 43:case 45:return this.readToken_plus_min(e);case 60:case 62:return this.readToken_lt_gt(e);case 61:case 33:return this.readToken_eq_excl(e);case 126:return this.finishOp(b.prefix,1)}this.raise(this.pos,"Unexpected character '"+Ce(e)+"'")};ke.finishOp=function(e,t){var r=this.input.slice(this.pos,this.pos+t);this.pos+=t;return this.finishToken(e,r)};ke.readRegexp=function(){var e=this;var t,r,i=this.pos;for(;;){if(e.pos>=e.input.length){e.raise(i,"Unterminated regular expression")}var n=e.input.charAt(e.pos);if(w.test(n)){e.raise(i,"Unterminated regular expression")}if(!t){if(n==="["){r=true}else if(n==="]"&&r){r=false}else if(n==="/"&&!r){break}t=n==="\\"}else{t=false}++e.pos}var a=this.input.slice(i,this.pos);++this.pos;var s=this.pos;var o=this.readWord1();if(this.containsEsc){this.unexpected(s)}var l=this.regexpState||(this.regexpState=new oe(this));l.reset(i,a,o);this.validateRegExpFlags(l);this.validateRegExpPattern(l);var u=null;try{u=new RegExp(a,o)}catch(e){}return this.finishToken(b.regexp,{pattern:a,flags:o,value:u})};ke.readInt=function(e,t){var r=this;var i=this.pos,n=0;for(var a=0,s=t==null?Infinity:t;a=97){l=o-97+10}else if(o>=65){l=o-65+10}else if(o>=48&&o<=57){l=o-48}else{l=Infinity}if(l>=e){break}++r.pos;n=n*e+l}if(this.pos===i||t!=null&&this.pos-i!==t){return null}return n};ke.readRadixNumber=function(e){this.pos+=2;var t=this.readInt(e);if(t==null){this.raise(this.start+2,"Expected number in radix "+e)}if(h(this.fullCharCodeAtPos())){this.raise(this.pos,"Identifier directly after number")}return this.finishToken(b.num,t)};ke.readNumber=function(e){var t=this.pos;if(!e&&this.readInt(10)===null){this.raise(t,"Invalid number")}var r=this.pos-t>=2&&this.input.charCodeAt(t)===48;if(r&&this.strict){this.raise(t,"Invalid number")}if(r&&/[89]/.test(this.input.slice(t,this.pos))){r=false}var i=this.input.charCodeAt(this.pos);if(i===46&&!r){++this.pos;this.readInt(10);i=this.input.charCodeAt(this.pos)}if((i===69||i===101)&&!r){i=this.input.charCodeAt(++this.pos);if(i===43||i===45){++this.pos}if(this.readInt(10)===null){this.raise(t,"Invalid number")}}if(h(this.fullCharCodeAtPos())){this.raise(this.pos,"Identifier directly after number")}var n=this.input.slice(t,this.pos);var a=r?parseInt(n,8):parseFloat(n);return this.finishToken(b.num,a)};ke.readCodePoint=function(){var e=this.input.charCodeAt(this.pos),t;if(e===123){if(this.options.ecmaVersion<6){this.unexpected()}var r=++this.pos;t=this.readHexChar(this.input.indexOf("}",this.pos)-this.pos);++this.pos;if(t>1114111){this.invalidStringToken(r,"Code point out of bounds")}}else{t=this.readHexChar(4)}return t};function Ce(e){if(e<=65535){return String.fromCharCode(e)}e-=65536;return String.fromCharCode((e>>10)+55296,(e&1023)+56320)}ke.readString=function(e){var t=this;var r="",i=++this.pos;for(;;){if(t.pos>=t.input.length){t.raise(t.start,"Unterminated string constant")}var n=t.input.charCodeAt(t.pos);if(n===e){break}if(n===92){r+=t.input.slice(i,t.pos);r+=t.readEscapedChar(false);i=t.pos}else{if(C(n,t.options.ecmaVersion>=10)){t.raise(t.start,"Unterminated string constant")}++t.pos}}r+=this.input.slice(i,this.pos++);return this.finishToken(b.string,r)};var Se={};ke.tryReadTemplateToken=function(){this.inTemplateElement=true;try{this.readTmplToken()}catch(e){if(e===Se){this.readInvalidTemplateToken()}else{throw e}}this.inTemplateElement=false};ke.invalidStringToken=function(e,t){if(this.inTemplateElement&&this.options.ecmaVersion>=9){throw Se}else{this.raise(e,t)}};ke.readTmplToken=function(){var e=this;var t="",r=this.pos;for(;;){if(e.pos>=e.input.length){e.raise(e.start,"Unterminated template")}var i=e.input.charCodeAt(e.pos);if(i===96||i===36&&e.input.charCodeAt(e.pos+1)===123){if(e.pos===e.start&&(e.type===b.template||e.type===b.invalidTemplate)){if(i===36){e.pos+=2;return e.finishToken(b.dollarBraceL)}else{++e.pos;return e.finishToken(b.backQuote)}}t+=e.input.slice(r,e.pos);return e.finishToken(b.template,t)}if(i===92){t+=e.input.slice(r,e.pos);t+=e.readEscapedChar(true);r=e.pos}else if(C(i)){t+=e.input.slice(r,e.pos);++e.pos;switch(i){case 13:if(e.input.charCodeAt(e.pos)===10){++e.pos}case 10:t+="\n";break;default:t+=String.fromCharCode(i);break}if(e.options.locations){++e.curLine;e.lineStart=e.pos}r=e.pos}else{++e.pos}}};ke.readInvalidTemplateToken=function(){var e=this;for(;this.pos=48&&t<=55){var r=this.input.substr(this.pos-1,3).match(/^[0-7]+/)[0];var i=parseInt(r,8);if(i>255){r=r.slice(0,-1);i=parseInt(r,8)}this.pos+=r.length-1;t=this.input.charCodeAt(this.pos);if((r!=="0"||t===56||t===57)&&(this.strict||e)){this.invalidStringToken(this.pos-1-r.length,e?"Octal literal in template string":"Octal literal in strict mode")}return String.fromCharCode(i)}return String.fromCharCode(t)}};ke.readHexChar=function(e){var t=this.pos;var r=this.readInt(16,e);if(r===null){this.invalidStringToken(t,"Bad character escape sequence")}return r};ke.readWord1=function(){var e=this;this.containsEsc=false;var t="",r=true,i=this.pos;var n=this.options.ecmaVersion>=6;while(this.pos=r)){s[u](n,o,e)}if((t==null||n.start===t)&&(r==null||n.end===r)&&i(u,n)){throw new a(n,o)}})(e,o)}catch(e){if(e instanceof a){return e}throw e}}function u(e,t,r,i,s){r=n(r);if(!i){i=v}try{(function e(n,s,o){var l=o||n.type;if(n.start>t||n.end=t&&r(l,n)){throw new a(n,s)}i[l](n,s,e)})(e,s)}catch(e){if(e instanceof a){return e}throw e}}function f(e,t,r,i,s){r=n(r);if(!i){i=v}var o;(function e(n,s,l){if(n.start>t){return}var u=l||n.type;if(n.end<=t&&(!o||o.node.end 0.1 || correlation < -0.1) {\n console.log(event + \":\", correlation);\n }\n}\n// → brushed teeth: -0.3805211953\n// → work: -0.1371988681\n// → reading: 0.1106828054\n", + "exercises": [ + { + "name": "The sum of a range", + "file": "code/solutions/04_1_the_sum_of_a_range.js", + "number": 1, + "type": "js", + "code": "// Your code here.\n\nconsole.log(range(1, 10));\n// → [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]\nconsole.log(range(5, 2, -1));\n// → [5, 4, 3, 2]\nconsole.log(sum(range(1, 10)));\n// → 55", + "solution": "function range(start, end, step = start < end ? 1 : -1) {\n let array = [];\n\n if (step > 0) {\n for (let i = start; i <= end; i += step) array.push(i);\n } else {\n for (let i = start; i >= end; i += step) array.push(i);\n }\n return array;\n}\n\nfunction sum(array) {\n let total = 0;\n for (let value of array) {\n total += value;\n }\n return total;\n}\n\nconsole.log(range(1, 10))\n// → [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]\nconsole.log(range(5, 2, -1));\n// → [5, 4, 3, 2]\nconsole.log(sum(range(1, 10)));\n// → 55" + }, + { + "name": "Reversing an array", + "file": "code/solutions/04_2_reversing_an_array.js", + "number": 2, + "type": "js", + "code": "// Your code here.\n\nconsole.log(reverseArray([\"A\", \"B\", \"C\"]));\n// → [\"C\", \"B\", \"A\"];\nlet arrayValue = [1, 2, 3, 4, 5];\nreverseArrayInPlace(arrayValue);\nconsole.log(arrayValue);\n// → [5, 4, 3, 2, 1]", + "solution": "function reverseArray(array) {\n let output = [];\n for (let i = array.length - 1; i >= 0; i--) {\n output.push(array[i]);\n }\n return output;\n}\n\nfunction reverseArrayInPlace(array) {\n for (let i = 0; i < Math.floor(array.length / 2); i++) {\n let old = array[i];\n array[i] = array[array.length - 1 - i];\n array[array.length - 1 - i] = old;\n }\n return array;\n}\n\nconsole.log(reverseArray([\"A\", \"B\", \"C\"]));\n// → [\"C\", \"B\", \"A\"];\nlet arrayValue = [1, 2, 3, 4, 5];\nreverseArrayInPlace(arrayValue);\nconsole.log(arrayValue);\n// → [5, 4, 3, 2, 1]" + }, + { + "name": "A list", + "file": "code/solutions/04_3_a_list.js", + "number": 3, + "type": "js", + "code": "// Your code here.\n\nconsole.log(arrayToList([10, 20]));\n// → {value: 10, rest: {value: 20, rest: null}}\nconsole.log(listToArray(arrayToList([10, 20, 30])));\n// → [10, 20, 30]\nconsole.log(prepend(10, prepend(20, null)));\n// → {value: 10, rest: {value: 20, rest: null}}\nconsole.log(nth(arrayToList([10, 20, 30]), 1));\n// → 20", + "solution": "function arrayToList(array) {\n let list = null;\n for (let i = array.length - 1; i >= 0; i--) {\n list = {value: array[i], rest: list};\n }\n return list;\n}\n\nfunction listToArray(list) {\n let array = [];\n for (let node = list; node; node = node.rest) {\n array.push(node.value);\n }\n return array;\n}\n\nfunction prepend(value, list) {\n return {value, rest: list};\n}\n\nfunction nth(list, n) {\n if (!list) return undefined;\n else if (n == 0) return list.value;\n else return nth(list.rest, n - 1);\n}\n\nconsole.log(arrayToList([10, 20]));\n// → {value: 10, rest: {value: 20, rest: null}}\nconsole.log(listToArray(arrayToList([10, 20, 30])));\n// → [10, 20, 30]\nconsole.log(prepend(10, prepend(20, null)));\n// → {value: 10, rest: {value: 20, rest: null}}\nconsole.log(nth(arrayToList([10, 20, 30]), 1));\n// → 20" + }, + { + "name": "Deep comparison", + "file": "code/solutions/04_4_deep_comparison.js", + "number": 4, + "type": "js", + "code": "// Your code here.\n\nlet obj = {here: {is: \"an\"}, object: 2};\nconsole.log(deepEqual(obj, obj));\n// → true\nconsole.log(deepEqual(obj, {here: 1, object: 2}));\n// → false\nconsole.log(deepEqual(obj, {here: {is: \"an\"}, object: 2}));\n// → true", + "solution": "function deepEqual(a, b) {\n if (a === b) return true;\n \n if (a == null || typeof a != \"object\" ||\n b == null || typeof b != \"object\") return false;\n\n let keysA = Object.keys(a), keysB = Object.keys(b);\n\n if (keysA.length != keysB.length) return false;\n\n for (let key of keysA) {\n if (!keysB.includes(key) || !deepEqual(a[key], b[key])) return false;\n }\n\n return true;\n}\n\nlet obj = {here: {is: \"an\"}, object: 2};\nconsole.log(deepEqual(obj, obj));\n// → true\nconsole.log(deepEqual(obj, {here: 1, object: 2}));\n// → false\nconsole.log(deepEqual(obj, {here: {is: \"an\"}, object: 2}));\n// → true" + } + ], + "include": [ + "code/journal.js", + "code/chapter/04_data.js" + ] + }, + { + "number": 5, + "id": "05_higher_order", + "title": "Higher-Order Functions", + "start_code": "function textScripts(text) {\n let scripts = countBy(text, char => {\n let script = characterScript(char.codePointAt(0));\n return script ? script.name : \"none\";\n }).filter(({name}) => name != \"none\");\n\n let total = scripts.reduce((n, {count}) => n + count, 0);\n if (total == 0) return \"No scripts found\";\n\n return scripts.map(({name, count}) => {\n return `${Math.round(count * 100 / total)}% ${name}`;\n }).join(\", \");\n}\n\nconsole.log(textScripts('英国的狗说\"woof\", 俄罗斯的狗说\"тяв\"'));\n", + "exercises": [ + { + "name": "Flattening", + "file": "code/solutions/05_1_flattening.js", + "number": 1, + "type": "js", + "code": "let arrays = [[1, 2, 3], [4, 5], [6]];\n// Your code here.\n// → [1, 2, 3, 4, 5, 6]", + "solution": "let arrays = [[1, 2, 3], [4, 5], [6]];\n\nconsole.log(arrays.reduce((flat, current) => flat.concat(current), []));\n// → [1, 2, 3, 4, 5, 6]" + }, + { + "name": "Your own loop", + "file": "code/solutions/05_2_your_own_loop.js", + "number": 2, + "type": "js", + "code": "// Your code here.\n\nloop(3, n => n > 0, n => n - 1, console.log);\n// → 3\n// → 2\n// → 1", + "solution": "function loop(start, test, update, body) {\n for (let value = start; test(value); value = update(value)) {\n body(value);\n }\n}\n\nloop(3, n => n > 0, n => n - 1, console.log);\n// → 3\n// → 2\n// → 1" + }, + { + "name": "Everything", + "file": "code/solutions/05_3_everything.js", + "number": 3, + "type": "js", + "code": "function every(array, test) {\n // Your code here.\n}\n\nconsole.log(every([1, 3, 5], n => n < 10));\n// → true\nconsole.log(every([2, 4, 16], n => n < 10));\n// → false\nconsole.log(every([], n => n < 10));\n// → true", + "solution": "function every(array, predicate) {\n for (let element of array) {\n if (!predicate(element)) return false;\n }\n return true;\n}\n\nfunction every2(array, predicate) {\n return !array.some(element => !predicate(element));\n}\n\nconsole.log(every([1, 3, 5], n => n < 10));\n// → true\nconsole.log(every([2, 4, 16], n => n < 10));\n// → false\nconsole.log(every([], n => n < 10));\n// → true" + }, + { + "name": "Dominant writing direction", + "file": "code/solutions/05_4_dominant_writing_direction.js", + "number": 4, + "type": "js", + "code": "function dominantDirection(text) {\n // Your code here.\n}\n\nconsole.log(dominantDirection(\"Hello!\"));\n// → ltr\nconsole.log(dominantDirection(\"Hey, مساء الخير\"));\n// → rtl", + "solution": "function dominantDirection(text) {\n let counted = countBy(text, char => {\n let script = characterScript(char.codePointAt(0));\n return script ? script.direction : \"none\";\n }).filter(({name}) => name != \"none\");\n\n if (counted.length == 0) return \"ltr\";\n\n return counted.reduce((a, b) => a.count > b.count ? a : b).name;\n}\n\nconsole.log(dominantDirection(\"Hello!\"));\n// → ltr\nconsole.log(dominantDirection(\"Hey, مساء الخير\"));\n// → rtl" + } + ], + "include": [ + "code/scripts.js", + "code/chapter/05_higher_order.js", + "code/intro.js" + ] + }, + { + "number": 6, + "id": "06_object", + "title": "The Secret Life of Objects", + "start_code": "class Temperature {\n constructor(celsius) {\n this.celsius = celsius;\n }\n get fahrenheit() {\n return this.celsius * 1.8 + 32;\n }\n set fahrenheit(value) {\n this.celsius = (value - 32) / 1.8;\n }\n\n static fromFahrenheit(value) {\n return new Temperature((value - 32) / 1.8);\n }\n}\n\nlet temp = new Temperature(22);\nconsole.log(temp.fahrenheit);\ntemp.fahrenheit = 86;\nconsole.log(temp.celsius);\n", + "exercises": [ + { + "name": "A vector type", + "file": "code/solutions/06_1_a_vector_type.js", + "number": 1, + "type": "js", + "code": "// Your code here.\n\nconsole.log(new Vec(1, 2).plus(new Vec(2, 3)));\n// → Vec{x: 3, y: 5}\nconsole.log(new Vec(1, 2).minus(new Vec(2, 3)));\n// → Vec{x: -1, y: -1}\nconsole.log(new Vec(3, 4).length);\n// → 5", + "solution": "class Vec {\n constructor(x, y) {\n this.x = x;\n this.y = y;\n }\n\n plus(other) {\n return new Vec(this.x + other.x, this.y + other.y);\n }\n\n minus(other) {\n return new Vec(this.x - other.x, this.y - other.y);\n }\n\n get length() {\n return Math.sqrt(this.x * this.x + this.y * this.y);\n }\n}\n\nconsole.log(new Vec(1, 2).plus(new Vec(2, 3)));\n// → Vec{x: 3, y: 5}\nconsole.log(new Vec(1, 2).minus(new Vec(2, 3)));\n// → Vec{x: -1, y: -1}\nconsole.log(new Vec(3, 4).length);\n// → 5" + }, + { + "name": "Groups", + "file": "code/solutions/06_2_groups.js", + "number": 2, + "type": "js", + "code": "class Group {\n // Your code here.\n}\n\nlet group = Group.from([10, 20]);\nconsole.log(group.has(10));\n// → true\nconsole.log(group.has(30));\n// → false\ngroup.add(10);\ngroup.delete(10);\nconsole.log(group.has(10));\n// → false", + "solution": "class Group {\n constructor() {\n this.members = [];\n }\n\n add(value) {\n if (!this.has(value)) {\n this.members.push(value);\n }\n }\n\n delete(value) {\n this.members = this.members.filter(v => v !== value);\n }\n\n has(value) {\n return this.members.includes(value);\n }\n\n static from(collection) {\n let group = new Group;\n for (let value of collection) {\n group.add(value);\n }\n return group;\n }\n}\n\nlet group = Group.from([10, 20]);\nconsole.log(group.has(10));\n// → true\nconsole.log(group.has(30));\n// → false\ngroup.add(10);\ngroup.delete(10);\nconsole.log(group.has(10));" + }, + { + "name": "Iterable groups", + "file": "code/solutions/06_3_iterable_groups.js", + "number": 3, + "type": "js", + "code": "// Your code here (and the code from the previous exercise)\n\nfor (let value of Group.from([\"a\", \"b\", \"c\"])) {\n console.log(value);\n}\n// → a\n// → b\n// → c", + "solution": "class Group {\n constructor() {\n this.members = [];\n }\n\n add(value) {\n if (!this.has(value)) {\n this.members.push(value);\n }\n }\n\n delete(value) {\n this.members = this.members.filter(v => v !== value);\n }\n\n has(value) {\n return this.members.includes(value);\n }\n\n static from(collection) {\n let group = new Group;\n for (let value of collection) {\n group.add(value);\n }\n return group;\n }\n\n [Symbol.iterator]() {\n return new GroupIterator(this);\n }\n}\n\nclass GroupIterator {\n constructor(group) {\n this.group = group;\n this.position = 0;\n }\n\n next() {\n if (this.position >= this.group.members.length) {\n return {done: true};\n } else {\n let result = {value: this.group.members[this.position],\n done: false};\n this.position++;\n return result;\n }\n }\n}\n\nfor (let value of Group.from([\"a\", \"b\", \"c\"])) {\n console.log(value);\n}\n// → a\n// → b\n// → c" + }, + { + "name": "Borrowing a method", + "file": "code/solutions/06_4_borrowing_a_method.js", + "number": 4, + "type": "js", + "code": "let map = {one: true, two: true, hasOwnProperty: true};\n\n// Fix this call\nconsole.log(map.hasOwnProperty(\"one\"));\n// → true", + "solution": "let map = {one: true, two: true, hasOwnProperty: true};\n\nconsole.log(Object.prototype.hasOwnProperty.call(map, \"one\"));\n// → true" + } + ], + "include": [ + "code/chapter/06_object.js" + ] + }, + { + "number": 7, + "id": "07_robot", + "title": "Project: A Robot", + "start_code": "runRobotAnimation(VillageState.random(),\n goalOrientedRobot, []);\n", + "exercises": [ + { + "name": "Measuring a robot", + "file": "code/solutions/07_1_measuring_a_robot.js", + "number": 1, + "type": "js", + "code": "function compareRobots(robot1, memory1, robot2, memory2) {\n // Your code here\n}\n\ncompareRobots(routeRobot, [], goalOrientedRobot, []);", + "solution": "function countSteps(state, robot, memory) {\n for (let steps = 0;; steps++) {\n if (state.parcels.length == 0) return steps;\n let action = robot(state, memory);\n state = state.move(action.direction);\n memory = action.memory;\n }\n}\n\nfunction compareRobots(robot1, memory1, robot2, memory2) {\n let total1 = 0, total2 = 0;\n for (let i = 0; i < 100; i++) {\n let state = VillageState.random();\n total1 += countSteps(state, robot1, memory1);\n total2 += countSteps(state, robot2, memory2);\n }\n console.log(`Robot 1 needed ${total1 / 100} steps per task`)\n console.log(`Robot 2 needed ${total2 / 100}`)\n}\n\ncompareRobots(routeRobot, [], goalOrientedRobot, []);" + }, + { + "name": "Robot efficiency", + "file": "code/solutions/07_2_robot_efficiency.js", + "number": 2, + "type": "js", + "code": "// Your code here\n\nrunRobotAnimation(VillageState.random(), yourRobot, memory);", + "solution": "function lazyRobot({place, parcels}, route) {\n if (route.length == 0) {\n // Describe a route for every parcel\n let routes = parcels.map(parcel => {\n if (parcel.place != place) {\n return {route: findRoute(roadGraph, place, parcel.place),\n pickUp: true};\n } else {\n return {route: findRoute(roadGraph, place, parcel.address),\n pickUp: false};\n }\n });\n\n // This determines the precedence a route gets when choosing.\n // Route length counts negatively, routes that pick up a package\n // get a small bonus.\n function score({route, pickUp}) {\n return (pickUp ? 0.5 : 0) - route.length;\n }\n route = routes.reduce((a, b) => score(a) > score(b) ? a : b).route;\n }\n\n return {direction: route[0], memory: route.slice(1)};\n}\n\nrunRobotAnimation(VillageState.random(), lazyRobot, []);" + }, + { + "name": "Persistent group", + "file": "code/solutions/07_3_persistent_group.js", + "number": 3, + "type": "js", + "code": "class PGroup {\n // Your code here\n}\n\nlet a = PGroup.empty.add(\"a\");\nlet ab = a.add(\"b\");\nlet b = ab.delete(\"a\");\n\nconsole.log(b.has(\"b\"));\n// → true\nconsole.log(a.has(\"b\"));\n// → false\nconsole.log(b.has(\"a\"));\n// → false", + "solution": "class PGroup {\n constructor(members) {\n this.members = members;\n }\n\n add(value) {\n if (this.has(value)) return this;\n return new PGroup(this.members.concat([value]));\n }\n\n delete(value) {\n if (!this.has(value)) return this;\n return new PGroup(this.members.filter(m => m !== value));\n }\n\n has(value) {\n return this.members.includes(value);\n }\n}\n\nPGroup.empty = new PGroup([]);\n\nlet a = PGroup.empty.add(\"a\");\nlet ab = a.add(\"b\");\nlet b = ab.delete(\"a\");\n\nconsole.log(b.has(\"b\"));\n// → true\nconsole.log(a.has(\"b\"));\n// → false\nconsole.log(b.has(\"a\"));\n// → false" + } + ], + "include": [ + "code/chapter/07_robot.js", + "code/animatevillage.js" + ] + }, + { + "number": 8, + "id": "08_error", + "title": "Bugs and Errors", + "start_code": "", + "exercises": [ + { + "name": "Retry", + "file": "code/solutions/08_1_retry.js", + "number": 1, + "type": "js", + "code": "class MultiplicatorUnitFailure extends Error {}\n\nfunction primitiveMultiply(a, b) {\n if (Math.random() < 0.2) {\n return a * b;\n } else {\n throw new MultiplicatorUnitFailure(\"Klunk\");\n }\n}\n\nfunction reliableMultiply(a, b) {\n // Your code here.\n}\n\nconsole.log(reliableMultiply(8, 8));\n// → 64", + "solution": "class MultiplicatorUnitFailure extends Error {}\n\nfunction primitiveMultiply(a, b) {\n if (Math.random() < 0.2) {\n return a * b;\n } else {\n throw new MultiplicatorUnitFailure(\"Klunk\");\n }\n}\n\nfunction reliableMultiply(a, b) {\n for (;;) {\n try {\n return primitiveMultiply(a, b);\n } catch (e) {\n if (!(e instanceof MultiplicatorUnitFailure))\n throw e;\n }\n }\n}\n\nconsole.log(reliableMultiply(8, 8));\n// → 64" + }, + { + "name": "The locked box", + "file": "code/solutions/08_2_the_locked_box.js", + "number": 2, + "type": "js", + "code": "const box = {\n locked: true,\n unlock() { this.locked = false; },\n lock() { this.locked = true; },\n _content: [],\n get content() {\n if (this.locked) throw new Error(\"Locked!\");\n return this._content;\n }\n};\n\nfunction withBoxUnlocked(body) {\n // Your code here.\n}\n\nwithBoxUnlocked(function() {\n box.content.push(\"gold piece\");\n});\n\ntry {\n withBoxUnlocked(function() {\n throw new Error(\"Pirates on the horizon! Abort!\");\n });\n} catch (e) {\n console.log(\"Error raised: \" + e);\n}\nconsole.log(box.locked);\n// → true", + "solution": "const box = {\n locked: true,\n unlock() { this.locked = false; },\n lock() { this.locked = true; },\n _content: [],\n get content() {\n if (this.locked) throw new Error(\"Locked!\");\n return this._content;\n }\n};\n\nfunction withBoxUnlocked(body) {\n let locked = box.locked;\n if (!locked) {\n return body();\n }\n\n box.unlock();\n try {\n return body();\n } finally {\n box.lock();\n }\n}\n\nwithBoxUnlocked(function() {\n box.content.push(\"gold piece\");\n});\n\ntry {\n withBoxUnlocked(function() {\n throw new Error(\"Pirates on the horizon! Abort!\");\n });\n} catch (e) {\n console.log(\"Error raised:\", e);\n}\n\nconsole.log(box.locked);\n// → true" + } + ], + "include": [ + "code/chapter/08_error.js" + ] + }, + { + "number": 9, + "id": "09_regexp", + "title": "Regular Expressions", + "start_code": "function parseINI(string) {\n // Start with an object to hold the top-level fields\n let result = {};\n let section = result;\n string.split(/\\r?\\n/).forEach(line => {\n let match;\n if (match = line.match(/^(\\w+)=(.*)$/)) {\n section[match[1]] = match[2];\n } else if (match = line.match(/^\\[(.*)\\]$/)) {\n section = result[match[1]] = {};\n } else if (!/^\\s*(;.*)?$/.test(line)) {\n throw new Error(\"Line '\" + line + \"' is not valid.\");\n }\n });\n return result;\n}\n\nconsole.log(parseINI(`\nname=Vasilis\n[address]\ncity=Tessaloniki`));\n", + "exercises": [ + { + "name": "Regexp golf", + "file": "code/solutions/09_1_regexp_golf.js", + "number": 1, + "type": "js", + "code": "// Fill in the regular expressions\n\nverify(/.../,\n [\"my car\", \"bad cats\"],\n [\"camper\", \"high art\"]);\n\nverify(/.../,\n [\"pop culture\", \"mad props\"],\n [\"plop\", \"prrrop\"]);\n\nverify(/.../,\n [\"ferret\", \"ferry\", \"ferrari\"],\n [\"ferrum\", \"transfer A\"]);\n\nverify(/.../,\n [\"how delicious\", \"spacious room\"],\n [\"ruinous\", \"consciousness\"]);\n\nverify(/.../,\n [\"bad punctuation .\"],\n [\"escape the period\"]);\n\nverify(/.../,\n [\"hottentottententen\"],\n [\"no\", \"hotten totten tenten\"]);\n\nverify(/.../,\n [\"red platypus\", \"wobbling nest\"],\n [\"earth bed\", \"learning ape\", \"BEET\"]);\n\n\nfunction verify(regexp, yes, no) {\n // Ignore unfinished exercises\n if (regexp.source == \"...\") return;\n for (let str of yes) if (!regexp.test(str)) {\n console.log(`Failure to match '${str}'`);\n }\n for (let str of no) if (regexp.test(str)) {\n console.log(`Unexpected match for '${str}'`);\n }\n}", + "solution": "// Fill in the regular expressions\n\nverify(/ca[rt]/,\n [\"my car\", \"bad cats\"],\n [\"camper\", \"high art\"]);\n\nverify(/pr?op/,\n [\"pop culture\", \"mad props\"],\n [\"plop\", \"prrrop\"]);\n\nverify(/ferr(et|y|ari)/,\n [\"ferret\", \"ferry\", \"ferrari\"],\n [\"ferrum\", \"transfer A\"]);\n\nverify(/ious\\b/,\n [\"how delicious\", \"spacious room\"],\n [\"ruinous\", \"consciousness\"]);\n\nverify(/\\s[.,:;]/,\n [\"bad punctuation .\"],\n [\"escape the dot\"]);\n\nverify(/\\w{7}/,\n [\"hottentottententen\"],\n [\"no\", \"hotten totten tenten\"]);\n\nverify(/\\b[^\\We]+\\b/i,\n [\"red platypus\", \"wobbling nest\"],\n [\"earth bed\", \"learning ape\", \"BEET\"]);\n\n\nfunction verify(regexp, yes, no) {\n // Ignore unfinished exercises\n if (regexp.source == \"...\") return;\n for (let str of yes) if (!regexp.test(str)) {\n console.log(`Failure to match '${str}'`);\n }\n for (let str of no) if (regexp.test(str)) {\n console.log(`Unexpected match for '${str}'`);\n }\n}" + }, + { + "name": "Quoting style", + "file": "code/solutions/09_2_quoting_style.js", + "number": 2, + "type": "js", + "code": "let text = \"'I'm the cook,' he said, 'it's my job.'\";\n// Change this call.\nconsole.log(text.replace(/A/g, \"B\"));\n// → \"I'm the cook,\" he said, \"it's my job.\"", + "solution": "let text = \"'I'm the cook,' he said, 'it's my job.'\";\n\nconsole.log(text.replace(/(^|\\W)'|'(\\W|$)/g, '$1\"$2'));\n// → \"I'm the cook,\" he said, \"it's my job.\"" + }, + { + "name": "Numbers again", + "file": "code/solutions/09_3_numbers_again.js", + "number": 3, + "type": "js", + "code": "// Fill in this regular expression.\nlet number = /^...$/;\n\n// Tests:\nfor (let str of [\"1\", \"-1\", \"+15\", \"1.55\", \".5\", \"5.\",\n \"1.3e2\", \"1E-4\", \"1e+12\"]) {\n if (!number.test(str)) {\n console.log(`Failed to match '${str}'`);\n }\n}\nfor (let str of [\"1a\", \"+-1\", \"1.2.3\", \"1+1\", \"1e4.5\",\n \".5.\", \"1f5\", \".\"]) {\n if (number.test(str)) {\n console.log(`Incorrectly accepted '${str}'`);\n }\n}", + "solution": "// Fill in this regular expression.\nlet number = /^[+\\-]?(\\d+(\\.\\d*)?|\\.\\d+)([eE][+\\-]?\\d+)?$/;\n\n// Tests:\nfor (let str of [\"1\", \"-1\", \"+15\", \"1.55\", \".5\", \"5.\",\n \"1.3e2\", \"1E-4\", \"1e+12\"]) {\n if (!number.test(str)) {\n console.log(`Failed to match '${str}'`);\n }\n}\nfor (let str of [\"1a\", \"+-1\", \"1.2.3\", \"1+1\", \"1e4.5\",\n \".5.\", \"1f5\", \".\"]) {\n if (number.test(str)) {\n console.log(`Incorrectly accepted '${str}'`);\n }\n}" + } + ], + "include": null + }, + { + "number": 10, + "id": "10_modules", + "title": "Modules", + "start_code": "", + "exercises": [ + { + "name": "Roads module", + "file": "code/solutions/10_2_roads_module.js", + "number": 2, + "type": "js", + "code": "// Add dependencies and exports\n\nconst roads = [\n \"Alice's House-Bob's House\", \"Alice's House-Cabin\",\n \"Alice's House-Post Office\", \"Bob's House-Town Hall\",\n \"Daria's House-Ernie's House\", \"Daria's House-Town Hall\",\n \"Ernie's House-Grete's House\", \"Grete's House-Farm\",\n \"Grete's House-Shop\", \"Marketplace-Farm\",\n \"Marketplace-Post Office\", \"Marketplace-Shop\",\n \"Marketplace-Town Hall\", \"Shop-Town Hall\"\n];", + "solution": "const {buildGraph} = require(\"./graph\");\n\nconst roads = [\n \"Alice's House-Bob's House\", \"Alice's House-Cabin\",\n \"Alice's House-Post Office\", \"Bob's House-Town Hall\",\n \"Daria's House-Ernie's House\", \"Daria's House-Town Hall\",\n \"Ernie's House-Grete's House\", \"Grete's House-Farm\",\n \"Grete's House-Shop\", \"Marketplace-Farm\",\n \"Marketplace-Post Office\", \"Marketplace-Shop\",\n \"Marketplace-Town Hall\", \"Shop-Town Hall\"\n];\n\nexports.roadGraph = buildGraph(roads.map(r => r.split(\"-\")));" + } + ], + "include": [ + "code/packages_chapter_10.js", + "code/chapter/07_robot.js" + ] + }, + { + "number": 11, + "id": "11_async", + "title": "Asynchronous Programming", + "start_code": "findInStorage(bigOak, \"events on 2017-12-21\")\n .then(console.log);\n", + "exercises": [ + { + "name": "Tracking the scalpel", + "file": "code/solutions/11_1_tracking_the_scalpel.js", + "number": 1, + "type": "js", + "code": "async function locateScalpel(nest) {\n // Your code here.\n}\n\nfunction locateScalpel2(nest) {\n // Your code here.\n}\n\nlocateScalpel(bigOak).then(console.log);\n// → Butcher Shop", + "solution": "async function locateScalpel(nest) {\n let current = nest.name;\n for (;;) {\n let next = await anyStorage(nest, current, \"scalpel\");\n if (next == current) return current;\n current = next;\n }\n}\n\nfunction locateScalpel2(nest) {\n function loop(current) {\n return anyStorage(nest, current, \"scalpel\").then(next => {\n if (next == current) return current;\n else return loop(next);\n });\n }\n return loop(nest.name);\n}\n\nlocateScalpel(bigOak).then(console.log);\n// → Butcher's Shop\nlocateScalpel2(bigOak).then(console.log);\n// → Butcher's Shop" + }, + { + "name": "Building Promise.all", + "file": "code/solutions/11_2_building_promiseall.js", + "number": 2, + "type": "js", + "code": "function Promise_all(promises) {\n return new Promise((resolve, reject) => {\n // Your code here.\n });\n}\n\n// Test code.\nPromise_all([]).then(array => {\n console.log(\"This should be []:\", array);\n});\nfunction soon(val) {\n return new Promise(resolve => {\n setTimeout(() => resolve(val), Math.random() * 500);\n });\n}\nPromise_all([soon(1), soon(2), soon(3)]).then(array => {\n console.log(\"This should be [1, 2, 3]:\", array);\n});\nPromise_all([soon(1), Promise.reject(\"X\"), soon(3)])\n .then(array => {\n console.log(\"We should not get here\");\n })\n .catch(error => {\n if (error != \"X\") {\n console.log(\"Unexpected failure:\", error);\n }\n });", + "solution": "function Promise_all(promises) {\n return new Promise((resolve, reject) => {\n let results = [];\n let pending = promises.length;\n for (let i = 0; i < promises.length; i++) {\n promises[i].then(result => {\n results[i] = result;\n pending--;\n if (pending == 0) resolve(results);\n }).catch(reject);\n }\n if (promises.length == 0) resolve(results);\n });\n}\n\n// Test code.\nPromise_all([]).then(array => {\n console.log(\"This should be []:\", array);\n});\nfunction soon(val) {\n return new Promise(resolve => {\n setTimeout(() => resolve(val), Math.random() * 500);\n });\n}\nPromise_all([soon(1), soon(2), soon(3)]).then(array => {\n console.log(\"This should be [1, 2, 3]:\", array);\n});\nPromise_all([soon(1), Promise.reject(\"X\"), soon(3)]).then(array => {\n console.log(\"We should not get here\");\n}).catch(error => {\n if (error != \"X\") {\n console.log(\"Unexpected failure:\", error);\n }\n});" + } + ], + "include": [ + "code/crow-tech.js", + "code/chapter/11_async.js" + ] + }, + { + "number": 12, + "id": "12_language", + "title": "Project: A Programming Language", + "start_code": "run(`\ndo(define(plusOne, fun(a, +(a, 1))),\n print(plusOne(10)))\n`);\n\nrun(`\ndo(define(pow, fun(base, exp,\n if(==(exp, 0),\n 1,\n *(base, pow(base, -(exp, 1)))))),\n print(pow(2, 10)))\n`);\n", + "exercises": [ + { + "name": "Arrays", + "file": "code/solutions/12_1_arrays.js", + "number": 1, + "type": "js", + "code": "// Modify these definitions...\n\ntopScope.array = \"...\";\n\ntopScope.length = \"...\";\n\ntopScope.element = \"...\";\n\nrun(`\ndo(define(sum, fun(array,\n do(define(i, 0),\n define(sum, 0),\n while(<(i, length(array)),\n do(define(sum, +(sum, element(array, i))),\n define(i, +(i, 1)))),\n sum))),\n print(sum(array(1, 2, 3))))\n`);\n// → 6", + "solution": "topScope.array = (...values) => values;\n\ntopScope.length = array => array.length;\n\ntopScope.element = (array, i) => array[i];\n\nrun(`\ndo(define(sum, fun(array,\n do(define(i, 0),\n define(sum, 0),\n while(<(i, length(array)),\n do(define(sum, +(sum, element(array, i))),\n define(i, +(i, 1)))),\n sum))),\n print(sum(array(1, 2, 3))))\n`);\n// → 6" + }, + { + "name": "Comments", + "file": "code/solutions/12_3_comments.js", + "number": 3, + "type": "js", + "code": "// This is the old skipSpace. Modify it...\nfunction skipSpace(string) {\n let first = string.search(/\\S/);\n if (first == -1) return \"\";\n return string.slice(first);\n}\n\nconsole.log(parse(\"# hello\\nx\"));\n// → {type: \"word\", name: \"x\"}\n\nconsole.log(parse(\"a # one\\n # two\\n()\"));\n// → {type: \"apply\",\n// operator: {type: \"word\", name: \"a\"},\n// args: []}", + "solution": "function skipSpace(string) {\n let skippable = string.match(/^(\\s|#.*)*/);\n return string.slice(skippable[0].length);\n}\n\nconsole.log(parse(\"# hello\\nx\"));\n// → {type: \"word\", name: \"x\"}\n\nconsole.log(parse(\"a # one\\n # two\\n()\"));\n// → {type: \"apply\",\n// operator: {type: \"word\", name: \"a\"},\n// args: []}" + }, + { + "name": "Fixing scope", + "file": "code/solutions/12_4_fixing_scope.js", + "number": 4, + "type": "js", + "code": "specialForms.set = (args, scope) => {\n // Your code here.\n};\n\nrun(`\ndo(define(x, 4),\n define(setx, fun(val, set(x, val))),\n setx(50),\n print(x))\n`);\n// → 50\nrun(`set(quux, true)`);\n// → Some kind of ReferenceError", + "solution": "specialForms.set = (args, env) => {\n if (args.length != 2 || args[0].type != \"word\") {\n throw new SyntaxError(\"Bad use of set\");\n }\n let varName = args[0].name;\n let value = evaluate(args[1], env);\n\n for (let scope = env; scope; scope = Object.getPrototypeOf(scope)) {\n if (Object.prototype.hasOwnProperty.call(scope, varName)) {\n scope[varName] = value;\n return value;\n }\n }\n throw new ReferenceError(`Setting undefined variable ${varName}`);\n};\n\nrun(`\ndo(define(x, 4),\n define(setx, fun(val, set(x, val))),\n setx(50),\n print(x))\n`);\n// → 50\nrun(`set(quux, true)`);\n// → Some kind of ReferenceError" + } + ], + "include": [ + "code/chapter/12_language.js" + ] + }, + { + "number": 13, + "id": "13_browser", + "title": "JavaScript and the Browser", + "start_code": "", + "exercises": [], + "include": null + }, + { + "number": 14, + "id": "14_dom", + "title": "The Document Object Model", + "start_code": "\n\n

      \n \n

      \n\n", + "exercises": [ + { + "name": "Build a table", + "file": "code/solutions/14_1_build_a_table.html", + "number": 1, + "type": "html", + "code": "\n\n

      Mountains

      \n\n
      \n\n", + "solution": "\n\n\n\n

      Mountains

      \n\n
      \n\n" + }, + { + "name": "Elements by tag name", + "file": "code/solutions/14_2_elements_by_tag_name.html", + "number": 2, + "type": "html", + "code": "\n\n

      Heading with a span element.

      \n

      A paragraph with one, two\n spans.

      \n\n", + "solution": "\n\n

      Heading with a span element.

      \n

      A paragraph with one, two\n spans.

      \n\n" + }, + { + "name": "The cat's hat", + "file": "code/solutions/14_3_the_cats_hat.html", + "number": 3, + "type": "html", + "code": "\n\n\n\n\n\n", + "solution": "\n\n\n\n\n\n\n\n\n\n" + } + ], + "include": null + }, + { + "number": 15, + "id": "15_event", + "title": "Handling Events", + "start_code": "\n\n

      Drag the bar to change its width:

      \n
      \n
      \n\n", + "exercises": [ + { + "name": "Balloon", + "file": "code/solutions/15_1_balloon.html", + "number": 1, + "type": "html", + "code": "\n\n

      🎈

      \n\n", + "solution": "\n\n

      🎈

      \n\n" + }, + { + "name": "Mouse trail", + "file": "code/solutions/15_2_mouse_trail.html", + "number": 2, + "type": "html", + "code": "\n\n\n\n", + "solution": "\n\n\n\n\n\n" + }, + { + "name": "Tabs", + "file": "code/solutions/15_3_tabs.html", + "number": 3, + "type": "html", + "code": "\n\n\n
      Tab one
      \n
      Tab two
      \n
      Tab three
      \n
      \n", + "solution": "\n\n\n
      Tab one
      \n
      Tab two
      \n
      Tab three
      \n
      \n" + } + ], + "include": null + }, + { + "number": 16, + "id": "16_game", + "title": "Project: A Platform Game", + "start_code": "\n\n\n\n\n\n\n \n\n", + "exercises": [ + { + "name": "Game over", + "file": "code/solutions/16_1_game_over.html", + "number": 1, + "type": "html", + "code": "\n\n\n\n\n\n\n\n", + "solution": "\n\n\n\n\n\n\n\n" + }, + { + "name": "Pausing the game", + "file": "code/solutions/16_2_pausing_the_game.html", + "number": 2, + "type": "html", + "code": "\n\n\n\n\n\n\n\n", + "solution": "\n\n\n\n\n\n\n\n" + }, + { + "name": "A monster", + "file": "code/solutions/16_3_a_monster.html", + "number": 3, + "type": "html", + "code": "\n\n\n\n\n\n\n\n \n", + "solution": "\n\n\n\n\n\n\n\n\n \n" + } + ], + "include": [ + "code/chapter/16_game.js", + "code/levels.js" + ] + }, + { + "number": 17, + "id": "17_canvas", + "title": "Drawing on Canvas", + "start_code": "\n\n\n\n\n\n \n\n", + "exercises": [ + { + "name": "Shapes", + "file": "code/solutions/17_1_shapes.html", + "number": 1, + "type": "html", + "code": "\n\n\n\n\n\n", + "solution": "\n\n\n\n\n\n" + }, + { + "name": "The pie chart", + "file": "code/solutions/17_2_the_pie_chart.html", + "number": 2, + "type": "html", + "code": "\n\n\n\n\n\n", + "solution": "\n\n\n\n\n\n" + }, + { + "name": "A bouncing ball", + "file": "code/solutions/17_3_a_bouncing_ball.html", + "number": 3, + "type": "html", + "code": "\n\n\n\n\n\n", + "solution": "\n\n\n\n\n\n" + } + ], + "include": [ + "code/chapter/16_game.js", + "code/levels.js", + "code/chapter/17_canvas.js" + ] + }, + { + "number": 18, + "id": "18_http", + "title": "HTTP and Forms", + "start_code": "\n\n\nNotes:
      \n\n\n\n", + "exercises": [ + { + "name": "Content negotiation", + "file": "code/solutions/18_1_content_negotiation.js", + "number": 1, + "type": "js", + "code": "// Your code here.", + "solution": "const url = \"https://eloquentjavascript.net/author\";\nconst types = [\"text/plain\",\n \"text/html\",\n \"application/json\",\n \"application/rainbows+unicorns\"];\n\nasync function showTypes() {\n for (let type of types) {\n let resp = await fetch(url, {headers: {accept: type}});\n console.log(`${type}: ${await resp.text()}\\n`);\n }\n}\n\nshowTypes();" + }, + { + "name": "A JavaScript workbench", + "file": "code/solutions/18_2_a_javascript_workbench.html", + "number": 2, + "type": "html", + "code": "\n\n\n\n\n
      \n\n",
      +        "solution": "\n\n\n\n\n
      \n\n"
      +      },
      +      {
      +        "name": "Conway's Game of Life",
      +        "file": "code/solutions/18_3_conways_game_of_life.html",
      +        "number": 3,
      +        "type": "html",
      +        "code": "\n\n\n
      \n\n\n", + "solution": "\n\n\n
      \n\n\n\n" + } + ], + "include": [ + "code/chapter/18_http.js" + ] + }, + { + "number": 19, + "id": "19_paint", + "title": "Project: A Pixel Art Editor", + "start_code": "\n\n\n
      \n\n", + "exercises": [ + { + "name": "Keyboard bindings", + "file": "code/solutions/19_1_keyboard_bindings.html", + "number": 1, + "type": "html", + "code": "\n\n\n
      \n", + "solution": "\n\n\n
      \n" + }, + { + "name": "Efficient drawing", + "file": "code/solutions/19_2_efficient_drawing.html", + "number": 2, + "type": "html", + "code": "\n\n\n
      \n", + "solution": "\n\n\n
      \n" + }, + { + "name": "Circles", + "file": "code/solutions/19_3_circles.html", + "number": 3, + "type": "html", + "code": "\n\n\n
      \n", + "solution": "\n\n\n
      \n" + }, + { + "name": "Proper lines", + "file": "code/solutions/19_4_proper_lines.html", + "number": 4, + "type": "html", + "code": "\n\n\n
      \n", + "solution": "\n\n\n
      \n" + } + ], + "include": [ + "code/chapter/19_paint.js" + ] + }, + { + "number": 20, + "id": "20_node", + "title": "Node.js", + "start_code": "", + "exercises": [ + { + "name": "Search tool", + "file": "code/solutions/20_1_search_tool.js", + "number": 1, + "type": "js", + "code": "// Node exercises can not be ran in the browser,\n// but you can look at their solution here.\n", + "solution": "const {statSync, readdirSync, readFileSync} = require(\"fs\");\n\nlet searchTerm = new RegExp(process.argv[2]);\n\nfor (let arg of process.argv.slice(3)) {\n search(arg);\n}\n\nfunction search(file) {\n let stats = statSync(file);\n if (stats.isDirectory()) {\n for (let f of readdirSync(file)) {\n search(file + \"/\" + f);\n }\n } else if (searchTerm.test(readFileSync(file, \"utf8\"))) {\n console.log(file);\n }\n}\n" + }, + { + "name": "Directory creation", + "file": "code/solutions/20_2_directory_creation.js", + "number": 2, + "type": "js", + "code": "// Node exercises can not be ran in the browser,\n// but you can look at their solution here.\n", + "solution": "// This code won't work on its own, but is also included in the\n// code/file_server.js file, which defines the whole system.\n\nconst {mkdir} = require(\"fs\").promises;\n\nmethods.MKCOL = async function(request) {\n let path = urlPath(request.url);\n let stats;\n try {\n stats = await stat(path);\n } catch (error) {\n if (error.code != \"ENOENT\") throw error;\n await mkdir(path);\n return {status: 204};\n }\n if (stats.isDirectory()) return {status: 204};\n else return {status: 400, body: \"Not a directory\"};\n};\n" + }, + { + "name": "A public space on the web", + "file": "code/solutions/20_3_a_public_space_on_the_web.zip", + "number": 3, + "type": "js", + "code": "// Node exercises can not be ran in the browser,\n// but you can look at their solution here.\n", + "solution": "// This solutions consists of multiple files. Download it\n// though the link below.\n" + } + ], + "include": null + }, + { + "number": 21, + "id": "21_skillsharing", + "title": "Project: Skill-Sharing Website", + "start_code": "", + "exercises": [ + { + "name": "Disk persistence", + "file": "code/solutions/21_1_disk_persistence.js", + "number": 1, + "type": "js", + "code": "// Node exercises can not be ran in the browser,\n// but you can look at their solution here.\n", + "solution": "// This isn't a stand-alone file, only a redefinition of a few\n// fragments from skillsharing/skillsharing_server.js\n\nconst {readFileSync, writeFile} = require(\"fs\");\n\nconst fileName = \"./talks.json\";\n\nfunction loadTalks() {\n let json;\n try {\n json = JSON.parse(readFileSync(fileName, \"utf8\"));\n } catch (e) {\n json = {};\n }\n return Object.assign(Object.create(null), json);\n}\n\nSkillShareServer.prototype.updated = function() {\n this.version++;\n let response = this.talkResponse();\n this.waiting.forEach(resolve => resolve(response));\n this.waiting = [];\n\n writeFile(fileName, JSON.stringify(this.talks), e => {\n if (e) throw e;\n });\n};\n\n// The line that starts the server must be changed to\nnew SkillShareServer(loadTalks()).start(8000);\n" + }, + { + "name": "Comment field resets", + "file": "code/solutions/21_2_comment_field_resets.js", + "number": 2, + "type": "js", + "code": "// Node exercises can not be ran in the browser,\n// but you can look at their solution here.\n", + "solution": "// This isn't a stand-alone file, only a redefinition of the main\n// component from skillsharing/public/skillsharing_client.js\n\nclass Talk {\n constructor(talk, dispatch) {\n this.comments = elt(\"div\");\n this.dom = elt(\n \"section\", {className: \"talk\"},\n elt(\"h2\", null, talk.title, \" \", elt(\"button\", {\n type: \"button\",\n onclick: () => dispatch({type: \"deleteTalk\",\n talk: talk.title})\n }, \"Delete\")),\n elt(\"div\", null, \"by \",\n elt(\"strong\", null, talk.presenter)),\n elt(\"p\", null, talk.summary),\n this.comments,\n elt(\"form\", {\n onsubmit(event) {\n event.preventDefault();\n let form = event.target;\n dispatch({type: \"newComment\",\n talk: talk.title,\n message: form.elements.comment.value});\n form.reset();\n }\n }, elt(\"input\", {type: \"text\", name: \"comment\"}), \" \",\n elt(\"button\", {type: \"submit\"}, \"Add comment\")));\n this.syncState(talk);\n }\n\n syncState(talk) {\n this.talk = talk;\n this.comments.textContent = \"\";\n for (let comment of talk.comments) {\n this.comments.appendChild(renderComment(comment));\n }\n }\n}\n\nclass SkillShareApp {\n constructor(state, dispatch) {\n this.dispatch = dispatch;\n this.talkDOM = elt(\"div\", {className: \"talks\"});\n this.talkMap = Object.create(null);\n this.dom = elt(\"div\", null,\n renderUserField(state.user, dispatch),\n this.talkDOM,\n renderTalkForm(dispatch));\n this.syncState(state);\n }\n\n syncState(state) {\n if (state.talks == this.talks) return;\n this.talks = state.talks;\n\n for (let talk of state.talks) {\n let cmp = this.talkMap[talk.title];\n if (cmp && cmp.talk.author == talk.author &&\n cmp.talk.summary == talk.summary) {\n cmp.syncState(talk);\n } else {\n if (cmp) cmp.dom.remove();\n cmp = new Talk(talk, this.dispatch);\n this.talkMap[talk.title] = cmp;\n this.talkDOM.appendChild(cmp.dom);\n }\n }\n for (let title of Object.keys(this.talkMap)) {\n if (!state.talks.some(talk => talk.title == title)) {\n this.talkMap[title].dom.remove();\n delete this.talkMap[title];\n }\n }\n }\n}\n" + } + ], + "include": null + }, + { + "title": "JavaScript and Performance", + "number": 22, + "start_code": "\n\n\n", + "include": [ + "code/draw_layout.js", + "code/chapter/22_fast.js" + ], + "exercises": [ + { + "name": "Pathfinding", + "file": "code/solutions/22_1_pathfinding.js", + "number": 1, + "type": "js", + "code": "function findPath(a, b) {\n // Your code here...\n}\n\nlet graph = treeGraph(4, 4);\nlet root = graph[0], leaf = graph[graph.length - 1];\nconsole.log(findPath(root, leaf).length);\n// → 4\n\nleaf.connect(root);\nconsole.log(findPath(root, leaf).length);\n// → 2\n", + "solution": "function findPath(a, b) {\n let work = [[a]];\n for (let path of work) {\n let end = path[path.length - 1];\n if (end == b) return path;\n for (let next of end.edges) {\n if (!work.some(path => path[path.length - 1] == next)) {\n work.push(path.concat([next]));\n }\n }\n }\n}\n\nlet graph = treeGraph(4, 4);\nlet root = graph[0], leaf = graph[graph.length - 1];\nconsole.log(findPath(root, leaf).length);\n// → 4\n\nleaf.connect(root);\nconsole.log(findPath(root, leaf).length);\n// → 2\n" + }, + { + "name": "Timing", + "file": "code/solutions/22_2_timing.js", + "number": 2, + "type": "js", + "code": "", + "solution": "function findPath(a, b) {\n let work = [[a]];\n for (let path of work) {\n let end = path[path.length - 1];\n if (end == b) return path;\n for (let next of end.edges) {\n if (!work.some(path => path[path.length - 1] == next)) {\n work.push(path.concat([next]));\n }\n }\n }\n}\n\nfunction time(findPath) {\n let graph = treeGraph(6, 6);\n let startTime = Date.now();\n let result = findPath(graph[0], graph[graph.length - 1]);\n console.log(`Path with length ${result.length} found in ${Date.now() - startTime}ms`);\n}\ntime(findPath);\n" + }, + { + "name": "Optimizing", + "file": "code/solutions/22_3_optimizing.js", + "number": 3, + "type": "js", + "code": "", + "solution": "function time(findPath) {\n let graph = treeGraph(6, 6);\n let startTime = Date.now();\n let result = findPath(graph[0], graph[graph.length - 1]);\n console.log(`Path with length ${result.length} found in ${Date.now() - startTime}ms`);\n}\n\nfunction findPath_set(a, b) {\n let work = [[a]];\n let reached = new Set([a]);\n for (let path of work) {\n let end = path[path.length - 1];\n if (end == b) return path;\n for (let next of end.edges) {\n if (!reached.has(next)) {\n reached.add(next);\n work.push(path.concat([next]));\n }\n }\n }\n}\n\ntime(findPath_set);\n\nfunction pathToArray(path) {\n let result = [];\n for (; path; path = path.via) result.unshift(path.at);\n return result;\n}\n\nfunction findPath_list(a, b) {\n let work = [{at: a, via: null}];\n let reached = new Set([a]);\n for (let path of work) {\n if (path.at == b) return pathToArray(path);\n for (let next of path.at.edges) {\n if (!reached.has(next)) {\n reached.add(next);\n work.push({at: next, via: path});\n }\n }\n }\n}\n\ntime(findPath_list);\n" + } + ] + } +]; diff --git a/docs/js/code.js b/docs/js/code.js new file mode 100644 index 000000000..93eb6a6be --- /dev/null +++ b/docs/js/code.js @@ -0,0 +1,217 @@ +addEventListener("load", () => { + let editor = CodeMirror.fromTextArea(document.querySelector("#editor"), { + mode: "javascript", + extraKeys: { + "Ctrl-Enter": runCode, + "Cmd-Enter": runCode + }, + matchBrackets: true, + lineNumbers: true + }) + function guessType(code) { + return /^[\s\w\n:]* { + clearTimeout(reGuess) + reGuess = setTimeout(() => { + if (context.type == null) { + let mode = guessType(editor.getValue()) == "html" ? "text/html" : "javascript" + if (mode != editor.getOption("mode")) + editor.setOption("mode", mode) + } + }, 500) + }) + + function hasIncludes(code, include) { + if (!include) return code + + let re = /(?:\s|)* + + + + + + +
      +

      RPM changes mode

      + +
      + + +

      MIME types defined: text/x-rpm-changes.

      +
      diff --git a/docs/js/node_modules/codemirror/mode/rpm/rpm.js b/docs/js/node_modules/codemirror/mode/rpm/rpm.js new file mode 100644 index 000000000..2dece2eab --- /dev/null +++ b/docs/js/node_modules/codemirror/mode/rpm/rpm.js @@ -0,0 +1,109 @@ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: https://codemirror.net/LICENSE + +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + +CodeMirror.defineMode("rpm-changes", function() { + var headerSeperator = /^-+$/; + var headerLine = /^(Mon|Tue|Wed|Thu|Fri|Sat|Sun) (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) ?\d{1,2} \d{2}:\d{2}(:\d{2})? [A-Z]{3,4} \d{4} - /; + var simpleEmail = /^[\w+.-]+@[\w.-]+/; + + return { + token: function(stream) { + if (stream.sol()) { + if (stream.match(headerSeperator)) { return 'tag'; } + if (stream.match(headerLine)) { return 'tag'; } + } + if (stream.match(simpleEmail)) { return 'string'; } + stream.next(); + return null; + } + }; +}); + +CodeMirror.defineMIME("text/x-rpm-changes", "rpm-changes"); + +// Quick and dirty spec file highlighting + +CodeMirror.defineMode("rpm-spec", function() { + var arch = /^(i386|i586|i686|x86_64|ppc64le|ppc64|ppc|ia64|s390x|s390|sparc64|sparcv9|sparc|noarch|alphaev6|alpha|hppa|mipsel)/; + + var preamble = /^[a-zA-Z0-9()]+:/; + var section = /^%(debug_package|package|description|prep|build|install|files|clean|changelog|preinstall|preun|postinstall|postun|pretrans|posttrans|pre|post|triggerin|triggerun|verifyscript|check|triggerpostun|triggerprein|trigger)/; + var control_flow_complex = /^%(ifnarch|ifarch|if)/; // rpm control flow macros + var control_flow_simple = /^%(else|endif)/; // rpm control flow macros + var operators = /^(\!|\?|\<\=|\<|\>\=|\>|\=\=|\&\&|\|\|)/; // operators in control flow macros + + return { + startState: function () { + return { + controlFlow: false, + macroParameters: false, + section: false + }; + }, + token: function (stream, state) { + var ch = stream.peek(); + if (ch == "#") { stream.skipToEnd(); return "comment"; } + + if (stream.sol()) { + if (stream.match(preamble)) { return "header"; } + if (stream.match(section)) { return "atom"; } + } + + if (stream.match(/^\$\w+/)) { return "def"; } // Variables like '$RPM_BUILD_ROOT' + if (stream.match(/^\$\{\w+\}/)) { return "def"; } // Variables like '${RPM_BUILD_ROOT}' + + if (stream.match(control_flow_simple)) { return "keyword"; } + if (stream.match(control_flow_complex)) { + state.controlFlow = true; + return "keyword"; + } + if (state.controlFlow) { + if (stream.match(operators)) { return "operator"; } + if (stream.match(/^(\d+)/)) { return "number"; } + if (stream.eol()) { state.controlFlow = false; } + } + + if (stream.match(arch)) { + if (stream.eol()) { state.controlFlow = false; } + return "number"; + } + + // Macros like '%make_install' or '%attr(0775,root,root)' + if (stream.match(/^%[\w]+/)) { + if (stream.match(/^\(/)) { state.macroParameters = true; } + return "keyword"; + } + if (state.macroParameters) { + if (stream.match(/^\d+/)) { return "number";} + if (stream.match(/^\)/)) { + state.macroParameters = false; + return "keyword"; + } + } + + // Macros like '%{defined fedora}' + if (stream.match(/^%\{\??[\w \-\:\!]+\}/)) { + if (stream.eol()) { state.controlFlow = false; } + return "def"; + } + + //TODO: Include bash script sub-parser (CodeMirror supports that) + stream.next(); + return null; + } + }; +}); + +CodeMirror.defineMIME("text/x-rpm-spec", "rpm-spec"); + +}); diff --git a/docs/js/node_modules/codemirror/mode/rst/rst.js b/docs/js/node_modules/codemirror/mode/rst/rst.js new file mode 100644 index 000000000..f14eb270f --- /dev/null +++ b/docs/js/node_modules/codemirror/mode/rst/rst.js @@ -0,0 +1,557 @@ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: https://codemirror.net/LICENSE + +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror"), require("../python/python"), require("../stex/stex"), require("../../addon/mode/overlay")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror", "../python/python", "../stex/stex", "../../addon/mode/overlay"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + +CodeMirror.defineMode('rst', function (config, options) { + + var rx_strong = /^\*\*[^\*\s](?:[^\*]*[^\*\s])?\*\*/; + var rx_emphasis = /^\*[^\*\s](?:[^\*]*[^\*\s])?\*/; + var rx_literal = /^``[^`\s](?:[^`]*[^`\s])``/; + + var rx_number = /^(?:[\d]+(?:[\.,]\d+)*)/; + var rx_positive = /^(?:\s\+[\d]+(?:[\.,]\d+)*)/; + var rx_negative = /^(?:\s\-[\d]+(?:[\.,]\d+)*)/; + + var rx_uri_protocol = "[Hh][Tt][Tt][Pp][Ss]?://"; + var rx_uri_domain = "(?:[\\d\\w.-]+)\\.(?:\\w{2,6})"; + var rx_uri_path = "(?:/[\\d\\w\\#\\%\\&\\-\\.\\,\\/\\:\\=\\?\\~]+)*"; + var rx_uri = new RegExp("^" + rx_uri_protocol + rx_uri_domain + rx_uri_path); + + var overlay = { + token: function (stream) { + + if (stream.match(rx_strong) && stream.match (/\W+|$/, false)) + return 'strong'; + if (stream.match(rx_emphasis) && stream.match (/\W+|$/, false)) + return 'em'; + if (stream.match(rx_literal) && stream.match (/\W+|$/, false)) + return 'string-2'; + if (stream.match(rx_number)) + return 'number'; + if (stream.match(rx_positive)) + return 'positive'; + if (stream.match(rx_negative)) + return 'negative'; + if (stream.match(rx_uri)) + return 'link'; + + while (stream.next() != null) { + if (stream.match(rx_strong, false)) break; + if (stream.match(rx_emphasis, false)) break; + if (stream.match(rx_literal, false)) break; + if (stream.match(rx_number, false)) break; + if (stream.match(rx_positive, false)) break; + if (stream.match(rx_negative, false)) break; + if (stream.match(rx_uri, false)) break; + } + + return null; + } + }; + + var mode = CodeMirror.getMode( + config, options.backdrop || 'rst-base' + ); + + return CodeMirror.overlayMode(mode, overlay, true); // combine +}, 'python', 'stex'); + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +CodeMirror.defineMode('rst-base', function (config) { + + /////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////// + + function format(string) { + var args = Array.prototype.slice.call(arguments, 1); + return string.replace(/{(\d+)}/g, function (match, n) { + return typeof args[n] != 'undefined' ? args[n] : match; + }); + } + + /////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////// + + var mode_python = CodeMirror.getMode(config, 'python'); + var mode_stex = CodeMirror.getMode(config, 'stex'); + + /////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////// + + var SEPA = "\\s+"; + var TAIL = "(?:\\s*|\\W|$)", + rx_TAIL = new RegExp(format('^{0}', TAIL)); + + var NAME = + "(?:[^\\W\\d_](?:[\\w!\"#$%&'()\\*\\+,\\-\\.\/:;<=>\\?]*[^\\W_])?)", + rx_NAME = new RegExp(format('^{0}', NAME)); + var NAME_WWS = + "(?:[^\\W\\d_](?:[\\w\\s!\"#$%&'()\\*\\+,\\-\\.\/:;<=>\\?]*[^\\W_])?)"; + var REF_NAME = format('(?:{0}|`{1}`)', NAME, NAME_WWS); + + var TEXT1 = "(?:[^\\s\\|](?:[^\\|]*[^\\s\\|])?)"; + var TEXT2 = "(?:[^\\`]+)", + rx_TEXT2 = new RegExp(format('^{0}', TEXT2)); + + var rx_section = new RegExp( + "^([!'#$%&\"()*+,-./:;<=>?@\\[\\\\\\]^_`{|}~])\\1{3,}\\s*$"); + var rx_explicit = new RegExp( + format('^\\.\\.{0}', SEPA)); + var rx_link = new RegExp( + format('^_{0}:{1}|^__:{1}', REF_NAME, TAIL)); + var rx_directive = new RegExp( + format('^{0}::{1}', REF_NAME, TAIL)); + var rx_substitution = new RegExp( + format('^\\|{0}\\|{1}{2}::{3}', TEXT1, SEPA, REF_NAME, TAIL)); + var rx_footnote = new RegExp( + format('^\\[(?:\\d+|#{0}?|\\*)]{1}', REF_NAME, TAIL)); + var rx_citation = new RegExp( + format('^\\[{0}\\]{1}', REF_NAME, TAIL)); + + var rx_substitution_ref = new RegExp( + format('^\\|{0}\\|', TEXT1)); + var rx_footnote_ref = new RegExp( + format('^\\[(?:\\d+|#{0}?|\\*)]_', REF_NAME)); + var rx_citation_ref = new RegExp( + format('^\\[{0}\\]_', REF_NAME)); + var rx_link_ref1 = new RegExp( + format('^{0}__?', REF_NAME)); + var rx_link_ref2 = new RegExp( + format('^`{0}`_', TEXT2)); + + var rx_role_pre = new RegExp( + format('^:{0}:`{1}`{2}', NAME, TEXT2, TAIL)); + var rx_role_suf = new RegExp( + format('^`{1}`:{0}:{2}', NAME, TEXT2, TAIL)); + var rx_role = new RegExp( + format('^:{0}:{1}', NAME, TAIL)); + + var rx_directive_name = new RegExp(format('^{0}', REF_NAME)); + var rx_directive_tail = new RegExp(format('^::{0}', TAIL)); + var rx_substitution_text = new RegExp(format('^\\|{0}\\|', TEXT1)); + var rx_substitution_sepa = new RegExp(format('^{0}', SEPA)); + var rx_substitution_name = new RegExp(format('^{0}', REF_NAME)); + var rx_substitution_tail = new RegExp(format('^::{0}', TAIL)); + var rx_link_head = new RegExp("^_"); + var rx_link_name = new RegExp(format('^{0}|_', REF_NAME)); + var rx_link_tail = new RegExp(format('^:{0}', TAIL)); + + var rx_verbatim = new RegExp('^::\\s*$'); + var rx_examples = new RegExp('^\\s+(?:>>>|In \\[\\d+\\]:)\\s'); + + /////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////// + + function to_normal(stream, state) { + var token = null; + + if (stream.sol() && stream.match(rx_examples, false)) { + change(state, to_mode, { + mode: mode_python, local: CodeMirror.startState(mode_python) + }); + } else if (stream.sol() && stream.match(rx_explicit)) { + change(state, to_explicit); + token = 'meta'; + } else if (stream.sol() && stream.match(rx_section)) { + change(state, to_normal); + token = 'header'; + } else if (phase(state) == rx_role_pre || + stream.match(rx_role_pre, false)) { + + switch (stage(state)) { + case 0: + change(state, to_normal, context(rx_role_pre, 1)); + stream.match(/^:/); + token = 'meta'; + break; + case 1: + change(state, to_normal, context(rx_role_pre, 2)); + stream.match(rx_NAME); + token = 'keyword'; + + if (stream.current().match(/^(?:math|latex)/)) { + state.tmp_stex = true; + } + break; + case 2: + change(state, to_normal, context(rx_role_pre, 3)); + stream.match(/^:`/); + token = 'meta'; + break; + case 3: + if (state.tmp_stex) { + state.tmp_stex = undefined; state.tmp = { + mode: mode_stex, local: CodeMirror.startState(mode_stex) + }; + } + + if (state.tmp) { + if (stream.peek() == '`') { + change(state, to_normal, context(rx_role_pre, 4)); + state.tmp = undefined; + break; + } + + token = state.tmp.mode.token(stream, state.tmp.local); + break; + } + + change(state, to_normal, context(rx_role_pre, 4)); + stream.match(rx_TEXT2); + token = 'string'; + break; + case 4: + change(state, to_normal, context(rx_role_pre, 5)); + stream.match(/^`/); + token = 'meta'; + break; + case 5: + change(state, to_normal, context(rx_role_pre, 6)); + stream.match(rx_TAIL); + break; + default: + change(state, to_normal); + } + } else if (phase(state) == rx_role_suf || + stream.match(rx_role_suf, false)) { + + switch (stage(state)) { + case 0: + change(state, to_normal, context(rx_role_suf, 1)); + stream.match(/^`/); + token = 'meta'; + break; + case 1: + change(state, to_normal, context(rx_role_suf, 2)); + stream.match(rx_TEXT2); + token = 'string'; + break; + case 2: + change(state, to_normal, context(rx_role_suf, 3)); + stream.match(/^`:/); + token = 'meta'; + break; + case 3: + change(state, to_normal, context(rx_role_suf, 4)); + stream.match(rx_NAME); + token = 'keyword'; + break; + case 4: + change(state, to_normal, context(rx_role_suf, 5)); + stream.match(/^:/); + token = 'meta'; + break; + case 5: + change(state, to_normal, context(rx_role_suf, 6)); + stream.match(rx_TAIL); + break; + default: + change(state, to_normal); + } + } else if (phase(state) == rx_role || stream.match(rx_role, false)) { + + switch (stage(state)) { + case 0: + change(state, to_normal, context(rx_role, 1)); + stream.match(/^:/); + token = 'meta'; + break; + case 1: + change(state, to_normal, context(rx_role, 2)); + stream.match(rx_NAME); + token = 'keyword'; + break; + case 2: + change(state, to_normal, context(rx_role, 3)); + stream.match(/^:/); + token = 'meta'; + break; + case 3: + change(state, to_normal, context(rx_role, 4)); + stream.match(rx_TAIL); + break; + default: + change(state, to_normal); + } + } else if (phase(state) == rx_substitution_ref || + stream.match(rx_substitution_ref, false)) { + + switch (stage(state)) { + case 0: + change(state, to_normal, context(rx_substitution_ref, 1)); + stream.match(rx_substitution_text); + token = 'variable-2'; + break; + case 1: + change(state, to_normal, context(rx_substitution_ref, 2)); + if (stream.match(/^_?_?/)) token = 'link'; + break; + default: + change(state, to_normal); + } + } else if (stream.match(rx_footnote_ref)) { + change(state, to_normal); + token = 'quote'; + } else if (stream.match(rx_citation_ref)) { + change(state, to_normal); + token = 'quote'; + } else if (stream.match(rx_link_ref1)) { + change(state, to_normal); + if (!stream.peek() || stream.peek().match(/^\W$/)) { + token = 'link'; + } + } else if (phase(state) == rx_link_ref2 || + stream.match(rx_link_ref2, false)) { + + switch (stage(state)) { + case 0: + if (!stream.peek() || stream.peek().match(/^\W$/)) { + change(state, to_normal, context(rx_link_ref2, 1)); + } else { + stream.match(rx_link_ref2); + } + break; + case 1: + change(state, to_normal, context(rx_link_ref2, 2)); + stream.match(/^`/); + token = 'link'; + break; + case 2: + change(state, to_normal, context(rx_link_ref2, 3)); + stream.match(rx_TEXT2); + break; + case 3: + change(state, to_normal, context(rx_link_ref2, 4)); + stream.match(/^`_/); + token = 'link'; + break; + default: + change(state, to_normal); + } + } else if (stream.match(rx_verbatim)) { + change(state, to_verbatim); + } + + else { + if (stream.next()) change(state, to_normal); + } + + return token; + } + + /////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////// + + function to_explicit(stream, state) { + var token = null; + + if (phase(state) == rx_substitution || + stream.match(rx_substitution, false)) { + + switch (stage(state)) { + case 0: + change(state, to_explicit, context(rx_substitution, 1)); + stream.match(rx_substitution_text); + token = 'variable-2'; + break; + case 1: + change(state, to_explicit, context(rx_substitution, 2)); + stream.match(rx_substitution_sepa); + break; + case 2: + change(state, to_explicit, context(rx_substitution, 3)); + stream.match(rx_substitution_name); + token = 'keyword'; + break; + case 3: + change(state, to_explicit, context(rx_substitution, 4)); + stream.match(rx_substitution_tail); + token = 'meta'; + break; + default: + change(state, to_normal); + } + } else if (phase(state) == rx_directive || + stream.match(rx_directive, false)) { + + switch (stage(state)) { + case 0: + change(state, to_explicit, context(rx_directive, 1)); + stream.match(rx_directive_name); + token = 'keyword'; + + if (stream.current().match(/^(?:math|latex)/)) + state.tmp_stex = true; + else if (stream.current().match(/^python/)) + state.tmp_py = true; + break; + case 1: + change(state, to_explicit, context(rx_directive, 2)); + stream.match(rx_directive_tail); + token = 'meta'; + + if (stream.match(/^latex\s*$/) || state.tmp_stex) { + state.tmp_stex = undefined; change(state, to_mode, { + mode: mode_stex, local: CodeMirror.startState(mode_stex) + }); + } + break; + case 2: + change(state, to_explicit, context(rx_directive, 3)); + if (stream.match(/^python\s*$/) || state.tmp_py) { + state.tmp_py = undefined; change(state, to_mode, { + mode: mode_python, local: CodeMirror.startState(mode_python) + }); + } + break; + default: + change(state, to_normal); + } + } else if (phase(state) == rx_link || stream.match(rx_link, false)) { + + switch (stage(state)) { + case 0: + change(state, to_explicit, context(rx_link, 1)); + stream.match(rx_link_head); + stream.match(rx_link_name); + token = 'link'; + break; + case 1: + change(state, to_explicit, context(rx_link, 2)); + stream.match(rx_link_tail); + token = 'meta'; + break; + default: + change(state, to_normal); + } + } else if (stream.match(rx_footnote)) { + change(state, to_normal); + token = 'quote'; + } else if (stream.match(rx_citation)) { + change(state, to_normal); + token = 'quote'; + } + + else { + stream.eatSpace(); + if (stream.eol()) { + change(state, to_normal); + } else { + stream.skipToEnd(); + change(state, to_comment); + token = 'comment'; + } + } + + return token; + } + + /////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////// + + function to_comment(stream, state) { + return as_block(stream, state, 'comment'); + } + + function to_verbatim(stream, state) { + return as_block(stream, state, 'meta'); + } + + function as_block(stream, state, token) { + if (stream.eol() || stream.eatSpace()) { + stream.skipToEnd(); + return token; + } else { + change(state, to_normal); + return null; + } + } + + /////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////// + + function to_mode(stream, state) { + + if (state.ctx.mode && state.ctx.local) { + + if (stream.sol()) { + if (!stream.eatSpace()) change(state, to_normal); + return null; + } + + return state.ctx.mode.token(stream, state.ctx.local); + } + + change(state, to_normal); + return null; + } + + /////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////// + + function context(phase, stage, mode, local) { + return {phase: phase, stage: stage, mode: mode, local: local}; + } + + function change(state, tok, ctx) { + state.tok = tok; + state.ctx = ctx || {}; + } + + function stage(state) { + return state.ctx.stage || 0; + } + + function phase(state) { + return state.ctx.phase; + } + + /////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////// + + return { + startState: function () { + return {tok: to_normal, ctx: context(undefined, 0)}; + }, + + copyState: function (state) { + var ctx = state.ctx, tmp = state.tmp; + if (ctx.local) + ctx = {mode: ctx.mode, local: CodeMirror.copyState(ctx.mode, ctx.local)}; + if (tmp) + tmp = {mode: tmp.mode, local: CodeMirror.copyState(tmp.mode, tmp.local)}; + return {tok: state.tok, ctx: ctx, tmp: tmp}; + }, + + innerMode: function (state) { + return state.tmp ? {state: state.tmp.local, mode: state.tmp.mode} + : state.ctx.mode ? {state: state.ctx.local, mode: state.ctx.mode} + : null; + }, + + token: function (stream, state) { + return state.tok(stream, state); + } + }; +}, 'python', 'stex'); + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +CodeMirror.defineMIME('text/x-rst', 'rst'); + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +}); diff --git a/docs/js/node_modules/codemirror/mode/ruby/ruby.js b/docs/js/node_modules/codemirror/mode/ruby/ruby.js new file mode 100644 index 000000000..dd0e603e5 --- /dev/null +++ b/docs/js/node_modules/codemirror/mode/ruby/ruby.js @@ -0,0 +1,298 @@ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: https://codemirror.net/LICENSE + +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + +CodeMirror.defineMode("ruby", function(config) { + function wordObj(words) { + var o = {}; + for (var i = 0, e = words.length; i < e; ++i) o[words[i]] = true; + return o; + } + var keywords = wordObj([ + "alias", "and", "BEGIN", "begin", "break", "case", "class", "def", "defined?", "do", "else", + "elsif", "END", "end", "ensure", "false", "for", "if", "in", "module", "next", "not", "or", + "redo", "rescue", "retry", "return", "self", "super", "then", "true", "undef", "unless", + "until", "when", "while", "yield", "nil", "raise", "throw", "catch", "fail", "loop", "callcc", + "caller", "lambda", "proc", "public", "protected", "private", "require", "load", + "require_relative", "extend", "autoload", "__END__", "__FILE__", "__LINE__", "__dir__" + ]); + var indentWords = wordObj(["def", "class", "case", "for", "while", "until", "module", "then", + "catch", "loop", "proc", "begin"]); + var dedentWords = wordObj(["end", "until"]); + var opening = {"[": "]", "{": "}", "(": ")"}; + var closing = {"]": "[", "}": "{", ")": "("}; + var curPunc; + + function chain(newtok, stream, state) { + state.tokenize.push(newtok); + return newtok(stream, state); + } + + function tokenBase(stream, state) { + if (stream.sol() && stream.match("=begin") && stream.eol()) { + state.tokenize.push(readBlockComment); + return "comment"; + } + if (stream.eatSpace()) return null; + var ch = stream.next(), m; + if (ch == "`" || ch == "'" || ch == '"') { + return chain(readQuoted(ch, "string", ch == '"' || ch == "`"), stream, state); + } else if (ch == "/") { + if (regexpAhead(stream)) + return chain(readQuoted(ch, "string-2", true), stream, state); + else + return "operator"; + } else if (ch == "%") { + var style = "string", embed = true; + if (stream.eat("s")) style = "atom"; + else if (stream.eat(/[WQ]/)) style = "string"; + else if (stream.eat(/[r]/)) style = "string-2"; + else if (stream.eat(/[wxq]/)) { style = "string"; embed = false; } + var delim = stream.eat(/[^\w\s=]/); + if (!delim) return "operator"; + if (opening.propertyIsEnumerable(delim)) delim = opening[delim]; + return chain(readQuoted(delim, style, embed, true), stream, state); + } else if (ch == "#") { + stream.skipToEnd(); + return "comment"; + } else if (ch == "<" && (m = stream.match(/^<([-~])[\`\"\']?([a-zA-Z_?]\w*)[\`\"\']?(?:;|$)/))) { + return chain(readHereDoc(m[2], m[1]), stream, state); + } else if (ch == "0") { + if (stream.eat("x")) stream.eatWhile(/[\da-fA-F]/); + else if (stream.eat("b")) stream.eatWhile(/[01]/); + else stream.eatWhile(/[0-7]/); + return "number"; + } else if (/\d/.test(ch)) { + stream.match(/^[\d_]*(?:\.[\d_]+)?(?:[eE][+\-]?[\d_]+)?/); + return "number"; + } else if (ch == "?") { + while (stream.match(/^\\[CM]-/)) {} + if (stream.eat("\\")) stream.eatWhile(/\w/); + else stream.next(); + return "string"; + } else if (ch == ":") { + if (stream.eat("'")) return chain(readQuoted("'", "atom", false), stream, state); + if (stream.eat('"')) return chain(readQuoted('"', "atom", true), stream, state); + + // :> :>> :< :<< are valid symbols + if (stream.eat(/[\<\>]/)) { + stream.eat(/[\<\>]/); + return "atom"; + } + + // :+ :- :/ :* :| :& :! are valid symbols + if (stream.eat(/[\+\-\*\/\&\|\:\!]/)) { + return "atom"; + } + + // Symbols can't start by a digit + if (stream.eat(/[a-zA-Z$@_\xa1-\uffff]/)) { + stream.eatWhile(/[\w$\xa1-\uffff]/); + // Only one ? ! = is allowed and only as the last character + stream.eat(/[\?\!\=]/); + return "atom"; + } + return "operator"; + } else if (ch == "@" && stream.match(/^@?[a-zA-Z_\xa1-\uffff]/)) { + stream.eat("@"); + stream.eatWhile(/[\w\xa1-\uffff]/); + return "variable-2"; + } else if (ch == "$") { + if (stream.eat(/[a-zA-Z_]/)) { + stream.eatWhile(/[\w]/); + } else if (stream.eat(/\d/)) { + stream.eat(/\d/); + } else { + stream.next(); // Must be a special global like $: or $! + } + return "variable-3"; + } else if (/[a-zA-Z_\xa1-\uffff]/.test(ch)) { + stream.eatWhile(/[\w\xa1-\uffff]/); + stream.eat(/[\?\!]/); + if (stream.eat(":")) return "atom"; + return "ident"; + } else if (ch == "|" && (state.varList || state.lastTok == "{" || state.lastTok == "do")) { + curPunc = "|"; + return null; + } else if (/[\(\)\[\]{}\\;]/.test(ch)) { + curPunc = ch; + return null; + } else if (ch == "-" && stream.eat(">")) { + return "arrow"; + } else if (/[=+\-\/*:\.^%<>~|]/.test(ch)) { + var more = stream.eatWhile(/[=+\-\/*:\.^%<>~|]/); + if (ch == "." && !more) curPunc = "."; + return "operator"; + } else { + return null; + } + } + + function regexpAhead(stream) { + var start = stream.pos, depth = 0, next, found = false, escaped = false + while ((next = stream.next()) != null) { + if (!escaped) { + if ("[{(".indexOf(next) > -1) { + depth++ + } else if ("]})".indexOf(next) > -1) { + depth-- + if (depth < 0) break + } else if (next == "/" && depth == 0) { + found = true + break + } + escaped = next == "\\" + } else { + escaped = false + } + } + stream.backUp(stream.pos - start) + return found + } + + function tokenBaseUntilBrace(depth) { + if (!depth) depth = 1; + return function(stream, state) { + if (stream.peek() == "}") { + if (depth == 1) { + state.tokenize.pop(); + return state.tokenize[state.tokenize.length-1](stream, state); + } else { + state.tokenize[state.tokenize.length - 1] = tokenBaseUntilBrace(depth - 1); + } + } else if (stream.peek() == "{") { + state.tokenize[state.tokenize.length - 1] = tokenBaseUntilBrace(depth + 1); + } + return tokenBase(stream, state); + }; + } + function tokenBaseOnce() { + var alreadyCalled = false; + return function(stream, state) { + if (alreadyCalled) { + state.tokenize.pop(); + return state.tokenize[state.tokenize.length-1](stream, state); + } + alreadyCalled = true; + return tokenBase(stream, state); + }; + } + function readQuoted(quote, style, embed, unescaped) { + return function(stream, state) { + var escaped = false, ch; + + if (state.context.type === 'read-quoted-paused') { + state.context = state.context.prev; + stream.eat("}"); + } + + while ((ch = stream.next()) != null) { + if (ch == quote && (unescaped || !escaped)) { + state.tokenize.pop(); + break; + } + if (embed && ch == "#" && !escaped) { + if (stream.eat("{")) { + if (quote == "}") { + state.context = {prev: state.context, type: 'read-quoted-paused'}; + } + state.tokenize.push(tokenBaseUntilBrace()); + break; + } else if (/[@\$]/.test(stream.peek())) { + state.tokenize.push(tokenBaseOnce()); + break; + } + } + escaped = !escaped && ch == "\\"; + } + return style; + }; + } + function readHereDoc(phrase, mayIndent) { + return function(stream, state) { + if (mayIndent) stream.eatSpace() + if (stream.match(phrase)) state.tokenize.pop(); + else stream.skipToEnd(); + return "string"; + }; + } + function readBlockComment(stream, state) { + if (stream.sol() && stream.match("=end") && stream.eol()) + state.tokenize.pop(); + stream.skipToEnd(); + return "comment"; + } + + return { + startState: function() { + return {tokenize: [tokenBase], + indented: 0, + context: {type: "top", indented: -config.indentUnit}, + continuedLine: false, + lastTok: null, + varList: false}; + }, + + token: function(stream, state) { + curPunc = null; + if (stream.sol()) state.indented = stream.indentation(); + var style = state.tokenize[state.tokenize.length-1](stream, state), kwtype; + var thisTok = curPunc; + if (style == "ident") { + var word = stream.current(); + style = state.lastTok == "." ? "property" + : keywords.propertyIsEnumerable(stream.current()) ? "keyword" + : /^[A-Z]/.test(word) ? "tag" + : (state.lastTok == "def" || state.lastTok == "class" || state.varList) ? "def" + : "variable"; + if (style == "keyword") { + thisTok = word; + if (indentWords.propertyIsEnumerable(word)) kwtype = "indent"; + else if (dedentWords.propertyIsEnumerable(word)) kwtype = "dedent"; + else if ((word == "if" || word == "unless") && stream.column() == stream.indentation()) + kwtype = "indent"; + else if (word == "do" && state.context.indented < state.indented) + kwtype = "indent"; + } + } + if (curPunc || (style && style != "comment")) state.lastTok = thisTok; + if (curPunc == "|") state.varList = !state.varList; + + if (kwtype == "indent" || /[\(\[\{]/.test(curPunc)) + state.context = {prev: state.context, type: curPunc || style, indented: state.indented}; + else if ((kwtype == "dedent" || /[\)\]\}]/.test(curPunc)) && state.context.prev) + state.context = state.context.prev; + + if (stream.eol()) + state.continuedLine = (curPunc == "\\" || style == "operator"); + return style; + }, + + indent: function(state, textAfter) { + if (state.tokenize[state.tokenize.length-1] != tokenBase) return CodeMirror.Pass; + var firstChar = textAfter && textAfter.charAt(0); + var ct = state.context; + var closed = ct.type == closing[firstChar] || + ct.type == "keyword" && /^(?:end|until|else|elsif|when|rescue)\b/.test(textAfter); + return ct.indented + (closed ? 0 : config.indentUnit) + + (state.continuedLine ? config.indentUnit : 0); + }, + + electricInput: /^\s*(?:end|rescue|elsif|else|\})$/, + lineComment: "#", + fold: "indent" + }; +}); + +CodeMirror.defineMIME("text/x-ruby", "ruby"); + +}); diff --git a/docs/js/node_modules/codemirror/mode/rust/rust.js b/docs/js/node_modules/codemirror/mode/rust/rust.js new file mode 100644 index 000000000..6bcfbc444 --- /dev/null +++ b/docs/js/node_modules/codemirror/mode/rust/rust.js @@ -0,0 +1,72 @@ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: https://codemirror.net/LICENSE + +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror"), require("../../addon/mode/simple")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror", "../../addon/mode/simple"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + +CodeMirror.defineSimpleMode("rust",{ + start: [ + // string and byte string + {regex: /b?"/, token: "string", next: "string"}, + // raw string and raw byte string + {regex: /b?r"/, token: "string", next: "string_raw"}, + {regex: /b?r#+"/, token: "string", next: "string_raw_hash"}, + // character + {regex: /'(?:[^'\\]|\\(?:[nrt0'"]|x[\da-fA-F]{2}|u\{[\da-fA-F]{6}\}))'/, token: "string-2"}, + // byte + {regex: /b'(?:[^']|\\(?:['\\nrt0]|x[\da-fA-F]{2}))'/, token: "string-2"}, + + {regex: /(?:(?:[0-9][0-9_]*)(?:(?:[Ee][+-]?[0-9_]+)|\.[0-9_]+(?:[Ee][+-]?[0-9_]+)?)(?:f32|f64)?)|(?:0(?:b[01_]+|(?:o[0-7_]+)|(?:x[0-9a-fA-F_]+))|(?:[0-9][0-9_]*))(?:u8|u16|u32|u64|i8|i16|i32|i64|isize|usize)?/, + token: "number"}, + {regex: /(let(?:\s+mut)?|fn|enum|mod|struct|type)(\s+)([a-zA-Z_][a-zA-Z0-9_]*)/, token: ["keyword", null, "def"]}, + {regex: /(?:abstract|alignof|as|box|break|continue|const|crate|do|else|enum|extern|fn|for|final|if|impl|in|loop|macro|match|mod|move|offsetof|override|priv|proc|pub|pure|ref|return|self|sizeof|static|struct|super|trait|type|typeof|unsafe|unsized|use|virtual|where|while|yield)\b/, token: "keyword"}, + {regex: /\b(?:Self|isize|usize|char|bool|u8|u16|u32|u64|f16|f32|f64|i8|i16|i32|i64|str|Option)\b/, token: "atom"}, + {regex: /\b(?:true|false|Some|None|Ok|Err)\b/, token: "builtin"}, + {regex: /\b(fn)(\s+)([a-zA-Z_][a-zA-Z0-9_]*)/, + token: ["keyword", null ,"def"]}, + {regex: /#!?\[.*\]/, token: "meta"}, + {regex: /\/\/.*/, token: "comment"}, + {regex: /\/\*/, token: "comment", next: "comment"}, + {regex: /[-+\/*=<>!]+/, token: "operator"}, + {regex: /[a-zA-Z_]\w*!/,token: "variable-3"}, + {regex: /[a-zA-Z_]\w*/, token: "variable"}, + {regex: /[\{\[\(]/, indent: true}, + {regex: /[\}\]\)]/, dedent: true} + ], + string: [ + {regex: /"/, token: "string", next: "start"}, + {regex: /(?:[^\\"]|\\(?:.|$))*/, token: "string"} + ], + string_raw: [ + {regex: /"/, token: "string", next: "start"}, + {regex: /[^"]*/, token: "string"} + ], + string_raw_hash: [ + {regex: /"#+/, token: "string", next: "start"}, + {regex: /(?:[^"]|"(?!#))*/, token: "string"} + ], + comment: [ + {regex: /.*?\*\//, token: "comment", next: "start"}, + {regex: /.*/, token: "comment"} + ], + meta: { + dontIndentStates: ["comment"], + electricInput: /^\s*\}$/, + blockCommentStart: "/*", + blockCommentEnd: "*/", + lineComment: "//", + fold: "brace" + } +}); + + +CodeMirror.defineMIME("text/x-rustsrc", "rust"); +CodeMirror.defineMIME("text/rust", "rust"); +}); diff --git a/docs/js/node_modules/codemirror/mode/sas/sas.js b/docs/js/node_modules/codemirror/mode/sas/sas.js new file mode 100755 index 000000000..c6f528e0a --- /dev/null +++ b/docs/js/node_modules/codemirror/mode/sas/sas.js @@ -0,0 +1,303 @@ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: https://codemirror.net/LICENSE + + +// SAS mode copyright (c) 2016 Jared Dean, SAS Institute +// Created by Jared Dean + +// TODO +// indent and de-indent +// identify macro variables + + +//Definitions +// comment -- text within * ; or /* */ +// keyword -- SAS language variable +// variable -- macro variables starts with '&' or variable formats +// variable-2 -- DATA Step, proc, or macro names +// string -- text within ' ' or " " +// operator -- numeric operator + / - * ** le eq ge ... and so on +// builtin -- proc %macro data run mend +// atom +// def + +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { + "use strict"; + + CodeMirror.defineMode("sas", function () { + var words = {}; + var isDoubleOperatorSym = { + eq: 'operator', + lt: 'operator', + le: 'operator', + gt: 'operator', + ge: 'operator', + "in": 'operator', + ne: 'operator', + or: 'operator' + }; + var isDoubleOperatorChar = /(<=|>=|!=|<>)/; + var isSingleOperatorChar = /[=\(:\),{}.*<>+\-\/^\[\]]/; + + // Takes a string of words separated by spaces and adds them as + // keys with the value of the first argument 'style' + function define(style, string, context) { + if (context) { + var split = string.split(' '); + for (var i = 0; i < split.length; i++) { + words[split[i]] = {style: style, state: context}; + } + } + } + //datastep + define('def', 'stack pgm view source debug nesting nolist', ['inDataStep']); + define('def', 'if while until for do do; end end; then else cancel', ['inDataStep']); + define('def', 'label format _n_ _error_', ['inDataStep']); + define('def', 'ALTER BUFNO BUFSIZE CNTLLEV COMPRESS DLDMGACTION ENCRYPT ENCRYPTKEY EXTENDOBSCOUNTER GENMAX GENNUM INDEX LABEL OBSBUF OUTREP PW PWREQ READ REPEMPTY REPLACE REUSE ROLE SORTEDBY SPILL TOBSNO TYPE WRITE FILECLOSE FIRSTOBS IN OBS POINTOBS WHERE WHEREUP IDXNAME IDXWHERE DROP KEEP RENAME', ['inDataStep']); + define('def', 'filevar finfo finv fipname fipnamel fipstate first firstobs floor', ['inDataStep']); + define('def', 'varfmt varinfmt varlabel varlen varname varnum varray varrayx vartype verify vformat vformatd vformatdx vformatn vformatnx vformatw vformatwx vformatx vinarray vinarrayx vinformat vinformatd vinformatdx vinformatn vinformatnx vinformatw vinformatwx vinformatx vlabel vlabelx vlength vlengthx vname vnamex vnferr vtype vtypex weekday', ['inDataStep']); + define('def', 'zipfips zipname zipnamel zipstate', ['inDataStep']); + define('def', 'put putc putn', ['inDataStep']); + define('builtin', 'data run', ['inDataStep']); + + + //proc + define('def', 'data', ['inProc']); + + // flow control for macros + define('def', '%if %end %end; %else %else; %do %do; %then', ['inMacro']); + + //everywhere + define('builtin', 'proc run; quit; libname filename %macro %mend option options', ['ALL']); + + define('def', 'footnote title libname ods', ['ALL']); + define('def', '%let %put %global %sysfunc %eval ', ['ALL']); + // automatic macro variables http://support.sas.com/documentation/cdl/en/mcrolref/61885/HTML/default/viewer.htm#a003167023.htm + define('variable', '&sysbuffr &syscc &syscharwidth &syscmd &sysdate &sysdate9 &sysday &sysdevic &sysdmg &sysdsn &sysencoding &sysenv &syserr &syserrortext &sysfilrc &syshostname &sysindex &sysinfo &sysjobid &syslast &syslckrc &syslibrc &syslogapplname &sysmacroname &sysmenv &sysmsg &sysncpu &sysodspath &sysparm &syspbuff &sysprocessid &sysprocessname &sysprocname &sysrc &sysscp &sysscpl &sysscpl &syssite &sysstartid &sysstartname &systcpiphostname &systime &sysuserid &sysver &sysvlong &sysvlong4 &syswarningtext', ['ALL']); + + //footnote[1-9]? title[1-9]? + + //options statement + define('def', 'source2 nosource2 page pageno pagesize', ['ALL']); + + //proc and datastep + define('def', '_all_ _character_ _cmd_ _freq_ _i_ _infile_ _last_ _msg_ _null_ _numeric_ _temporary_ _type_ abort abs addr adjrsq airy alpha alter altlog altprint and arcos array arsin as atan attrc attrib attrn authserver autoexec awscontrol awsdef awsmenu awsmenumerge awstitle backward band base betainv between blocksize blshift bnot bor brshift bufno bufsize bxor by byerr byline byte calculated call cards cards4 catcache cbufno cdf ceil center cexist change chisq cinv class cleanup close cnonct cntllev coalesce codegen col collate collin column comamid comaux1 comaux2 comdef compbl compound compress config continue convert cos cosh cpuid create cross crosstab css curobs cv daccdb daccdbsl daccsl daccsyd dacctab dairy datalines datalines4 datejul datepart datetime day dbcslang dbcstype dclose ddm delete delimiter depdb depdbsl depsl depsyd deptab dequote descending descript design= device dflang dhms dif digamma dim dinfo display distinct dkricond dkrocond dlm dnum do dopen doptname doptnum dread drop dropnote dsname dsnferr echo else emaildlg emailid emailpw emailserver emailsys encrypt end endsas engine eof eov erf erfc error errorcheck errors exist exp fappend fclose fcol fdelete feedback fetch fetchobs fexist fget file fileclose fileexist filefmt filename fileref fmterr fmtsearch fnonct fnote font fontalias fopen foptname foptnum force formatted formchar formdelim formdlim forward fpoint fpos fput fread frewind frlen from fsep fuzz fwrite gaminv gamma getoption getvarc getvarn go goto group gwindow hbar hbound helpenv helploc hms honorappearance hosthelp hostprint hour hpct html hvar ibessel ibr id if index indexc indexw initcmd initstmt inner input inputc inputn inr insert int intck intnx into intrr invaliddata irr is jbessel join juldate keep kentb kurtosis label lag last lbound leave left length levels lgamma lib library libref line linesize link list log log10 log2 logpdf logpmf logsdf lostcard lowcase lrecl ls macro macrogen maps mautosource max maxdec maxr mdy mean measures median memtype merge merror min minute missing missover mlogic mod mode model modify month mopen mort mprint mrecall msglevel msymtabmax mvarsize myy n nest netpv new news nmiss no nobatch nobs nocaps nocardimage nocenter nocharcode nocmdmac nocol nocum nodate nodbcs nodetails nodmr nodms nodmsbatch nodup nodupkey noduplicates noechoauto noequals noerrorabend noexitwindows nofullstimer noicon noimplmac noint nolist noloadlist nomiss nomlogic nomprint nomrecall nomsgcase nomstored nomultenvappl nonotes nonumber noobs noovp nopad nopercent noprint noprintinit normal norow norsasuser nosetinit nosplash nosymbolgen note notes notitle notitles notsorted noverbose noxsync noxwait npv null number numkeys nummousekeys nway obs on open order ordinal otherwise out outer outp= output over ovp p(1 5 10 25 50 75 90 95 99) pad pad2 paired parm parmcards path pathdll pathname pdf peek peekc pfkey pmf point poisson poke position printer probbeta probbnml probchi probf probgam probhypr probit probnegb probnorm probsig probt procleave prt ps pw pwreq qtr quote r ranbin rancau ranexp rangam range ranks rannor ranpoi rantbl rantri ranuni read recfm register regr remote remove rename repeat replace resolve retain return reuse reverse rewind right round rsquare rtf rtrace rtraceloc s s2 samploc sasautos sascontrol sasfrscr sasmsg sasmstore sasscript sasuser saving scan sdf second select selection separated seq serror set setcomm setot sign simple sin sinh siteinfo skewness skip sle sls sortedby sortpgm sortseq sortsize soundex spedis splashlocation split spool sqrt start std stderr stdin stfips stimer stname stnamel stop stopover subgroup subpopn substr sum sumwgt symbol symbolgen symget symput sysget sysin sysleave sysmsg sysparm sysprint sysprintfont sysprod sysrc system t table tables tan tanh tapeclose tbufsize terminal test then timepart tinv tnonct to today tol tooldef totper transformout translate trantab tranwrd trigamma trim trimn trunc truncover type unformatted uniform union until upcase update user usericon uss validate value var weight when where while wincharset window work workinit workterm write wsum xsync xwait yearcutoff yes yyq min max', ['inDataStep', 'inProc']); + define('operator', 'and not ', ['inDataStep', 'inProc']); + + // Main function + function tokenize(stream, state) { + // Finally advance the stream + var ch = stream.next(); + + // BLOCKCOMMENT + if (ch === '/' && stream.eat('*')) { + state.continueComment = true; + return "comment"; + } else if (state.continueComment === true) { // in comment block + //comment ends at the beginning of the line + if (ch === '*' && stream.peek() === '/') { + stream.next(); + state.continueComment = false; + } else if (stream.skipTo('*')) { //comment is potentially later in line + stream.skipTo('*'); + stream.next(); + if (stream.eat('/')) + state.continueComment = false; + } else { + stream.skipToEnd(); + } + return "comment"; + } + + if (ch == "*" && stream.column() == stream.indentation()) { + stream.skipToEnd() + return "comment" + } + + // DoubleOperator match + var doubleOperator = ch + stream.peek(); + + if ((ch === '"' || ch === "'") && !state.continueString) { + state.continueString = ch + return "string" + } else if (state.continueString) { + if (state.continueString == ch) { + state.continueString = null; + } else if (stream.skipTo(state.continueString)) { + // quote found on this line + stream.next(); + state.continueString = null; + } else { + stream.skipToEnd(); + } + return "string"; + } else if (state.continueString !== null && stream.eol()) { + stream.skipTo(state.continueString) || stream.skipToEnd(); + return "string"; + } else if (/[\d\.]/.test(ch)) { //find numbers + if (ch === ".") + stream.match(/^[0-9]+([eE][\-+]?[0-9]+)?/); + else if (ch === "0") + stream.match(/^[xX][0-9a-fA-F]+/) || stream.match(/^0[0-7]+/); + else + stream.match(/^[0-9]*\.?[0-9]*([eE][\-+]?[0-9]+)?/); + return "number"; + } else if (isDoubleOperatorChar.test(ch + stream.peek())) { // TWO SYMBOL TOKENS + stream.next(); + return "operator"; + } else if (isDoubleOperatorSym.hasOwnProperty(doubleOperator)) { + stream.next(); + if (stream.peek() === ' ') + return isDoubleOperatorSym[doubleOperator.toLowerCase()]; + } else if (isSingleOperatorChar.test(ch)) { // SINGLE SYMBOL TOKENS + return "operator"; + } + + // Matches one whole word -- even if the word is a character + var word; + if (stream.match(/[%&;\w]+/, false) != null) { + word = ch + stream.match(/[%&;\w]+/, true); + if (/&/.test(word)) return 'variable' + } else { + word = ch; + } + // the word after DATA PROC or MACRO + if (state.nextword) { + stream.match(/[\w]+/); + // match memname.libname + if (stream.peek() === '.') stream.skipTo(' '); + state.nextword = false; + return 'variable-2'; + } + + word = word.toLowerCase() + // Are we in a DATA Step? + if (state.inDataStep) { + if (word === 'run;' || stream.match(/run\s;/)) { + state.inDataStep = false; + return 'builtin'; + } + // variable formats + if ((word) && stream.next() === '.') { + //either a format or libname.memname + if (/\w/.test(stream.peek())) return 'variable-2'; + else return 'variable'; + } + // do we have a DATA Step keyword + if (word && words.hasOwnProperty(word) && + (words[word].state.indexOf("inDataStep") !== -1 || + words[word].state.indexOf("ALL") !== -1)) { + //backup to the start of the word + if (stream.start < stream.pos) + stream.backUp(stream.pos - stream.start); + //advance the length of the word and return + for (var i = 0; i < word.length; ++i) stream.next(); + return words[word].style; + } + } + // Are we in an Proc statement? + if (state.inProc) { + if (word === 'run;' || word === 'quit;') { + state.inProc = false; + return 'builtin'; + } + // do we have a proc keyword + if (word && words.hasOwnProperty(word) && + (words[word].state.indexOf("inProc") !== -1 || + words[word].state.indexOf("ALL") !== -1)) { + stream.match(/[\w]+/); + return words[word].style; + } + } + // Are we in a Macro statement? + if (state.inMacro) { + if (word === '%mend') { + if (stream.peek() === ';') stream.next(); + state.inMacro = false; + return 'builtin'; + } + if (word && words.hasOwnProperty(word) && + (words[word].state.indexOf("inMacro") !== -1 || + words[word].state.indexOf("ALL") !== -1)) { + stream.match(/[\w]+/); + return words[word].style; + } + + return 'atom'; + } + // Do we have Keywords specific words? + if (word && words.hasOwnProperty(word)) { + // Negates the initial next() + stream.backUp(1); + // Actually move the stream + stream.match(/[\w]+/); + if (word === 'data' && /=/.test(stream.peek()) === false) { + state.inDataStep = true; + state.nextword = true; + return 'builtin'; + } + if (word === 'proc') { + state.inProc = true; + state.nextword = true; + return 'builtin'; + } + if (word === '%macro') { + state.inMacro = true; + state.nextword = true; + return 'builtin'; + } + if (/title[1-9]/.test(word)) return 'def'; + + if (word === 'footnote') { + stream.eat(/[1-9]/); + return 'def'; + } + + // Returns their value as state in the prior define methods + if (state.inDataStep === true && words[word].state.indexOf("inDataStep") !== -1) + return words[word].style; + if (state.inProc === true && words[word].state.indexOf("inProc") !== -1) + return words[word].style; + if (state.inMacro === true && words[word].state.indexOf("inMacro") !== -1) + return words[word].style; + if (words[word].state.indexOf("ALL") !== -1) + return words[word].style; + return null; + } + // Unrecognized syntax + return null; + } + + return { + startState: function () { + return { + inDataStep: false, + inProc: false, + inMacro: false, + nextword: false, + continueString: null, + continueComment: false + }; + }, + token: function (stream, state) { + // Strip the spaces, but regex will account for them either way + if (stream.eatSpace()) return null; + // Go through the main process + return tokenize(stream, state); + }, + + blockCommentStart: "/*", + blockCommentEnd: "*/" + }; + + }); + + CodeMirror.defineMIME("text/x-sas", "sas"); +}); diff --git a/docs/js/node_modules/codemirror/mode/sass/sass.js b/docs/js/node_modules/codemirror/mode/sass/sass.js new file mode 100644 index 000000000..c37ab0b28 --- /dev/null +++ b/docs/js/node_modules/codemirror/mode/sass/sass.js @@ -0,0 +1,454 @@ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: https://codemirror.net/LICENSE + +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror"), require("../css/css")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror", "../css/css"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + +CodeMirror.defineMode("sass", function(config) { + var cssMode = CodeMirror.mimeModes["text/css"]; + var propertyKeywords = cssMode.propertyKeywords || {}, + colorKeywords = cssMode.colorKeywords || {}, + valueKeywords = cssMode.valueKeywords || {}, + fontProperties = cssMode.fontProperties || {}; + + function tokenRegexp(words) { + return new RegExp("^" + words.join("|")); + } + + var keywords = ["true", "false", "null", "auto"]; + var keywordsRegexp = new RegExp("^" + keywords.join("|")); + + var operators = ["\\(", "\\)", "=", ">", "<", "==", ">=", "<=", "\\+", "-", + "\\!=", "/", "\\*", "%", "and", "or", "not", ";","\\{","\\}",":"]; + var opRegexp = tokenRegexp(operators); + + var pseudoElementsRegexp = /^::?[a-zA-Z_][\w\-]*/; + + var word; + + function isEndLine(stream) { + return !stream.peek() || stream.match(/\s+$/, false); + } + + function urlTokens(stream, state) { + var ch = stream.peek(); + + if (ch === ")") { + stream.next(); + state.tokenizer = tokenBase; + return "operator"; + } else if (ch === "(") { + stream.next(); + stream.eatSpace(); + + return "operator"; + } else if (ch === "'" || ch === '"') { + state.tokenizer = buildStringTokenizer(stream.next()); + return "string"; + } else { + state.tokenizer = buildStringTokenizer(")", false); + return "string"; + } + } + function comment(indentation, multiLine) { + return function(stream, state) { + if (stream.sol() && stream.indentation() <= indentation) { + state.tokenizer = tokenBase; + return tokenBase(stream, state); + } + + if (multiLine && stream.skipTo("*/")) { + stream.next(); + stream.next(); + state.tokenizer = tokenBase; + } else { + stream.skipToEnd(); + } + + return "comment"; + }; + } + + function buildStringTokenizer(quote, greedy) { + if (greedy == null) { greedy = true; } + + function stringTokenizer(stream, state) { + var nextChar = stream.next(); + var peekChar = stream.peek(); + var previousChar = stream.string.charAt(stream.pos-2); + + var endingString = ((nextChar !== "\\" && peekChar === quote) || (nextChar === quote && previousChar !== "\\")); + + if (endingString) { + if (nextChar !== quote && greedy) { stream.next(); } + if (isEndLine(stream)) { + state.cursorHalf = 0; + } + state.tokenizer = tokenBase; + return "string"; + } else if (nextChar === "#" && peekChar === "{") { + state.tokenizer = buildInterpolationTokenizer(stringTokenizer); + stream.next(); + return "operator"; + } else { + return "string"; + } + } + + return stringTokenizer; + } + + function buildInterpolationTokenizer(currentTokenizer) { + return function(stream, state) { + if (stream.peek() === "}") { + stream.next(); + state.tokenizer = currentTokenizer; + return "operator"; + } else { + return tokenBase(stream, state); + } + }; + } + + function indent(state) { + if (state.indentCount == 0) { + state.indentCount++; + var lastScopeOffset = state.scopes[0].offset; + var currentOffset = lastScopeOffset + config.indentUnit; + state.scopes.unshift({ offset:currentOffset }); + } + } + + function dedent(state) { + if (state.scopes.length == 1) return; + + state.scopes.shift(); + } + + function tokenBase(stream, state) { + var ch = stream.peek(); + + // Comment + if (stream.match("/*")) { + state.tokenizer = comment(stream.indentation(), true); + return state.tokenizer(stream, state); + } + if (stream.match("//")) { + state.tokenizer = comment(stream.indentation(), false); + return state.tokenizer(stream, state); + } + + // Interpolation + if (stream.match("#{")) { + state.tokenizer = buildInterpolationTokenizer(tokenBase); + return "operator"; + } + + // Strings + if (ch === '"' || ch === "'") { + stream.next(); + state.tokenizer = buildStringTokenizer(ch); + return "string"; + } + + if(!state.cursorHalf){// state.cursorHalf === 0 + // first half i.e. before : for key-value pairs + // including selectors + + if (ch === "-") { + if (stream.match(/^-\w+-/)) { + return "meta"; + } + } + + if (ch === ".") { + stream.next(); + if (stream.match(/^[\w-]+/)) { + indent(state); + return "qualifier"; + } else if (stream.peek() === "#") { + indent(state); + return "tag"; + } + } + + if (ch === "#") { + stream.next(); + // ID selectors + if (stream.match(/^[\w-]+/)) { + indent(state); + return "builtin"; + } + if (stream.peek() === "#") { + indent(state); + return "tag"; + } + } + + // Variables + if (ch === "$") { + stream.next(); + stream.eatWhile(/[\w-]/); + return "variable-2"; + } + + // Numbers + if (stream.match(/^-?[0-9\.]+/)) + return "number"; + + // Units + if (stream.match(/^(px|em|in)\b/)) + return "unit"; + + if (stream.match(keywordsRegexp)) + return "keyword"; + + if (stream.match(/^url/) && stream.peek() === "(") { + state.tokenizer = urlTokens; + return "atom"; + } + + if (ch === "=") { + // Match shortcut mixin definition + if (stream.match(/^=[\w-]+/)) { + indent(state); + return "meta"; + } + } + + if (ch === "+") { + // Match shortcut mixin definition + if (stream.match(/^\+[\w-]+/)){ + return "variable-3"; + } + } + + if(ch === "@"){ + if(stream.match(/@extend/)){ + if(!stream.match(/\s*[\w]/)) + dedent(state); + } + } + + + // Indent Directives + if (stream.match(/^@(else if|if|media|else|for|each|while|mixin|function)/)) { + indent(state); + return "def"; + } + + // Other Directives + if (ch === "@") { + stream.next(); + stream.eatWhile(/[\w-]/); + return "def"; + } + + if (stream.eatWhile(/[\w-]/)){ + if(stream.match(/ *: *[\w-\+\$#!\("']/,false)){ + word = stream.current().toLowerCase(); + var prop = state.prevProp + "-" + word; + if (propertyKeywords.hasOwnProperty(prop)) { + return "property"; + } else if (propertyKeywords.hasOwnProperty(word)) { + state.prevProp = word; + return "property"; + } else if (fontProperties.hasOwnProperty(word)) { + return "property"; + } + return "tag"; + } + else if(stream.match(/ *:/,false)){ + indent(state); + state.cursorHalf = 1; + state.prevProp = stream.current().toLowerCase(); + return "property"; + } + else if(stream.match(/ *,/,false)){ + return "tag"; + } + else{ + indent(state); + return "tag"; + } + } + + if(ch === ":"){ + if (stream.match(pseudoElementsRegexp)){ // could be a pseudo-element + return "variable-3"; + } + stream.next(); + state.cursorHalf=1; + return "operator"; + } + + } // cursorHalf===0 ends here + else{ + + if (ch === "#") { + stream.next(); + // Hex numbers + if (stream.match(/[0-9a-fA-F]{6}|[0-9a-fA-F]{3}/)){ + if (isEndLine(stream)) { + state.cursorHalf = 0; + } + return "number"; + } + } + + // Numbers + if (stream.match(/^-?[0-9\.]+/)){ + if (isEndLine(stream)) { + state.cursorHalf = 0; + } + return "number"; + } + + // Units + if (stream.match(/^(px|em|in)\b/)){ + if (isEndLine(stream)) { + state.cursorHalf = 0; + } + return "unit"; + } + + if (stream.match(keywordsRegexp)){ + if (isEndLine(stream)) { + state.cursorHalf = 0; + } + return "keyword"; + } + + if (stream.match(/^url/) && stream.peek() === "(") { + state.tokenizer = urlTokens; + if (isEndLine(stream)) { + state.cursorHalf = 0; + } + return "atom"; + } + + // Variables + if (ch === "$") { + stream.next(); + stream.eatWhile(/[\w-]/); + if (isEndLine(stream)) { + state.cursorHalf = 0; + } + return "variable-2"; + } + + // bang character for !important, !default, etc. + if (ch === "!") { + stream.next(); + state.cursorHalf = 0; + return stream.match(/^[\w]+/) ? "keyword": "operator"; + } + + if (stream.match(opRegexp)){ + if (isEndLine(stream)) { + state.cursorHalf = 0; + } + return "operator"; + } + + // attributes + if (stream.eatWhile(/[\w-]/)) { + if (isEndLine(stream)) { + state.cursorHalf = 0; + } + word = stream.current().toLowerCase(); + if (valueKeywords.hasOwnProperty(word)) { + return "atom"; + } else if (colorKeywords.hasOwnProperty(word)) { + return "keyword"; + } else if (propertyKeywords.hasOwnProperty(word)) { + state.prevProp = stream.current().toLowerCase(); + return "property"; + } else { + return "tag"; + } + } + + //stream.eatSpace(); + if (isEndLine(stream)) { + state.cursorHalf = 0; + return null; + } + + } // else ends here + + if (stream.match(opRegexp)) + return "operator"; + + // If we haven't returned by now, we move 1 character + // and return an error + stream.next(); + return null; + } + + function tokenLexer(stream, state) { + if (stream.sol()) state.indentCount = 0; + var style = state.tokenizer(stream, state); + var current = stream.current(); + + if (current === "@return" || current === "}"){ + dedent(state); + } + + if (style !== null) { + var startOfToken = stream.pos - current.length; + + var withCurrentIndent = startOfToken + (config.indentUnit * state.indentCount); + + var newScopes = []; + + for (var i = 0; i < state.scopes.length; i++) { + var scope = state.scopes[i]; + + if (scope.offset <= withCurrentIndent) + newScopes.push(scope); + } + + state.scopes = newScopes; + } + + + return style; + } + + return { + startState: function() { + return { + tokenizer: tokenBase, + scopes: [{offset: 0, type: "sass"}], + indentCount: 0, + cursorHalf: 0, // cursor half tells us if cursor lies after (1) + // or before (0) colon (well... more or less) + definedVars: [], + definedMixins: [] + }; + }, + token: function(stream, state) { + var style = tokenLexer(stream, state); + + state.lastToken = { style: style, content: stream.current() }; + + return style; + }, + + indent: function(state) { + return state.scopes[0].offset; + } + }; +}, "css"); + +CodeMirror.defineMIME("text/x-sass", "sass"); + +}); diff --git a/docs/js/node_modules/codemirror/mode/scheme/scheme.js b/docs/js/node_modules/codemirror/mode/scheme/scheme.js new file mode 100644 index 000000000..56e4e332e --- /dev/null +++ b/docs/js/node_modules/codemirror/mode/scheme/scheme.js @@ -0,0 +1,265 @@ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: https://codemirror.net/LICENSE + +/** + * Author: Koh Zi Han, based on implementation by Koh Zi Chun + */ + +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + +CodeMirror.defineMode("scheme", function () { + var BUILTIN = "builtin", COMMENT = "comment", STRING = "string", + ATOM = "atom", NUMBER = "number", BRACKET = "bracket"; + var INDENT_WORD_SKIP = 2; + + function makeKeywords(str) { + var obj = {}, words = str.split(" "); + for (var i = 0; i < words.length; ++i) obj[words[i]] = true; + return obj; + } + + var keywords = makeKeywords("λ case-lambda call/cc class define-class exit-handler field import inherit init-field interface let*-values let-values let/ec mixin opt-lambda override protect provide public rename require require-for-syntax syntax syntax-case syntax-error unit/sig unless when with-syntax and begin call-with-current-continuation call-with-input-file call-with-output-file case cond define define-syntax delay do dynamic-wind else for-each if lambda let let* let-syntax letrec letrec-syntax map or syntax-rules abs acos angle append apply asin assoc assq assv atan boolean? caar cadr call-with-input-file call-with-output-file call-with-values car cdddar cddddr cdr ceiling char->integer char-alphabetic? char-ci<=? char-ci=? char-ci>? char-downcase char-lower-case? char-numeric? char-ready? char-upcase char-upper-case? char-whitespace? char<=? char=? char>? char? close-input-port close-output-port complex? cons cos current-input-port current-output-port denominator display eof-object? eq? equal? eqv? eval even? exact->inexact exact? exp expt #f floor force gcd imag-part inexact->exact inexact? input-port? integer->char integer? interaction-environment lcm length list list->string list->vector list-ref list-tail list? load log magnitude make-polar make-rectangular make-string make-vector max member memq memv min modulo negative? newline not null-environment null? number->string number? numerator odd? open-input-file open-output-file output-port? pair? peek-char port? positive? procedure? quasiquote quote quotient rational? rationalize read read-char real-part real? remainder reverse round scheme-report-environment set! set-car! set-cdr! sin sqrt string string->list string->number string->symbol string-append string-ci<=? string-ci=? string-ci>? string-copy string-fill! string-length string-ref string-set! string<=? string=? string>? string? substring symbol->string symbol? #t tan transcript-off transcript-on truncate values vector vector->list vector-fill! vector-length vector-ref vector-set! with-input-from-file with-output-to-file write write-char zero?"); + var indentKeys = makeKeywords("define let letrec let* lambda"); + + function stateStack(indent, type, prev) { // represents a state stack object + this.indent = indent; + this.type = type; + this.prev = prev; + } + + function pushStack(state, indent, type) { + state.indentStack = new stateStack(indent, type, state.indentStack); + } + + function popStack(state) { + state.indentStack = state.indentStack.prev; + } + + var binaryMatcher = new RegExp(/^(?:[-+]i|[-+][01]+#*(?:\/[01]+#*)?i|[-+]?[01]+#*(?:\/[01]+#*)?@[-+]?[01]+#*(?:\/[01]+#*)?|[-+]?[01]+#*(?:\/[01]+#*)?[-+](?:[01]+#*(?:\/[01]+#*)?)?i|[-+]?[01]+#*(?:\/[01]+#*)?)(?=[()\s;"]|$)/i); + var octalMatcher = new RegExp(/^(?:[-+]i|[-+][0-7]+#*(?:\/[0-7]+#*)?i|[-+]?[0-7]+#*(?:\/[0-7]+#*)?@[-+]?[0-7]+#*(?:\/[0-7]+#*)?|[-+]?[0-7]+#*(?:\/[0-7]+#*)?[-+](?:[0-7]+#*(?:\/[0-7]+#*)?)?i|[-+]?[0-7]+#*(?:\/[0-7]+#*)?)(?=[()\s;"]|$)/i); + var hexMatcher = new RegExp(/^(?:[-+]i|[-+][\da-f]+#*(?:\/[\da-f]+#*)?i|[-+]?[\da-f]+#*(?:\/[\da-f]+#*)?@[-+]?[\da-f]+#*(?:\/[\da-f]+#*)?|[-+]?[\da-f]+#*(?:\/[\da-f]+#*)?[-+](?:[\da-f]+#*(?:\/[\da-f]+#*)?)?i|[-+]?[\da-f]+#*(?:\/[\da-f]+#*)?)(?=[()\s;"]|$)/i); + var decimalMatcher = new RegExp(/^(?:[-+]i|[-+](?:(?:(?:\d+#+\.?#*|\d+\.\d*#*|\.\d+#*|\d+)(?:[esfdl][-+]?\d+)?)|\d+#*\/\d+#*)i|[-+]?(?:(?:(?:\d+#+\.?#*|\d+\.\d*#*|\.\d+#*|\d+)(?:[esfdl][-+]?\d+)?)|\d+#*\/\d+#*)@[-+]?(?:(?:(?:\d+#+\.?#*|\d+\.\d*#*|\.\d+#*|\d+)(?:[esfdl][-+]?\d+)?)|\d+#*\/\d+#*)|[-+]?(?:(?:(?:\d+#+\.?#*|\d+\.\d*#*|\.\d+#*|\d+)(?:[esfdl][-+]?\d+)?)|\d+#*\/\d+#*)[-+](?:(?:(?:\d+#+\.?#*|\d+\.\d*#*|\.\d+#*|\d+)(?:[esfdl][-+]?\d+)?)|\d+#*\/\d+#*)?i|(?:(?:(?:\d+#+\.?#*|\d+\.\d*#*|\.\d+#*|\d+)(?:[esfdl][-+]?\d+)?)|\d+#*\/\d+#*))(?=[()\s;"]|$)/i); + + function isBinaryNumber (stream) { + return stream.match(binaryMatcher); + } + + function isOctalNumber (stream) { + return stream.match(octalMatcher); + } + + function isDecimalNumber (stream, backup) { + if (backup === true) { + stream.backUp(1); + } + return stream.match(decimalMatcher); + } + + function isHexNumber (stream) { + return stream.match(hexMatcher); + } + + return { + startState: function () { + return { + indentStack: null, + indentation: 0, + mode: false, + sExprComment: false, + sExprQuote: false + }; + }, + + token: function (stream, state) { + if (state.indentStack == null && stream.sol()) { + // update indentation, but only if indentStack is empty + state.indentation = stream.indentation(); + } + + // skip spaces + if (stream.eatSpace()) { + return null; + } + var returnType = null; + + switch(state.mode){ + case "string": // multi-line string parsing mode + var next, escaped = false; + while ((next = stream.next()) != null) { + if (next == "\"" && !escaped) { + + state.mode = false; + break; + } + escaped = !escaped && next == "\\"; + } + returnType = STRING; // continue on in scheme-string mode + break; + case "comment": // comment parsing mode + var next, maybeEnd = false; + while ((next = stream.next()) != null) { + if (next == "#" && maybeEnd) { + + state.mode = false; + break; + } + maybeEnd = (next == "|"); + } + returnType = COMMENT; + break; + case "s-expr-comment": // s-expr commenting mode + state.mode = false; + if(stream.peek() == "(" || stream.peek() == "["){ + // actually start scheme s-expr commenting mode + state.sExprComment = 0; + }else{ + // if not we just comment the entire of the next token + stream.eatWhile(/[^\s\(\)\[\]]/); // eat symbol atom + returnType = COMMENT; + break; + } + default: // default parsing mode + var ch = stream.next(); + + if (ch == "\"") { + state.mode = "string"; + returnType = STRING; + + } else if (ch == "'") { + if (stream.peek() == "(" || stream.peek() == "["){ + if (typeof state.sExprQuote != "number") { + state.sExprQuote = 0; + } // else already in a quoted expression + returnType = ATOM; + } else { + stream.eatWhile(/[\w_\-!$%&*+\.\/:<=>?@\^~]/); + returnType = ATOM; + } + } else if (ch == '#') { + if (stream.eat("|")) { // Multi-line comment + state.mode = "comment"; // toggle to comment mode + returnType = COMMENT; + } else if (stream.eat(/[tf]/i)) { // #t/#f (atom) + returnType = ATOM; + } else if (stream.eat(';')) { // S-Expr comment + state.mode = "s-expr-comment"; + returnType = COMMENT; + } else { + var numTest = null, hasExactness = false, hasRadix = true; + if (stream.eat(/[ei]/i)) { + hasExactness = true; + } else { + stream.backUp(1); // must be radix specifier + } + if (stream.match(/^#b/i)) { + numTest = isBinaryNumber; + } else if (stream.match(/^#o/i)) { + numTest = isOctalNumber; + } else if (stream.match(/^#x/i)) { + numTest = isHexNumber; + } else if (stream.match(/^#d/i)) { + numTest = isDecimalNumber; + } else if (stream.match(/^[-+0-9.]/, false)) { + hasRadix = false; + numTest = isDecimalNumber; + // re-consume the intial # if all matches failed + } else if (!hasExactness) { + stream.eat('#'); + } + if (numTest != null) { + if (hasRadix && !hasExactness) { + // consume optional exactness after radix + stream.match(/^#[ei]/i); + } + if (numTest(stream)) + returnType = NUMBER; + } + } + } else if (/^[-+0-9.]/.test(ch) && isDecimalNumber(stream, true)) { // match non-prefixed number, must be decimal + returnType = NUMBER; + } else if (ch == ";") { // comment + stream.skipToEnd(); // rest of the line is a comment + returnType = COMMENT; + } else if (ch == "(" || ch == "[") { + var keyWord = ''; var indentTemp = stream.column(), letter; + /** + Either + (indent-word .. + (non-indent-word .. + (;something else, bracket, etc. + */ + + while ((letter = stream.eat(/[^\s\(\[\;\)\]]/)) != null) { + keyWord += letter; + } + + if (keyWord.length > 0 && indentKeys.propertyIsEnumerable(keyWord)) { // indent-word + + pushStack(state, indentTemp + INDENT_WORD_SKIP, ch); + } else { // non-indent word + // we continue eating the spaces + stream.eatSpace(); + if (stream.eol() || stream.peek() == ";") { + // nothing significant after + // we restart indentation 1 space after + pushStack(state, indentTemp + 1, ch); + } else { + pushStack(state, indentTemp + stream.current().length, ch); // else we match + } + } + stream.backUp(stream.current().length - 1); // undo all the eating + + if(typeof state.sExprComment == "number") state.sExprComment++; + if(typeof state.sExprQuote == "number") state.sExprQuote++; + + returnType = BRACKET; + } else if (ch == ")" || ch == "]") { + returnType = BRACKET; + if (state.indentStack != null && state.indentStack.type == (ch == ")" ? "(" : "[")) { + popStack(state); + + if(typeof state.sExprComment == "number"){ + if(--state.sExprComment == 0){ + returnType = COMMENT; // final closing bracket + state.sExprComment = false; // turn off s-expr commenting mode + } + } + if(typeof state.sExprQuote == "number"){ + if(--state.sExprQuote == 0){ + returnType = ATOM; // final closing bracket + state.sExprQuote = false; // turn off s-expr quote mode + } + } + } + } else { + stream.eatWhile(/[\w_\-!$%&*+\.\/:<=>?@\^~]/); + + if (keywords && keywords.propertyIsEnumerable(stream.current())) { + returnType = BUILTIN; + } else returnType = "variable"; + } + } + return (typeof state.sExprComment == "number") ? COMMENT : ((typeof state.sExprQuote == "number") ? ATOM : returnType); + }, + + indent: function (state) { + if (state.indentStack == null) return state.indentation; + return state.indentStack.indent; + }, + + closeBrackets: {pairs: "()[]{}\"\""}, + lineComment: ";;" + }; +}); + +CodeMirror.defineMIME("text/x-scheme", "scheme"); + +}); diff --git a/docs/js/node_modules/codemirror/mode/shell/shell.js b/docs/js/node_modules/codemirror/mode/shell/shell.js new file mode 100644 index 000000000..5af12413b --- /dev/null +++ b/docs/js/node_modules/codemirror/mode/shell/shell.js @@ -0,0 +1,152 @@ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: https://codemirror.net/LICENSE + +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + +CodeMirror.defineMode('shell', function() { + + var words = {}; + function define(style, dict) { + for(var i = 0; i < dict.length; i++) { + words[dict[i]] = style; + } + }; + + var commonAtoms = ["true", "false"]; + var commonKeywords = ["if", "then", "do", "else", "elif", "while", "until", "for", "in", "esac", "fi", + "fin", "fil", "done", "exit", "set", "unset", "export", "function"]; + var commonCommands = ["ab", "awk", "bash", "beep", "cat", "cc", "cd", "chown", "chmod", "chroot", "clear", + "cp", "curl", "cut", "diff", "echo", "find", "gawk", "gcc", "get", "git", "grep", "hg", "kill", "killall", + "ln", "ls", "make", "mkdir", "openssl", "mv", "nc", "nl", "node", "npm", "ping", "ps", "restart", "rm", + "rmdir", "sed", "service", "sh", "shopt", "shred", "source", "sort", "sleep", "ssh", "start", "stop", + "su", "sudo", "svn", "tee", "telnet", "top", "touch", "vi", "vim", "wall", "wc", "wget", "who", "write", + "yes", "zsh"]; + + CodeMirror.registerHelper("hintWords", "shell", commonAtoms.concat(commonKeywords, commonCommands)); + + define('atom', commonAtoms); + define('keyword', commonKeywords); + define('builtin', commonCommands); + + function tokenBase(stream, state) { + if (stream.eatSpace()) return null; + + var sol = stream.sol(); + var ch = stream.next(); + + if (ch === '\\') { + stream.next(); + return null; + } + if (ch === '\'' || ch === '"' || ch === '`') { + state.tokens.unshift(tokenString(ch, ch === "`" ? "quote" : "string")); + return tokenize(stream, state); + } + if (ch === '#') { + if (sol && stream.eat('!')) { + stream.skipToEnd(); + return 'meta'; // 'comment'? + } + stream.skipToEnd(); + return 'comment'; + } + if (ch === '$') { + state.tokens.unshift(tokenDollar); + return tokenize(stream, state); + } + if (ch === '+' || ch === '=') { + return 'operator'; + } + if (ch === '-') { + stream.eat('-'); + stream.eatWhile(/\w/); + return 'attribute'; + } + if (/\d/.test(ch)) { + stream.eatWhile(/\d/); + if(stream.eol() || !/\w/.test(stream.peek())) { + return 'number'; + } + } + stream.eatWhile(/[\w-]/); + var cur = stream.current(); + if (stream.peek() === '=' && /\w+/.test(cur)) return 'def'; + return words.hasOwnProperty(cur) ? words[cur] : null; + } + + function tokenString(quote, style) { + var close = quote == "(" ? ")" : quote == "{" ? "}" : quote + return function(stream, state) { + var next, escaped = false; + while ((next = stream.next()) != null) { + if (next === close && !escaped) { + state.tokens.shift(); + break; + } else if (next === '$' && !escaped && quote !== "'" && stream.peek() != close) { + escaped = true; + stream.backUp(1); + state.tokens.unshift(tokenDollar); + break; + } else if (!escaped && quote !== close && next === quote) { + state.tokens.unshift(tokenString(quote, style)) + return tokenize(stream, state) + } else if (!escaped && /['"]/.test(next) && !/['"]/.test(quote)) { + state.tokens.unshift(tokenStringStart(next, "string")); + stream.backUp(1); + break; + } + escaped = !escaped && next === '\\'; + } + return style; + }; + }; + + function tokenStringStart(quote, style) { + return function(stream, state) { + state.tokens[0] = tokenString(quote, style) + stream.next() + return tokenize(stream, state) + } + } + + var tokenDollar = function(stream, state) { + if (state.tokens.length > 1) stream.eat('$'); + var ch = stream.next() + if (/['"({]/.test(ch)) { + state.tokens[0] = tokenString(ch, ch == "(" ? "quote" : ch == "{" ? "def" : "string"); + return tokenize(stream, state); + } + if (!/\d/.test(ch)) stream.eatWhile(/\w/); + state.tokens.shift(); + return 'def'; + }; + + function tokenize(stream, state) { + return (state.tokens[0] || tokenBase) (stream, state); + }; + + return { + startState: function() {return {tokens:[]};}, + token: function(stream, state) { + return tokenize(stream, state); + }, + closeBrackets: "()[]{}''\"\"``", + lineComment: '#', + fold: "brace" + }; +}); + +CodeMirror.defineMIME('text/x-sh', 'shell'); +// Apache uses a slightly different Media Type for Shell scripts +// http://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types +CodeMirror.defineMIME('application/x-sh', 'shell'); + +}); diff --git a/docs/js/node_modules/codemirror/mode/sieve/sieve.js b/docs/js/node_modules/codemirror/mode/sieve/sieve.js new file mode 100644 index 000000000..f02a867e7 --- /dev/null +++ b/docs/js/node_modules/codemirror/mode/sieve/sieve.js @@ -0,0 +1,193 @@ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: https://codemirror.net/LICENSE + +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + +CodeMirror.defineMode("sieve", function(config) { + function words(str) { + var obj = {}, words = str.split(" "); + for (var i = 0; i < words.length; ++i) obj[words[i]] = true; + return obj; + } + + var keywords = words("if elsif else stop require"); + var atoms = words("true false not"); + var indentUnit = config.indentUnit; + + function tokenBase(stream, state) { + + var ch = stream.next(); + if (ch == "/" && stream.eat("*")) { + state.tokenize = tokenCComment; + return tokenCComment(stream, state); + } + + if (ch === '#') { + stream.skipToEnd(); + return "comment"; + } + + if (ch == "\"") { + state.tokenize = tokenString(ch); + return state.tokenize(stream, state); + } + + if (ch == "(") { + state._indent.push("("); + // add virtual angel wings so that editor behaves... + // ...more sane incase of broken brackets + state._indent.push("{"); + return null; + } + + if (ch === "{") { + state._indent.push("{"); + return null; + } + + if (ch == ")") { + state._indent.pop(); + state._indent.pop(); + } + + if (ch === "}") { + state._indent.pop(); + return null; + } + + if (ch == ",") + return null; + + if (ch == ";") + return null; + + + if (/[{}\(\),;]/.test(ch)) + return null; + + // 1*DIGIT "K" / "M" / "G" + if (/\d/.test(ch)) { + stream.eatWhile(/[\d]/); + stream.eat(/[KkMmGg]/); + return "number"; + } + + // ":" (ALPHA / "_") *(ALPHA / DIGIT / "_") + if (ch == ":") { + stream.eatWhile(/[a-zA-Z_]/); + stream.eatWhile(/[a-zA-Z0-9_]/); + + return "operator"; + } + + stream.eatWhile(/\w/); + var cur = stream.current(); + + // "text:" *(SP / HTAB) (hash-comment / CRLF) + // *(multiline-literal / multiline-dotstart) + // "." CRLF + if ((cur == "text") && stream.eat(":")) + { + state.tokenize = tokenMultiLineString; + return "string"; + } + + if (keywords.propertyIsEnumerable(cur)) + return "keyword"; + + if (atoms.propertyIsEnumerable(cur)) + return "atom"; + + return null; + } + + function tokenMultiLineString(stream, state) + { + state._multiLineString = true; + // the first line is special it may contain a comment + if (!stream.sol()) { + stream.eatSpace(); + + if (stream.peek() == "#") { + stream.skipToEnd(); + return "comment"; + } + + stream.skipToEnd(); + return "string"; + } + + if ((stream.next() == ".") && (stream.eol())) + { + state._multiLineString = false; + state.tokenize = tokenBase; + } + + return "string"; + } + + function tokenCComment(stream, state) { + var maybeEnd = false, ch; + while ((ch = stream.next()) != null) { + if (maybeEnd && ch == "/") { + state.tokenize = tokenBase; + break; + } + maybeEnd = (ch == "*"); + } + return "comment"; + } + + function tokenString(quote) { + return function(stream, state) { + var escaped = false, ch; + while ((ch = stream.next()) != null) { + if (ch == quote && !escaped) + break; + escaped = !escaped && ch == "\\"; + } + if (!escaped) state.tokenize = tokenBase; + return "string"; + }; + } + + return { + startState: function(base) { + return {tokenize: tokenBase, + baseIndent: base || 0, + _indent: []}; + }, + + token: function(stream, state) { + if (stream.eatSpace()) + return null; + + return (state.tokenize || tokenBase)(stream, state); + }, + + indent: function(state, _textAfter) { + var length = state._indent.length; + if (_textAfter && (_textAfter[0] == "}")) + length--; + + if (length <0) + length = 0; + + return length * indentUnit; + }, + + electricChars: "}" + }; +}); + +CodeMirror.defineMIME("application/sieve", "sieve"); + +}); diff --git a/docs/js/node_modules/codemirror/mode/slim/slim.js b/docs/js/node_modules/codemirror/mode/slim/slim.js new file mode 100644 index 000000000..b8ccb1381 --- /dev/null +++ b/docs/js/node_modules/codemirror/mode/slim/slim.js @@ -0,0 +1,575 @@ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: https://codemirror.net/LICENSE + +// Slim Highlighting for CodeMirror copyright (c) HicknHack Software Gmbh + +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror"), require("../htmlmixed/htmlmixed"), require("../ruby/ruby")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror", "../htmlmixed/htmlmixed", "../ruby/ruby"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + + CodeMirror.defineMode("slim", function(config) { + var htmlMode = CodeMirror.getMode(config, {name: "htmlmixed"}); + var rubyMode = CodeMirror.getMode(config, "ruby"); + var modes = { html: htmlMode, ruby: rubyMode }; + var embedded = { + ruby: "ruby", + javascript: "javascript", + css: "text/css", + sass: "text/x-sass", + scss: "text/x-scss", + less: "text/x-less", + styl: "text/x-styl", // no highlighting so far + coffee: "coffeescript", + asciidoc: "text/x-asciidoc", + markdown: "text/x-markdown", + textile: "text/x-textile", // no highlighting so far + creole: "text/x-creole", // no highlighting so far + wiki: "text/x-wiki", // no highlighting so far + mediawiki: "text/x-mediawiki", // no highlighting so far + rdoc: "text/x-rdoc", // no highlighting so far + builder: "text/x-builder", // no highlighting so far + nokogiri: "text/x-nokogiri", // no highlighting so far + erb: "application/x-erb" + }; + var embeddedRegexp = function(map){ + var arr = []; + for(var key in map) arr.push(key); + return new RegExp("^("+arr.join('|')+"):"); + }(embedded); + + var styleMap = { + "commentLine": "comment", + "slimSwitch": "operator special", + "slimTag": "tag", + "slimId": "attribute def", + "slimClass": "attribute qualifier", + "slimAttribute": "attribute", + "slimSubmode": "keyword special", + "closeAttributeTag": null, + "slimDoctype": null, + "lineContinuation": null + }; + var closing = { + "{": "}", + "[": "]", + "(": ")" + }; + + var nameStartChar = "_a-zA-Z\xC0-\xD6\xD8-\xF6\xF8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD"; + var nameChar = nameStartChar + "\\-0-9\xB7\u0300-\u036F\u203F-\u2040"; + var nameRegexp = new RegExp("^[:"+nameStartChar+"](?::["+nameChar+"]|["+nameChar+"]*)"); + var attributeNameRegexp = new RegExp("^[:"+nameStartChar+"][:\\."+nameChar+"]*(?=\\s*=)"); + var wrappedAttributeNameRegexp = new RegExp("^[:"+nameStartChar+"][:\\."+nameChar+"]*"); + var classNameRegexp = /^\.-?[_a-zA-Z]+[\w\-]*/; + var classIdRegexp = /^#[_a-zA-Z]+[\w\-]*/; + + function backup(pos, tokenize, style) { + var restore = function(stream, state) { + state.tokenize = tokenize; + if (stream.pos < pos) { + stream.pos = pos; + return style; + } + return state.tokenize(stream, state); + }; + return function(stream, state) { + state.tokenize = restore; + return tokenize(stream, state); + }; + } + + function maybeBackup(stream, state, pat, offset, style) { + var cur = stream.current(); + var idx = cur.search(pat); + if (idx > -1) { + state.tokenize = backup(stream.pos, state.tokenize, style); + stream.backUp(cur.length - idx - offset); + } + return style; + } + + function continueLine(state, column) { + state.stack = { + parent: state.stack, + style: "continuation", + indented: column, + tokenize: state.line + }; + state.line = state.tokenize; + } + function finishContinue(state) { + if (state.line == state.tokenize) { + state.line = state.stack.tokenize; + state.stack = state.stack.parent; + } + } + + function lineContinuable(column, tokenize) { + return function(stream, state) { + finishContinue(state); + if (stream.match(/^\\$/)) { + continueLine(state, column); + return "lineContinuation"; + } + var style = tokenize(stream, state); + if (stream.eol() && stream.current().match(/(?:^|[^\\])(?:\\\\)*\\$/)) { + stream.backUp(1); + } + return style; + }; + } + function commaContinuable(column, tokenize) { + return function(stream, state) { + finishContinue(state); + var style = tokenize(stream, state); + if (stream.eol() && stream.current().match(/,$/)) { + continueLine(state, column); + } + return style; + }; + } + + function rubyInQuote(endQuote, tokenize) { + // TODO: add multi line support + return function(stream, state) { + var ch = stream.peek(); + if (ch == endQuote && state.rubyState.tokenize.length == 1) { + // step out of ruby context as it seems to complete processing all the braces + stream.next(); + state.tokenize = tokenize; + return "closeAttributeTag"; + } else { + return ruby(stream, state); + } + }; + } + function startRubySplat(tokenize) { + var rubyState; + var runSplat = function(stream, state) { + if (state.rubyState.tokenize.length == 1 && !state.rubyState.context.prev) { + stream.backUp(1); + if (stream.eatSpace()) { + state.rubyState = rubyState; + state.tokenize = tokenize; + return tokenize(stream, state); + } + stream.next(); + } + return ruby(stream, state); + }; + return function(stream, state) { + rubyState = state.rubyState; + state.rubyState = CodeMirror.startState(rubyMode); + state.tokenize = runSplat; + return ruby(stream, state); + }; + } + + function ruby(stream, state) { + return rubyMode.token(stream, state.rubyState); + } + + function htmlLine(stream, state) { + if (stream.match(/^\\$/)) { + return "lineContinuation"; + } + return html(stream, state); + } + function html(stream, state) { + if (stream.match(/^#\{/)) { + state.tokenize = rubyInQuote("}", state.tokenize); + return null; + } + return maybeBackup(stream, state, /[^\\]#\{/, 1, htmlMode.token(stream, state.htmlState)); + } + + function startHtmlLine(lastTokenize) { + return function(stream, state) { + var style = htmlLine(stream, state); + if (stream.eol()) state.tokenize = lastTokenize; + return style; + }; + } + + function startHtmlMode(stream, state, offset) { + state.stack = { + parent: state.stack, + style: "html", + indented: stream.column() + offset, // pipe + space + tokenize: state.line + }; + state.line = state.tokenize = html; + return null; + } + + function comment(stream, state) { + stream.skipToEnd(); + return state.stack.style; + } + + function commentMode(stream, state) { + state.stack = { + parent: state.stack, + style: "comment", + indented: state.indented + 1, + tokenize: state.line + }; + state.line = comment; + return comment(stream, state); + } + + function attributeWrapper(stream, state) { + if (stream.eat(state.stack.endQuote)) { + state.line = state.stack.line; + state.tokenize = state.stack.tokenize; + state.stack = state.stack.parent; + return null; + } + if (stream.match(wrappedAttributeNameRegexp)) { + state.tokenize = attributeWrapperAssign; + return "slimAttribute"; + } + stream.next(); + return null; + } + function attributeWrapperAssign(stream, state) { + if (stream.match(/^==?/)) { + state.tokenize = attributeWrapperValue; + return null; + } + return attributeWrapper(stream, state); + } + function attributeWrapperValue(stream, state) { + var ch = stream.peek(); + if (ch == '"' || ch == "\'") { + state.tokenize = readQuoted(ch, "string", true, false, attributeWrapper); + stream.next(); + return state.tokenize(stream, state); + } + if (ch == '[') { + return startRubySplat(attributeWrapper)(stream, state); + } + if (stream.match(/^(true|false|nil)\b/)) { + state.tokenize = attributeWrapper; + return "keyword"; + } + return startRubySplat(attributeWrapper)(stream, state); + } + + function startAttributeWrapperMode(state, endQuote, tokenize) { + state.stack = { + parent: state.stack, + style: "wrapper", + indented: state.indented + 1, + tokenize: tokenize, + line: state.line, + endQuote: endQuote + }; + state.line = state.tokenize = attributeWrapper; + return null; + } + + function sub(stream, state) { + if (stream.match(/^#\{/)) { + state.tokenize = rubyInQuote("}", state.tokenize); + return null; + } + var subStream = new CodeMirror.StringStream(stream.string.slice(state.stack.indented), stream.tabSize); + subStream.pos = stream.pos - state.stack.indented; + subStream.start = stream.start - state.stack.indented; + subStream.lastColumnPos = stream.lastColumnPos - state.stack.indented; + subStream.lastColumnValue = stream.lastColumnValue - state.stack.indented; + var style = state.subMode.token(subStream, state.subState); + stream.pos = subStream.pos + state.stack.indented; + return style; + } + function firstSub(stream, state) { + state.stack.indented = stream.column(); + state.line = state.tokenize = sub; + return state.tokenize(stream, state); + } + + function createMode(mode) { + var query = embedded[mode]; + var spec = CodeMirror.mimeModes[query]; + if (spec) { + return CodeMirror.getMode(config, spec); + } + var factory = CodeMirror.modes[query]; + if (factory) { + return factory(config, {name: query}); + } + return CodeMirror.getMode(config, "null"); + } + + function getMode(mode) { + if (!modes.hasOwnProperty(mode)) { + return modes[mode] = createMode(mode); + } + return modes[mode]; + } + + function startSubMode(mode, state) { + var subMode = getMode(mode); + var subState = CodeMirror.startState(subMode); + + state.subMode = subMode; + state.subState = subState; + + state.stack = { + parent: state.stack, + style: "sub", + indented: state.indented + 1, + tokenize: state.line + }; + state.line = state.tokenize = firstSub; + return "slimSubmode"; + } + + function doctypeLine(stream, _state) { + stream.skipToEnd(); + return "slimDoctype"; + } + + function startLine(stream, state) { + var ch = stream.peek(); + if (ch == '<') { + return (state.tokenize = startHtmlLine(state.tokenize))(stream, state); + } + if (stream.match(/^[|']/)) { + return startHtmlMode(stream, state, 1); + } + if (stream.match(/^\/(!|\[\w+])?/)) { + return commentMode(stream, state); + } + if (stream.match(/^(-|==?[<>]?)/)) { + state.tokenize = lineContinuable(stream.column(), commaContinuable(stream.column(), ruby)); + return "slimSwitch"; + } + if (stream.match(/^doctype\b/)) { + state.tokenize = doctypeLine; + return "keyword"; + } + + var m = stream.match(embeddedRegexp); + if (m) { + return startSubMode(m[1], state); + } + + return slimTag(stream, state); + } + + function slim(stream, state) { + if (state.startOfLine) { + return startLine(stream, state); + } + return slimTag(stream, state); + } + + function slimTag(stream, state) { + if (stream.eat('*')) { + state.tokenize = startRubySplat(slimTagExtras); + return null; + } + if (stream.match(nameRegexp)) { + state.tokenize = slimTagExtras; + return "slimTag"; + } + return slimClass(stream, state); + } + function slimTagExtras(stream, state) { + if (stream.match(/^(<>?|> state.indented && state.last != "slimSubmode") { + state.line = state.tokenize = state.stack.tokenize; + state.stack = state.stack.parent; + state.subMode = null; + state.subState = null; + } + } + if (stream.eatSpace()) return null; + var style = state.tokenize(stream, state); + state.startOfLine = false; + if (style) state.last = style; + return styleMap.hasOwnProperty(style) ? styleMap[style] : style; + }, + + blankLine: function(state) { + if (state.subMode && state.subMode.blankLine) { + return state.subMode.blankLine(state.subState); + } + }, + + innerMode: function(state) { + if (state.subMode) return {state: state.subState, mode: state.subMode}; + return {state: state, mode: mode}; + } + + //indent: function(state) { + // return state.indented; + //} + }; + return mode; + }, "htmlmixed", "ruby"); + + CodeMirror.defineMIME("text/x-slim", "slim"); + CodeMirror.defineMIME("application/x-slim", "slim"); +}); diff --git a/docs/js/node_modules/codemirror/mode/smalltalk/smalltalk.js b/docs/js/node_modules/codemirror/mode/smalltalk/smalltalk.js new file mode 100644 index 000000000..5039fe2d1 --- /dev/null +++ b/docs/js/node_modules/codemirror/mode/smalltalk/smalltalk.js @@ -0,0 +1,168 @@ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: https://codemirror.net/LICENSE + +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + +CodeMirror.defineMode('smalltalk', function(config) { + + var specialChars = /[+\-\/\\*~<>=@%|&?!.,:;^]/; + var keywords = /true|false|nil|self|super|thisContext/; + + var Context = function(tokenizer, parent) { + this.next = tokenizer; + this.parent = parent; + }; + + var Token = function(name, context, eos) { + this.name = name; + this.context = context; + this.eos = eos; + }; + + var State = function() { + this.context = new Context(next, null); + this.expectVariable = true; + this.indentation = 0; + this.userIndentationDelta = 0; + }; + + State.prototype.userIndent = function(indentation) { + this.userIndentationDelta = indentation > 0 ? (indentation / config.indentUnit - this.indentation) : 0; + }; + + var next = function(stream, context, state) { + var token = new Token(null, context, false); + var aChar = stream.next(); + + if (aChar === '"') { + token = nextComment(stream, new Context(nextComment, context)); + + } else if (aChar === '\'') { + token = nextString(stream, new Context(nextString, context)); + + } else if (aChar === '#') { + if (stream.peek() === '\'') { + stream.next(); + token = nextSymbol(stream, new Context(nextSymbol, context)); + } else { + if (stream.eatWhile(/[^\s.{}\[\]()]/)) + token.name = 'string-2'; + else + token.name = 'meta'; + } + + } else if (aChar === '$') { + if (stream.next() === '<') { + stream.eatWhile(/[^\s>]/); + stream.next(); + } + token.name = 'string-2'; + + } else if (aChar === '|' && state.expectVariable) { + token.context = new Context(nextTemporaries, context); + + } else if (/[\[\]{}()]/.test(aChar)) { + token.name = 'bracket'; + token.eos = /[\[{(]/.test(aChar); + + if (aChar === '[') { + state.indentation++; + } else if (aChar === ']') { + state.indentation = Math.max(0, state.indentation - 1); + } + + } else if (specialChars.test(aChar)) { + stream.eatWhile(specialChars); + token.name = 'operator'; + token.eos = aChar !== ';'; // ; cascaded message expression + + } else if (/\d/.test(aChar)) { + stream.eatWhile(/[\w\d]/); + token.name = 'number'; + + } else if (/[\w_]/.test(aChar)) { + stream.eatWhile(/[\w\d_]/); + token.name = state.expectVariable ? (keywords.test(stream.current()) ? 'keyword' : 'variable') : null; + + } else { + token.eos = state.expectVariable; + } + + return token; + }; + + var nextComment = function(stream, context) { + stream.eatWhile(/[^"]/); + return new Token('comment', stream.eat('"') ? context.parent : context, true); + }; + + var nextString = function(stream, context) { + stream.eatWhile(/[^']/); + return new Token('string', stream.eat('\'') ? context.parent : context, false); + }; + + var nextSymbol = function(stream, context) { + stream.eatWhile(/[^']/); + return new Token('string-2', stream.eat('\'') ? context.parent : context, false); + }; + + var nextTemporaries = function(stream, context) { + var token = new Token(null, context, false); + var aChar = stream.next(); + + if (aChar === '|') { + token.context = context.parent; + token.eos = true; + + } else { + stream.eatWhile(/[^|]/); + token.name = 'variable'; + } + + return token; + }; + + return { + startState: function() { + return new State; + }, + + token: function(stream, state) { + state.userIndent(stream.indentation()); + + if (stream.eatSpace()) { + return null; + } + + var token = state.context.next(stream, state.context, state); + state.context = token.context; + state.expectVariable = token.eos; + + return token.name; + }, + + blankLine: function(state) { + state.userIndent(0); + }, + + indent: function(state, textAfter) { + var i = state.context.next === next && textAfter && textAfter.charAt(0) === ']' ? -1 : state.userIndentationDelta; + return (state.indentation + i) * config.indentUnit; + }, + + electricChars: ']' + }; + +}); + +CodeMirror.defineMIME('text/x-stsrc', {name: 'smalltalk'}); + +}); diff --git a/docs/js/node_modules/codemirror/mode/smarty/smarty.js b/docs/js/node_modules/codemirror/mode/smarty/smarty.js new file mode 100644 index 000000000..57852feb0 --- /dev/null +++ b/docs/js/node_modules/codemirror/mode/smarty/smarty.js @@ -0,0 +1,225 @@ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: https://codemirror.net/LICENSE + +/** + * Smarty 2 and 3 mode. + */ + +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { + "use strict"; + + CodeMirror.defineMode("smarty", function(config, parserConf) { + var rightDelimiter = parserConf.rightDelimiter || "}"; + var leftDelimiter = parserConf.leftDelimiter || "{"; + var version = parserConf.version || 2; + var baseMode = CodeMirror.getMode(config, parserConf.baseMode || "null"); + + var keyFunctions = ["debug", "extends", "function", "include", "literal"]; + var regs = { + operatorChars: /[+\-*&%=<>!?]/, + validIdentifier: /[a-zA-Z0-9_]/, + stringChar: /['"]/ + }; + + var last; + function cont(style, lastType) { + last = lastType; + return style; + } + + function chain(stream, state, parser) { + state.tokenize = parser; + return parser(stream, state); + } + + // Smarty 3 allows { and } surrounded by whitespace to NOT slip into Smarty mode + function doesNotCount(stream, pos) { + if (pos == null) pos = stream.pos; + return version === 3 && leftDelimiter == "{" && + (pos == stream.string.length || /\s/.test(stream.string.charAt(pos))); + } + + function tokenTop(stream, state) { + var string = stream.string; + for (var scan = stream.pos;;) { + var nextMatch = string.indexOf(leftDelimiter, scan); + scan = nextMatch + leftDelimiter.length; + if (nextMatch == -1 || !doesNotCount(stream, nextMatch + leftDelimiter.length)) break; + } + if (nextMatch == stream.pos) { + stream.match(leftDelimiter); + if (stream.eat("*")) { + return chain(stream, state, tokenBlock("comment", "*" + rightDelimiter)); + } else { + state.depth++; + state.tokenize = tokenSmarty; + last = "startTag"; + return "tag"; + } + } + + if (nextMatch > -1) stream.string = string.slice(0, nextMatch); + var token = baseMode.token(stream, state.base); + if (nextMatch > -1) stream.string = string; + return token; + } + + // parsing Smarty content + function tokenSmarty(stream, state) { + if (stream.match(rightDelimiter, true)) { + if (version === 3) { + state.depth--; + if (state.depth <= 0) { + state.tokenize = tokenTop; + } + } else { + state.tokenize = tokenTop; + } + return cont("tag", null); + } + + if (stream.match(leftDelimiter, true)) { + state.depth++; + return cont("tag", "startTag"); + } + + var ch = stream.next(); + if (ch == "$") { + stream.eatWhile(regs.validIdentifier); + return cont("variable-2", "variable"); + } else if (ch == "|") { + return cont("operator", "pipe"); + } else if (ch == ".") { + return cont("operator", "property"); + } else if (regs.stringChar.test(ch)) { + state.tokenize = tokenAttribute(ch); + return cont("string", "string"); + } else if (regs.operatorChars.test(ch)) { + stream.eatWhile(regs.operatorChars); + return cont("operator", "operator"); + } else if (ch == "[" || ch == "]") { + return cont("bracket", "bracket"); + } else if (ch == "(" || ch == ")") { + return cont("bracket", "operator"); + } else if (/\d/.test(ch)) { + stream.eatWhile(/\d/); + return cont("number", "number"); + } else { + + if (state.last == "variable") { + if (ch == "@") { + stream.eatWhile(regs.validIdentifier); + return cont("property", "property"); + } else if (ch == "|") { + stream.eatWhile(regs.validIdentifier); + return cont("qualifier", "modifier"); + } + } else if (state.last == "pipe") { + stream.eatWhile(regs.validIdentifier); + return cont("qualifier", "modifier"); + } else if (state.last == "whitespace") { + stream.eatWhile(regs.validIdentifier); + return cont("attribute", "modifier"); + } if (state.last == "property") { + stream.eatWhile(regs.validIdentifier); + return cont("property", null); + } else if (/\s/.test(ch)) { + last = "whitespace"; + return null; + } + + var str = ""; + if (ch != "/") { + str += ch; + } + var c = null; + while (c = stream.eat(regs.validIdentifier)) { + str += c; + } + for (var i=0, j=keyFunctions.length; i]=?)/)) { + // Tokenize filter, binary, null propagator, and equality operators. + return "operator"; + } + if (match = stream.match(/^\$([\w]+)/)) { + return ref(state.variables, match[1]); + } + if (match = stream.match(/^\w+/)) { + return /^(?:as|and|or|not|in)$/.test(match[0]) ? "keyword" : null; + } + stream.next(); + return null; + + case "literal": + if (stream.match(/^(?=\{\/literal})/)) { + state.indent -= config.indentUnit; + state.soyState.pop(); + return this.token(stream, state); + } + return tokenUntil(stream, state, /\{\/literal}/); + } + + if (stream.match(/^\{literal}/)) { + state.indent += config.indentUnit; + state.soyState.push("literal"); + state.context = new Context(state.context, "literal", state.variables); + return "keyword"; + + // A tag-keyword must be followed by whitespace, comment or a closing tag. + } else if (match = stream.match(/^\{([/@\\]?\w+\??)(?=$|[\s}]|\/[/*])/)) { + var prevTag = state.tag; + state.tag = match[1]; + var endTag = state.tag[0] == "/"; + var indentingTag = !!tags[state.tag]; + var tagName = endTag ? state.tag.substring(1) : state.tag; + var tag = tags[tagName]; + if (state.tag != "/switch") + state.indent += ((endTag || tag && tag.reduceIndent) && prevTag != "switch" ? 1 : 2) * config.indentUnit; + + state.soyState.push("tag"); + var tagError = false; + if (tag) { + if (!endTag) { + if (tag.soyState) state.soyState.push(tag.soyState); + } + // If a new tag, open a new context. + if (!tag.noEndTag && (indentingTag || !endTag)) { + state.context = new Context(state.context, state.tag, tag.variableScope ? state.variables : null); + // Otherwise close the current context. + } else if (endTag) { + if (!state.context || state.context.tag != tagName) { + tagError = true; + } else if (state.context) { + if (state.context.kind) { + state.localStates.pop(); + var localState = last(state.localStates); + if (localState.mode.indent) { + state.indent -= localState.mode.indent(localState.state, "", ""); + } + } + popcontext(state); + } + } + } else if (endTag) { + // Assume all tags with a closing tag are defined in the config. + tagError = true; + } + return (tagError ? "error " : "") + "keyword"; + + // Not a tag-keyword; it's an implicit print tag. + } else if (stream.eat('{')) { + state.tag = "print"; + state.indent += 2 * config.indentUnit; + state.soyState.push("tag"); + return "keyword"; + } + + return tokenUntil(stream, state, /\{|\s+\/\/|\/\*/); + }, + + indent: function(state, textAfter, line) { + var indent = state.indent, top = last(state.soyState); + if (top == "comment") return CodeMirror.Pass; + + if (top == "literal") { + if (/^\{\/literal}/.test(textAfter)) indent -= config.indentUnit; + } else { + if (/^\s*\{\/(template|deltemplate)\b/.test(textAfter)) return 0; + if (/^\{(\/|(fallbackmsg|elseif|else|ifempty)\b)/.test(textAfter)) indent -= config.indentUnit; + if (state.tag != "switch" && /^\{(case|default)\b/.test(textAfter)) indent -= config.indentUnit; + if (/^\{\/switch\b/.test(textAfter)) indent -= config.indentUnit; + } + var localState = last(state.localStates); + if (indent && localState.mode.indent) { + indent += localState.mode.indent(localState.state, textAfter, line); + } + return indent; + }, + + innerMode: function(state) { + if (state.soyState.length && last(state.soyState) != "literal") return null; + else return last(state.localStates); + }, + + electricInput: /^\s*\{(\/|\/template|\/deltemplate|\/switch|fallbackmsg|elseif|else|case|default|ifempty|\/literal\})$/, + lineComment: "//", + blockCommentStart: "/*", + blockCommentEnd: "*/", + blockCommentContinue: " * ", + useInnerComments: false, + fold: "indent" + }; + }, "htmlmixed"); + + CodeMirror.registerHelper("wordChars", "soy", /[\w$]/); + + CodeMirror.registerHelper("hintWords", "soy", Object.keys(tags).concat( + ["css", "debugger"])); + + CodeMirror.defineMIME("text/x-soy", "soy"); +}); diff --git a/docs/js/node_modules/codemirror/mode/sparql/sparql.js b/docs/js/node_modules/codemirror/mode/sparql/sparql.js new file mode 100644 index 000000000..bb79abff7 --- /dev/null +++ b/docs/js/node_modules/codemirror/mode/sparql/sparql.js @@ -0,0 +1,180 @@ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: https://codemirror.net/LICENSE + +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + +CodeMirror.defineMode("sparql", function(config) { + var indentUnit = config.indentUnit; + var curPunc; + + function wordRegexp(words) { + return new RegExp("^(?:" + words.join("|") + ")$", "i"); + } + var ops = wordRegexp(["str", "lang", "langmatches", "datatype", "bound", "sameterm", "isiri", "isuri", + "iri", "uri", "bnode", "count", "sum", "min", "max", "avg", "sample", + "group_concat", "rand", "abs", "ceil", "floor", "round", "concat", "substr", "strlen", + "replace", "ucase", "lcase", "encode_for_uri", "contains", "strstarts", "strends", + "strbefore", "strafter", "year", "month", "day", "hours", "minutes", "seconds", + "timezone", "tz", "now", "uuid", "struuid", "md5", "sha1", "sha256", "sha384", + "sha512", "coalesce", "if", "strlang", "strdt", "isnumeric", "regex", "exists", + "isblank", "isliteral", "a", "bind"]); + var keywords = wordRegexp(["base", "prefix", "select", "distinct", "reduced", "construct", "describe", + "ask", "from", "named", "where", "order", "limit", "offset", "filter", "optional", + "graph", "by", "asc", "desc", "as", "having", "undef", "values", "group", + "minus", "in", "not", "service", "silent", "using", "insert", "delete", "union", + "true", "false", "with", + "data", "copy", "to", "move", "add", "create", "drop", "clear", "load"]); + var operatorChars = /[*+\-<>=&|\^\/!\?]/; + + function tokenBase(stream, state) { + var ch = stream.next(); + curPunc = null; + if (ch == "$" || ch == "?") { + if(ch == "?" && stream.match(/\s/, false)){ + return "operator"; + } + stream.match(/^[A-Za-z0-9_\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD][A-Za-z0-9_\u00B7\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u037D\u037F-\u1FFF\u200C-\u200D\u203F-\u2040\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD]*/); + return "variable-2"; + } + else if (ch == "<" && !stream.match(/^[\s\u00a0=]/, false)) { + stream.match(/^[^\s\u00a0>]*>?/); + return "atom"; + } + else if (ch == "\"" || ch == "'") { + state.tokenize = tokenLiteral(ch); + return state.tokenize(stream, state); + } + else if (/[{}\(\),\.;\[\]]/.test(ch)) { + curPunc = ch; + return "bracket"; + } + else if (ch == "#") { + stream.skipToEnd(); + return "comment"; + } + else if (operatorChars.test(ch)) { + stream.eatWhile(operatorChars); + return "operator"; + } + else if (ch == ":") { + stream.eatWhile(/[\w\d\._\-]/); + return "atom"; + } + else if (ch == "@") { + stream.eatWhile(/[a-z\d\-]/i); + return "meta"; + } + else { + stream.eatWhile(/[_\w\d]/); + if (stream.eat(":")) { + stream.eatWhile(/[\w\d_\-]/); + return "atom"; + } + var word = stream.current(); + if (ops.test(word)) + return "builtin"; + else if (keywords.test(word)) + return "keyword"; + else + return "variable"; + } + } + + function tokenLiteral(quote) { + return function(stream, state) { + var escaped = false, ch; + while ((ch = stream.next()) != null) { + if (ch == quote && !escaped) { + state.tokenize = tokenBase; + break; + } + escaped = !escaped && ch == "\\"; + } + return "string"; + }; + } + + function pushContext(state, type, col) { + state.context = {prev: state.context, indent: state.indent, col: col, type: type}; + } + function popContext(state) { + state.indent = state.context.indent; + state.context = state.context.prev; + } + + return { + startState: function() { + return {tokenize: tokenBase, + context: null, + indent: 0, + col: 0}; + }, + + token: function(stream, state) { + if (stream.sol()) { + if (state.context && state.context.align == null) state.context.align = false; + state.indent = stream.indentation(); + } + if (stream.eatSpace()) return null; + var style = state.tokenize(stream, state); + + if (style != "comment" && state.context && state.context.align == null && state.context.type != "pattern") { + state.context.align = true; + } + + if (curPunc == "(") pushContext(state, ")", stream.column()); + else if (curPunc == "[") pushContext(state, "]", stream.column()); + else if (curPunc == "{") pushContext(state, "}", stream.column()); + else if (/[\]\}\)]/.test(curPunc)) { + while (state.context && state.context.type == "pattern") popContext(state); + if (state.context && curPunc == state.context.type) { + popContext(state); + if (curPunc == "}" && state.context && state.context.type == "pattern") + popContext(state); + } + } + else if (curPunc == "." && state.context && state.context.type == "pattern") popContext(state); + else if (/atom|string|variable/.test(style) && state.context) { + if (/[\}\]]/.test(state.context.type)) + pushContext(state, "pattern", stream.column()); + else if (state.context.type == "pattern" && !state.context.align) { + state.context.align = true; + state.context.col = stream.column(); + } + } + + return style; + }, + + indent: function(state, textAfter) { + var firstChar = textAfter && textAfter.charAt(0); + var context = state.context; + if (/[\]\}]/.test(firstChar)) + while (context && context.type == "pattern") context = context.prev; + + var closing = context && firstChar == context.type; + if (!context) + return 0; + else if (context.type == "pattern") + return context.col; + else if (context.align) + return context.col + (closing ? 0 : 1); + else + return context.indent + (closing ? 0 : indentUnit); + }, + + lineComment: "#" + }; +}); + +CodeMirror.defineMIME("application/sparql-query", "sparql"); + +}); diff --git a/docs/js/node_modules/codemirror/mode/spreadsheet/spreadsheet.js b/docs/js/node_modules/codemirror/mode/spreadsheet/spreadsheet.js new file mode 100644 index 000000000..d87f988d3 --- /dev/null +++ b/docs/js/node_modules/codemirror/mode/spreadsheet/spreadsheet.js @@ -0,0 +1,112 @@ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: https://codemirror.net/LICENSE + +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { + "use strict"; + + CodeMirror.defineMode("spreadsheet", function () { + return { + startState: function () { + return { + stringType: null, + stack: [] + }; + }, + token: function (stream, state) { + if (!stream) return; + + //check for state changes + if (state.stack.length === 0) { + //strings + if ((stream.peek() == '"') || (stream.peek() == "'")) { + state.stringType = stream.peek(); + stream.next(); // Skip quote + state.stack.unshift("string"); + } + } + + //return state + //stack has + switch (state.stack[0]) { + case "string": + while (state.stack[0] === "string" && !stream.eol()) { + if (stream.peek() === state.stringType) { + stream.next(); // Skip quote + state.stack.shift(); // Clear flag + } else if (stream.peek() === "\\") { + stream.next(); + stream.next(); + } else { + stream.match(/^.[^\\\"\']*/); + } + } + return "string"; + + case "characterClass": + while (state.stack[0] === "characterClass" && !stream.eol()) { + if (!(stream.match(/^[^\]\\]+/) || stream.match(/^\\./))) + state.stack.shift(); + } + return "operator"; + } + + var peek = stream.peek(); + + //no stack + switch (peek) { + case "[": + stream.next(); + state.stack.unshift("characterClass"); + return "bracket"; + case ":": + stream.next(); + return "operator"; + case "\\": + if (stream.match(/\\[a-z]+/)) return "string-2"; + else { + stream.next(); + return "atom"; + } + case ".": + case ",": + case ";": + case "*": + case "-": + case "+": + case "^": + case "<": + case "/": + case "=": + stream.next(); + return "atom"; + case "$": + stream.next(); + return "builtin"; + } + + if (stream.match(/\d+/)) { + if (stream.match(/^\w+/)) return "error"; + return "number"; + } else if (stream.match(/^[a-zA-Z_]\w*/)) { + if (stream.match(/(?=[\(.])/, false)) return "keyword"; + return "variable-2"; + } else if (["[", "]", "(", ")", "{", "}"].indexOf(peek) != -1) { + stream.next(); + return "bracket"; + } else if (!stream.eatSpace()) { + stream.next(); + } + return null; + } + }; + }); + + CodeMirror.defineMIME("text/x-spreadsheet", "spreadsheet"); +}); diff --git a/docs/js/node_modules/codemirror/mode/sql/sql.js b/docs/js/node_modules/codemirror/mode/sql/sql.js new file mode 100644 index 000000000..35902bbab --- /dev/null +++ b/docs/js/node_modules/codemirror/mode/sql/sql.js @@ -0,0 +1,494 @@ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: https://codemirror.net/LICENSE + +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + +CodeMirror.defineMode("sql", function(config, parserConfig) { + var client = parserConfig.client || {}, + atoms = parserConfig.atoms || {"false": true, "true": true, "null": true}, + builtin = parserConfig.builtin || set(defaultBuiltin), + keywords = parserConfig.keywords || set(sqlKeywords), + operatorChars = parserConfig.operatorChars || /^[*+\-%<>!=&|~^\/]/, + support = parserConfig.support || {}, + hooks = parserConfig.hooks || {}, + dateSQL = parserConfig.dateSQL || {"date" : true, "time" : true, "timestamp" : true}, + backslashStringEscapes = parserConfig.backslashStringEscapes !== false, + brackets = parserConfig.brackets || /^[\{}\(\)\[\]]/, + punctuation = parserConfig.punctuation || /^[;.,:]/ + + function tokenBase(stream, state) { + var ch = stream.next(); + + // call hooks from the mime type + if (hooks[ch]) { + var result = hooks[ch](stream, state); + if (result !== false) return result; + } + + if (support.hexNumber && + ((ch == "0" && stream.match(/^[xX][0-9a-fA-F]+/)) + || (ch == "x" || ch == "X") && stream.match(/^'[0-9a-fA-F]+'/))) { + // hex + // ref: http://dev.mysql.com/doc/refman/5.5/en/hexadecimal-literals.html + return "number"; + } else if (support.binaryNumber && + (((ch == "b" || ch == "B") && stream.match(/^'[01]+'/)) + || (ch == "0" && stream.match(/^b[01]+/)))) { + // bitstring + // ref: http://dev.mysql.com/doc/refman/5.5/en/bit-field-literals.html + return "number"; + } else if (ch.charCodeAt(0) > 47 && ch.charCodeAt(0) < 58) { + // numbers + // ref: http://dev.mysql.com/doc/refman/5.5/en/number-literals.html + stream.match(/^[0-9]*(\.[0-9]+)?([eE][-+]?[0-9]+)?/); + support.decimallessFloat && stream.match(/^\.(?!\.)/); + return "number"; + } else if (ch == "?" && (stream.eatSpace() || stream.eol() || stream.eat(";"))) { + // placeholders + return "variable-3"; + } else if (ch == "'" || (ch == '"' && support.doubleQuote)) { + // strings + // ref: http://dev.mysql.com/doc/refman/5.5/en/string-literals.html + state.tokenize = tokenLiteral(ch); + return state.tokenize(stream, state); + } else if ((((support.nCharCast && (ch == "n" || ch == "N")) + || (support.charsetCast && ch == "_" && stream.match(/[a-z][a-z0-9]*/i))) + && (stream.peek() == "'" || stream.peek() == '"'))) { + // charset casting: _utf8'str', N'str', n'str' + // ref: http://dev.mysql.com/doc/refman/5.5/en/string-literals.html + return "keyword"; + } else if (support.commentSlashSlash && ch == "/" && stream.eat("/")) { + // 1-line comment + stream.skipToEnd(); + return "comment"; + } else if ((support.commentHash && ch == "#") + || (ch == "-" && stream.eat("-") && (!support.commentSpaceRequired || stream.eat(" ")))) { + // 1-line comments + // ref: https://kb.askmonty.org/en/comment-syntax/ + stream.skipToEnd(); + return "comment"; + } else if (ch == "/" && stream.eat("*")) { + // multi-line comments + // ref: https://kb.askmonty.org/en/comment-syntax/ + state.tokenize = tokenComment(1); + return state.tokenize(stream, state); + } else if (ch == ".") { + // .1 for 0.1 + if (support.zerolessFloat && stream.match(/^(?:\d+(?:e[+-]?\d+)?)/i)) + return "number"; + if (stream.match(/^\.+/)) + return null + // .table_name (ODBC) + // // ref: http://dev.mysql.com/doc/refman/5.6/en/identifier-qualifiers.html + if (support.ODBCdotTable && stream.match(/^[\w\d_]+/)) + return "variable-2"; + } else if (operatorChars.test(ch)) { + // operators + stream.eatWhile(operatorChars); + return "operator"; + } else if (brackets.test(ch)) { + // brackets + return "bracket"; + } else if (punctuation.test(ch)) { + // punctuation + stream.eatWhile(punctuation); + return "punctuation"; + } else if (ch == '{' && + (stream.match(/^( )*(d|D|t|T|ts|TS)( )*'[^']*'( )*}/) || stream.match(/^( )*(d|D|t|T|ts|TS)( )*"[^"]*"( )*}/))) { + // dates (weird ODBC syntax) + // ref: http://dev.mysql.com/doc/refman/5.5/en/date-and-time-literals.html + return "number"; + } else { + stream.eatWhile(/^[_\w\d]/); + var word = stream.current().toLowerCase(); + // dates (standard SQL syntax) + // ref: http://dev.mysql.com/doc/refman/5.5/en/date-and-time-literals.html + if (dateSQL.hasOwnProperty(word) && (stream.match(/^( )+'[^']*'/) || stream.match(/^( )+"[^"]*"/))) + return "number"; + if (atoms.hasOwnProperty(word)) return "atom"; + if (builtin.hasOwnProperty(word)) return "builtin"; + if (keywords.hasOwnProperty(word)) return "keyword"; + if (client.hasOwnProperty(word)) return "string-2"; + return null; + } + } + + // 'string', with char specified in quote escaped by '\' + function tokenLiteral(quote) { + return function(stream, state) { + var escaped = false, ch; + while ((ch = stream.next()) != null) { + if (ch == quote && !escaped) { + state.tokenize = tokenBase; + break; + } + escaped = backslashStringEscapes && !escaped && ch == "\\"; + } + return "string"; + }; + } + function tokenComment(depth) { + return function(stream, state) { + var m = stream.match(/^.*?(\/\*|\*\/)/) + if (!m) stream.skipToEnd() + else if (m[1] == "/*") state.tokenize = tokenComment(depth + 1) + else if (depth > 1) state.tokenize = tokenComment(depth - 1) + else state.tokenize = tokenBase + return "comment" + } + } + + function pushContext(stream, state, type) { + state.context = { + prev: state.context, + indent: stream.indentation(), + col: stream.column(), + type: type + }; + } + + function popContext(state) { + state.indent = state.context.indent; + state.context = state.context.prev; + } + + return { + startState: function() { + return {tokenize: tokenBase, context: null}; + }, + + token: function(stream, state) { + if (stream.sol()) { + if (state.context && state.context.align == null) + state.context.align = false; + } + if (state.tokenize == tokenBase && stream.eatSpace()) return null; + + var style = state.tokenize(stream, state); + if (style == "comment") return style; + + if (state.context && state.context.align == null) + state.context.align = true; + + var tok = stream.current(); + if (tok == "(") + pushContext(stream, state, ")"); + else if (tok == "[") + pushContext(stream, state, "]"); + else if (state.context && state.context.type == tok) + popContext(state); + return style; + }, + + indent: function(state, textAfter) { + var cx = state.context; + if (!cx) return CodeMirror.Pass; + var closing = textAfter.charAt(0) == cx.type; + if (cx.align) return cx.col + (closing ? 0 : 1); + else return cx.indent + (closing ? 0 : config.indentUnit); + }, + + blockCommentStart: "/*", + blockCommentEnd: "*/", + lineComment: support.commentSlashSlash ? "//" : support.commentHash ? "#" : "--", + closeBrackets: "()[]{}''\"\"``" + }; +}); + + // `identifier` + function hookIdentifier(stream) { + // MySQL/MariaDB identifiers + // ref: http://dev.mysql.com/doc/refman/5.6/en/identifier-qualifiers.html + var ch; + while ((ch = stream.next()) != null) { + if (ch == "`" && !stream.eat("`")) return "variable-2"; + } + stream.backUp(stream.current().length - 1); + return stream.eatWhile(/\w/) ? "variable-2" : null; + } + + // "identifier" + function hookIdentifierDoublequote(stream) { + // Standard SQL /SQLite identifiers + // ref: http://web.archive.org/web/20160813185132/http://savage.net.au/SQL/sql-99.bnf.html#delimited%20identifier + // ref: http://sqlite.org/lang_keywords.html + var ch; + while ((ch = stream.next()) != null) { + if (ch == "\"" && !stream.eat("\"")) return "variable-2"; + } + stream.backUp(stream.current().length - 1); + return stream.eatWhile(/\w/) ? "variable-2" : null; + } + + // variable token + function hookVar(stream) { + // variables + // @@prefix.varName @varName + // varName can be quoted with ` or ' or " + // ref: http://dev.mysql.com/doc/refman/5.5/en/user-variables.html + if (stream.eat("@")) { + stream.match(/^session\./); + stream.match(/^local\./); + stream.match(/^global\./); + } + + if (stream.eat("'")) { + stream.match(/^.*'/); + return "variable-2"; + } else if (stream.eat('"')) { + stream.match(/^.*"/); + return "variable-2"; + } else if (stream.eat("`")) { + stream.match(/^.*`/); + return "variable-2"; + } else if (stream.match(/^[0-9a-zA-Z$\.\_]+/)) { + return "variable-2"; + } + return null; + }; + + // short client keyword token + function hookClient(stream) { + // \N means NULL + // ref: http://dev.mysql.com/doc/refman/5.5/en/null-values.html + if (stream.eat("N")) { + return "atom"; + } + // \g, etc + // ref: http://dev.mysql.com/doc/refman/5.5/en/mysql-commands.html + return stream.match(/^[a-zA-Z.#!?]/) ? "variable-2" : null; + } + + // these keywords are used by all SQL dialects (however, a mode can still overwrite it) + var sqlKeywords = "alter and as asc between by count create delete desc distinct drop from group having in insert into is join like not on or order select set table union update values where limit "; + + // turn a space-separated list into an array + function set(str) { + var obj = {}, words = str.split(" "); + for (var i = 0; i < words.length; ++i) obj[words[i]] = true; + return obj; + } + + var defaultBuiltin = "bool boolean bit blob enum long longblob longtext medium mediumblob mediumint mediumtext time timestamp tinyblob tinyint tinytext text bigint int int1 int2 int3 int4 int8 integer float float4 float8 double char varbinary varchar varcharacter precision real date datetime year unsigned signed decimal numeric" + + // A generic SQL Mode. It's not a standard, it just try to support what is generally supported + CodeMirror.defineMIME("text/x-sql", { + name: "sql", + keywords: set(sqlKeywords + "begin"), + builtin: set(defaultBuiltin), + atoms: set("false true null unknown"), + dateSQL: set("date time timestamp"), + support: set("ODBCdotTable doubleQuote binaryNumber hexNumber") + }); + + CodeMirror.defineMIME("text/x-mssql", { + name: "sql", + client: set("$partition binary_checksum checksum connectionproperty context_info current_request_id error_line error_message error_number error_procedure error_severity error_state formatmessage get_filestream_transaction_context getansinull host_id host_name isnull isnumeric min_active_rowversion newid newsequentialid rowcount_big xact_state object_id"), + keywords: set(sqlKeywords + "begin trigger proc view index for add constraint key primary foreign collate clustered nonclustered declare exec go if use index holdlock nolock nowait paglock readcommitted readcommittedlock readpast readuncommitted repeatableread rowlock serializable snapshot tablock tablockx updlock with"), + builtin: set("bigint numeric bit smallint decimal smallmoney int tinyint money float real char varchar text nchar nvarchar ntext binary varbinary image cursor timestamp hierarchyid uniqueidentifier sql_variant xml table "), + atoms: set("is not null like and or in left right between inner outer join all any some cross unpivot pivot exists"), + operatorChars: /^[*+\-%<>!=^\&|\/]/, + brackets: /^[\{}\(\)]/, + punctuation: /^[;.,:/]/, + backslashStringEscapes: false, + dateSQL: set("date datetimeoffset datetime2 smalldatetime datetime time"), + hooks: { + "@": hookVar + } + }); + + CodeMirror.defineMIME("text/x-mysql", { + name: "sql", + client: set("charset clear connect edit ego exit go help nopager notee nowarning pager print prompt quit rehash source status system tee"), + keywords: set(sqlKeywords + "accessible action add after algorithm all analyze asensitive at authors auto_increment autocommit avg avg_row_length before binary binlog both btree cache call cascade cascaded case catalog_name chain change changed character check checkpoint checksum class_origin client_statistics close coalesce code collate collation collations column columns comment commit committed completion concurrent condition connection consistent constraint contains continue contributors convert cross current current_date current_time current_timestamp current_user cursor data database databases day_hour day_microsecond day_minute day_second deallocate dec declare default delay_key_write delayed delimiter des_key_file describe deterministic dev_pop dev_samp deviance diagnostics directory disable discard distinctrow div dual dumpfile each elseif enable enclosed end ends engine engines enum errors escape escaped even event events every execute exists exit explain extended fast fetch field fields first flush for force foreign found_rows full fulltext function general get global grant grants group group_concat handler hash help high_priority hosts hour_microsecond hour_minute hour_second if ignore ignore_server_ids import index index_statistics infile inner innodb inout insensitive insert_method install interval invoker isolation iterate key keys kill language last leading leave left level limit linear lines list load local localtime localtimestamp lock logs low_priority master master_heartbeat_period master_ssl_verify_server_cert masters match max max_rows maxvalue message_text middleint migrate min min_rows minute_microsecond minute_second mod mode modifies modify mutex mysql_errno natural next no no_write_to_binlog offline offset one online open optimize option optionally out outer outfile pack_keys parser partition partitions password phase plugin plugins prepare preserve prev primary privileges procedure processlist profile profiles purge query quick range read read_write reads real rebuild recover references regexp relaylog release remove rename reorganize repair repeatable replace require resignal restrict resume return returns revoke right rlike rollback rollup row row_format rtree savepoint schedule schema schema_name schemas second_microsecond security sensitive separator serializable server session share show signal slave slow smallint snapshot soname spatial specific sql sql_big_result sql_buffer_result sql_cache sql_calc_found_rows sql_no_cache sql_small_result sqlexception sqlstate sqlwarning ssl start starting starts status std stddev stddev_pop stddev_samp storage straight_join subclass_origin sum suspend table_name table_statistics tables tablespace temporary terminated to trailing transaction trigger triggers truncate uncommitted undo uninstall unique unlock upgrade usage use use_frm user user_resources user_statistics using utc_date utc_time utc_timestamp value variables varying view views warnings when while with work write xa xor year_month zerofill begin do then else loop repeat"), + builtin: set("bool boolean bit blob decimal double float long longblob longtext medium mediumblob mediumint mediumtext time timestamp tinyblob tinyint tinytext text bigint int int1 int2 int3 int4 int8 integer float float4 float8 double char varbinary varchar varcharacter precision date datetime year unsigned signed numeric"), + atoms: set("false true null unknown"), + operatorChars: /^[*+\-%<>!=&|^]/, + dateSQL: set("date time timestamp"), + support: set("ODBCdotTable decimallessFloat zerolessFloat binaryNumber hexNumber doubleQuote nCharCast charsetCast commentHash commentSpaceRequired"), + hooks: { + "@": hookVar, + "`": hookIdentifier, + "\\": hookClient + } + }); + + CodeMirror.defineMIME("text/x-mariadb", { + name: "sql", + client: set("charset clear connect edit ego exit go help nopager notee nowarning pager print prompt quit rehash source status system tee"), + keywords: set(sqlKeywords + "accessible action add after algorithm all always analyze asensitive at authors auto_increment autocommit avg avg_row_length before binary binlog both btree cache call cascade cascaded case catalog_name chain change changed character check checkpoint checksum class_origin client_statistics close coalesce code collate collation collations column columns comment commit committed completion concurrent condition connection consistent constraint contains continue contributors convert cross current current_date current_time current_timestamp current_user cursor data database databases day_hour day_microsecond day_minute day_second deallocate dec declare default delay_key_write delayed delimiter des_key_file describe deterministic dev_pop dev_samp deviance diagnostics directory disable discard distinctrow div dual dumpfile each elseif enable enclosed end ends engine engines enum errors escape escaped even event events every execute exists exit explain extended fast fetch field fields first flush for force foreign found_rows full fulltext function general generated get global grant grants group groupby_concat handler hard hash help high_priority hosts hour_microsecond hour_minute hour_second if ignore ignore_server_ids import index index_statistics infile inner innodb inout insensitive insert_method install interval invoker isolation iterate key keys kill language last leading leave left level limit linear lines list load local localtime localtimestamp lock logs low_priority master master_heartbeat_period master_ssl_verify_server_cert masters match max max_rows maxvalue message_text middleint migrate min min_rows minute_microsecond minute_second mod mode modifies modify mutex mysql_errno natural next no no_write_to_binlog offline offset one online open optimize option optionally out outer outfile pack_keys parser partition partitions password persistent phase plugin plugins prepare preserve prev primary privileges procedure processlist profile profiles purge query quick range read read_write reads real rebuild recover references regexp relaylog release remove rename reorganize repair repeatable replace require resignal restrict resume return returns revoke right rlike rollback rollup row row_format rtree savepoint schedule schema schema_name schemas second_microsecond security sensitive separator serializable server session share show shutdown signal slave slow smallint snapshot soft soname spatial specific sql sql_big_result sql_buffer_result sql_cache sql_calc_found_rows sql_no_cache sql_small_result sqlexception sqlstate sqlwarning ssl start starting starts status std stddev stddev_pop stddev_samp storage straight_join subclass_origin sum suspend table_name table_statistics tables tablespace temporary terminated to trailing transaction trigger triggers truncate uncommitted undo uninstall unique unlock upgrade usage use use_frm user user_resources user_statistics using utc_date utc_time utc_timestamp value variables varying view views virtual warnings when while with work write xa xor year_month zerofill begin do then else loop repeat"), + builtin: set("bool boolean bit blob decimal double float long longblob longtext medium mediumblob mediumint mediumtext time timestamp tinyblob tinyint tinytext text bigint int int1 int2 int3 int4 int8 integer float float4 float8 double char varbinary varchar varcharacter precision date datetime year unsigned signed numeric"), + atoms: set("false true null unknown"), + operatorChars: /^[*+\-%<>!=&|^]/, + dateSQL: set("date time timestamp"), + support: set("ODBCdotTable decimallessFloat zerolessFloat binaryNumber hexNumber doubleQuote nCharCast charsetCast commentHash commentSpaceRequired"), + hooks: { + "@": hookVar, + "`": hookIdentifier, + "\\": hookClient + } + }); + + // provided by the phpLiteAdmin project - phpliteadmin.org + CodeMirror.defineMIME("text/x-sqlite", { + name: "sql", + // commands of the official SQLite client, ref: https://www.sqlite.org/cli.html#dotcmd + client: set("auth backup bail binary changes check clone databases dbinfo dump echo eqp exit explain fullschema headers help import imposter indexes iotrace limit lint load log mode nullvalue once open output print prompt quit read restore save scanstats schema separator session shell show stats system tables testcase timeout timer trace vfsinfo vfslist vfsname width"), + // ref: http://sqlite.org/lang_keywords.html + keywords: set(sqlKeywords + "abort action add after all analyze attach autoincrement before begin cascade case cast check collate column commit conflict constraint cross current_date current_time current_timestamp database default deferrable deferred detach each else end escape except exclusive exists explain fail for foreign full glob if ignore immediate index indexed initially inner instead intersect isnull key left limit match natural no notnull null of offset outer plan pragma primary query raise recursive references regexp reindex release rename replace restrict right rollback row savepoint temp temporary then to transaction trigger unique using vacuum view virtual when with without"), + // SQLite is weakly typed, ref: http://sqlite.org/datatype3.html. This is just a list of some common types. + builtin: set("bool boolean bit blob decimal double float long longblob longtext medium mediumblob mediumint mediumtext time timestamp tinyblob tinyint tinytext text clob bigint int int2 int8 integer float double char varchar date datetime year unsigned signed numeric real"), + // ref: http://sqlite.org/syntax/literal-value.html + atoms: set("null current_date current_time current_timestamp"), + // ref: http://sqlite.org/lang_expr.html#binaryops + operatorChars: /^[*+\-%<>!=&|/~]/, + // SQLite is weakly typed, ref: http://sqlite.org/datatype3.html. This is just a list of some common types. + dateSQL: set("date time timestamp datetime"), + support: set("decimallessFloat zerolessFloat"), + identifierQuote: "\"", //ref: http://sqlite.org/lang_keywords.html + hooks: { + // bind-parameters ref:http://sqlite.org/lang_expr.html#varparam + "@": hookVar, + ":": hookVar, + "?": hookVar, + "$": hookVar, + // The preferred way to escape Identifiers is using double quotes, ref: http://sqlite.org/lang_keywords.html + "\"": hookIdentifierDoublequote, + // there is also support for backtics, ref: http://sqlite.org/lang_keywords.html + "`": hookIdentifier + } + }); + + // the query language used by Apache Cassandra is called CQL, but this mime type + // is called Cassandra to avoid confusion with Contextual Query Language + CodeMirror.defineMIME("text/x-cassandra", { + name: "sql", + client: { }, + keywords: set("add all allow alter and any apply as asc authorize batch begin by clustering columnfamily compact consistency count create custom delete desc distinct drop each_quorum exists filtering from grant if in index insert into key keyspace keyspaces level limit local_one local_quorum modify nan norecursive nosuperuser not of on one order password permission permissions primary quorum rename revoke schema select set storage superuser table three to token truncate ttl two type unlogged update use user users using values where with writetime"), + builtin: set("ascii bigint blob boolean counter decimal double float frozen inet int list map static text timestamp timeuuid tuple uuid varchar varint"), + atoms: set("false true infinity NaN"), + operatorChars: /^[<>=]/, + dateSQL: { }, + support: set("commentSlashSlash decimallessFloat"), + hooks: { } + }); + + // this is based on Peter Raganitsch's 'plsql' mode + CodeMirror.defineMIME("text/x-plsql", { + name: "sql", + client: set("appinfo arraysize autocommit autoprint autorecovery autotrace blockterminator break btitle cmdsep colsep compatibility compute concat copycommit copytypecheck define describe echo editfile embedded escape exec execute feedback flagger flush heading headsep instance linesize lno loboffset logsource long longchunksize markup native newpage numformat numwidth pagesize pause pno recsep recsepchar release repfooter repheader serveroutput shiftinout show showmode size spool sqlblanklines sqlcase sqlcode sqlcontinue sqlnumber sqlpluscompatibility sqlprefix sqlprompt sqlterminator suffix tab term termout time timing trimout trimspool ttitle underline verify version wrap"), + keywords: set("abort accept access add all alter and any array arraylen as asc assert assign at attributes audit authorization avg base_table begin between binary_integer body boolean by case cast char char_base check close cluster clusters colauth column comment commit compress connect connected constant constraint crash create current currval cursor data_base database date dba deallocate debugoff debugon decimal declare default definition delay delete desc digits dispose distinct do drop else elseif elsif enable end entry escape exception exception_init exchange exclusive exists exit external fast fetch file for force form from function generic goto grant group having identified if immediate in increment index indexes indicator initial initrans insert interface intersect into is key level library like limited local lock log logging long loop master maxextents maxtrans member minextents minus mislabel mode modify multiset new next no noaudit nocompress nologging noparallel not nowait number_base object of off offline on online only open option or order out package parallel partition pctfree pctincrease pctused pls_integer positive positiven pragma primary prior private privileges procedure public raise range raw read rebuild record ref references refresh release rename replace resource restrict return returning returns reverse revoke rollback row rowid rowlabel rownum rows run savepoint schema segment select separate session set share snapshot some space split sql start statement storage subtype successful synonym tabauth table tables tablespace task terminate then to trigger truncate type union unique unlimited unrecoverable unusable update use using validate value values variable view views when whenever where while with work"), + builtin: set("abs acos add_months ascii asin atan atan2 average bfile bfilename bigserial bit blob ceil character chartorowid chr clob concat convert cos cosh count dec decode deref dual dump dup_val_on_index empty error exp false float floor found glb greatest hextoraw initcap instr instrb int integer isopen last_day least length lengthb ln lower lpad ltrim lub make_ref max min mlslabel mod months_between natural naturaln nchar nclob new_time next_day nextval nls_charset_decl_len nls_charset_id nls_charset_name nls_initcap nls_lower nls_sort nls_upper nlssort no_data_found notfound null number numeric nvarchar2 nvl others power rawtohex real reftohex round rowcount rowidtochar rowtype rpad rtrim serial sign signtype sin sinh smallint soundex sqlcode sqlerrm sqrt stddev string substr substrb sum sysdate tan tanh to_char text to_date to_label to_multi_byte to_number to_single_byte translate true trunc uid unlogged upper user userenv varchar varchar2 variance varying vsize xml"), + operatorChars: /^[*\/+\-%<>!=~]/, + dateSQL: set("date time timestamp"), + support: set("doubleQuote nCharCast zerolessFloat binaryNumber hexNumber") + }); + + // Created to support specific hive keywords + CodeMirror.defineMIME("text/x-hive", { + name: "sql", + keywords: set("select alter $elem$ $key$ $value$ add after all analyze and archive as asc before between binary both bucket buckets by cascade case cast change cluster clustered clusterstatus collection column columns comment compute concatenate continue create cross cursor data database databases dbproperties deferred delete delimited desc describe directory disable distinct distribute drop else enable end escaped exclusive exists explain export extended external fetch fields fileformat first format formatted from full function functions grant group having hold_ddltime idxproperties if import in index indexes inpath inputdriver inputformat insert intersect into is items join keys lateral left like limit lines load local location lock locks mapjoin materialized minus msck no_drop nocompress not of offline on option or order out outer outputdriver outputformat overwrite partition partitioned partitions percent plus preserve procedure purge range rcfile read readonly reads rebuild recordreader recordwriter recover reduce regexp rename repair replace restrict revoke right rlike row schema schemas semi sequencefile serde serdeproperties set shared show show_database sort sorted ssl statistics stored streamtable table tables tablesample tblproperties temporary terminated textfile then tmp to touch transform trigger unarchive undo union uniquejoin unlock update use using utc utc_tmestamp view when where while with admin authorization char compact compactions conf cube current current_date current_timestamp day decimal defined dependency directories elem_type exchange file following for grouping hour ignore inner interval jar less logical macro minute month more none noscan over owner partialscan preceding pretty principals protection reload rewrite role roles rollup rows second server sets skewed transactions truncate unbounded unset uri user values window year"), + builtin: set("bool boolean long timestamp tinyint smallint bigint int float double date datetime unsigned string array struct map uniontype key_type utctimestamp value_type varchar"), + atoms: set("false true null unknown"), + operatorChars: /^[*+\-%<>!=]/, + dateSQL: set("date timestamp"), + support: set("ODBCdotTable doubleQuote binaryNumber hexNumber") + }); + + CodeMirror.defineMIME("text/x-pgsql", { + name: "sql", + client: set("source"), + // For PostgreSQL - https://www.postgresql.org/docs/11/sql-keywords-appendix.html + // For pl/pgsql lang - https://github.com/postgres/postgres/blob/REL_11_2/src/pl/plpgsql/src/pl_scanner.c + keywords: set(sqlKeywords + "a abort abs absent absolute access according action ada add admin after aggregate alias all allocate also alter always analyse analyze and any are array array_agg array_max_cardinality as asc asensitive assert assertion assignment asymmetric at atomic attach attribute attributes authorization avg backward base64 before begin begin_frame begin_partition bernoulli between bigint binary bit bit_length blob blocked bom boolean both breadth by c cache call called cardinality cascade cascaded case cast catalog catalog_name ceil ceiling chain char char_length character character_length character_set_catalog character_set_name character_set_schema characteristics characters check checkpoint class class_origin clob close cluster coalesce cobol collate collation collation_catalog collation_name collation_schema collect column column_name columns command_function command_function_code comment comments commit committed concurrently condition condition_number configuration conflict connect connection connection_name constant constraint constraint_catalog constraint_name constraint_schema constraints constructor contains content continue control conversion convert copy corr corresponding cost count covar_pop covar_samp create cross csv cube cume_dist current current_catalog current_date current_default_transform_group current_path current_role current_row current_schema current_time current_timestamp current_transform_group_for_type current_user cursor cursor_name cycle data database datalink datatype date datetime_interval_code datetime_interval_precision day db deallocate debug dec decimal declare default defaults deferrable deferred defined definer degree delete delimiter delimiters dense_rank depends depth deref derived desc describe descriptor detach detail deterministic diagnostics dictionary disable discard disconnect dispatch distinct dlnewcopy dlpreviouscopy dlurlcomplete dlurlcompleteonly dlurlcompletewrite dlurlpath dlurlpathonly dlurlpathwrite dlurlscheme dlurlserver dlvalue do document domain double drop dump dynamic dynamic_function dynamic_function_code each element else elseif elsif empty enable encoding encrypted end end_frame end_partition endexec enforced enum equals errcode error escape event every except exception exclude excluding exclusive exec execute exists exit exp explain expression extension external extract false family fetch file filter final first first_value flag float floor following for force foreach foreign fortran forward found frame_row free freeze from fs full function functions fusion g general generated get global go goto grant granted greatest group grouping groups handler having header hex hierarchy hint hold hour id identity if ignore ilike immediate immediately immutable implementation implicit import in include including increment indent index indexes indicator info inherit inherits initially inline inner inout input insensitive insert instance instantiable instead int integer integrity intersect intersection interval into invoker is isnull isolation join k key key_member key_type label lag language large last last_value lateral lead leading leakproof least left length level library like like_regex limit link listen ln load local localtime localtimestamp location locator lock locked log logged loop lower m map mapping match matched materialized max max_cardinality maxvalue member merge message message_length message_octet_length message_text method min minute minvalue mod mode modifies module month more move multiset mumps name names namespace national natural nchar nclob nesting new next nfc nfd nfkc nfkd nil no none normalize normalized not nothing notice notify notnull nowait nth_value ntile null nullable nullif nulls number numeric object occurrences_regex octet_length octets of off offset oids old on only open operator option options or order ordering ordinality others out outer output over overlaps overlay overriding owned owner p pad parallel parameter parameter_mode parameter_name parameter_ordinal_position parameter_specific_catalog parameter_specific_name parameter_specific_schema parser partial partition pascal passing passthrough password path percent percent_rank percentile_cont percentile_disc perform period permission pg_context pg_datatype_name pg_exception_context pg_exception_detail pg_exception_hint placing plans pli policy portion position position_regex power precedes preceding precision prepare prepared preserve primary print_strict_params prior privileges procedural procedure procedures program public publication query quote raise range rank read reads real reassign recheck recovery recursive ref references referencing refresh regr_avgx regr_avgy regr_count regr_intercept regr_r2 regr_slope regr_sxx regr_sxy regr_syy reindex relative release rename repeatable replace replica requiring reset respect restart restore restrict result result_oid return returned_cardinality returned_length returned_octet_length returned_sqlstate returning returns reverse revoke right role rollback rollup routine routine_catalog routine_name routine_schema routines row row_count row_number rows rowtype rule savepoint scale schema schema_name schemas scope scope_catalog scope_name scope_schema scroll search second section security select selective self sensitive sequence sequences serializable server server_name session session_user set setof sets share show similar simple size skip slice smallint snapshot some source space specific specific_name specifictype sql sqlcode sqlerror sqlexception sqlstate sqlwarning sqrt stable stacked standalone start state statement static statistics stddev_pop stddev_samp stdin stdout storage strict strip structure style subclass_origin submultiset subscription substring substring_regex succeeds sum symmetric sysid system system_time system_user t table table_name tables tablesample tablespace temp template temporary text then ties time timestamp timezone_hour timezone_minute to token top_level_count trailing transaction transaction_active transactions_committed transactions_rolled_back transform transforms translate translate_regex translation treat trigger trigger_catalog trigger_name trigger_schema trim trim_array true truncate trusted type types uescape unbounded uncommitted under unencrypted union unique unknown unlink unlisten unlogged unnamed unnest until untyped update upper uri usage use_column use_variable user user_defined_type_catalog user_defined_type_code user_defined_type_name user_defined_type_schema using vacuum valid validate validator value value_of values var_pop var_samp varbinary varchar variable_conflict variadic varying verbose version versioning view views volatile warning when whenever where while whitespace width_bucket window with within without work wrapper write xml xmlagg xmlattributes xmlbinary xmlcast xmlcomment xmlconcat xmldeclaration xmldocument xmlelement xmlexists xmlforest xmliterate xmlnamespaces xmlparse xmlpi xmlquery xmlroot xmlschema xmlserialize xmltable xmltext xmlvalidate year yes zone"), + // https://www.postgresql.org/docs/11/datatype.html + builtin: set("bigint int8 bigserial serial8 bit varying varbit boolean bool box bytea character char varchar cidr circle date double precision float8 inet integer int int4 interval json jsonb line lseg macaddr macaddr8 money numeric decimal path pg_lsn point polygon real float4 smallint int2 smallserial serial2 serial serial4 text time without zone with timetz timestamp timestamptz tsquery tsvector txid_snapshot uuid xml"), + atoms: set("false true null unknown"), + operatorChars: /^[*\/+\-%<>!=&|^\/#@?~]/, + dateSQL: set("date time timestamp"), + support: set("ODBCdotTable decimallessFloat zerolessFloat binaryNumber hexNumber nCharCast charsetCast") + }); + + // Google's SQL-like query language, GQL + CodeMirror.defineMIME("text/x-gql", { + name: "sql", + keywords: set("ancestor and asc by contains desc descendant distinct from group has in is limit offset on order select superset where"), + atoms: set("false true"), + builtin: set("blob datetime first key __key__ string integer double boolean null"), + operatorChars: /^[*+\-%<>!=]/ + }); + + // Greenplum + CodeMirror.defineMIME("text/x-gpsql", { + name: "sql", + client: set("source"), + //https://github.com/greenplum-db/gpdb/blob/master/src/include/parser/kwlist.h + keywords: set("abort absolute access action active add admin after aggregate all also alter always analyse analyze and any array as asc assertion assignment asymmetric at authorization backward before begin between bigint binary bit boolean both by cache called cascade cascaded case cast chain char character characteristics check checkpoint class close cluster coalesce codegen collate column comment commit committed concurrency concurrently configuration connection constraint constraints contains content continue conversion copy cost cpu_rate_limit create createdb createexttable createrole createuser cross csv cube current current_catalog current_date current_role current_schema current_time current_timestamp current_user cursor cycle data database day deallocate dec decimal declare decode default defaults deferrable deferred definer delete delimiter delimiters deny desc dictionary disable discard distinct distributed do document domain double drop dxl each else enable encoding encrypted end enum errors escape every except exchange exclude excluding exclusive execute exists explain extension external extract false family fetch fields filespace fill filter first float following for force foreign format forward freeze from full function global grant granted greatest group group_id grouping handler hash having header hold host hour identity if ignore ilike immediate immutable implicit in including inclusive increment index indexes inherit inherits initially inline inner inout input insensitive insert instead int integer intersect interval into invoker is isnull isolation join key language large last leading least left level like limit list listen load local localtime localtimestamp location lock log login mapping master match maxvalue median merge minute minvalue missing mode modifies modify month move name names national natural nchar new newline next no nocreatedb nocreateexttable nocreaterole nocreateuser noinherit nologin none noovercommit nosuperuser not nothing notify notnull nowait null nullif nulls numeric object of off offset oids old on only operator option options or order ordered others out outer over overcommit overlaps overlay owned owner parser partial partition partitions passing password percent percentile_cont percentile_disc placing plans position preceding precision prepare prepared preserve primary prior privileges procedural procedure protocol queue quote randomly range read readable reads real reassign recheck recursive ref references reindex reject relative release rename repeatable replace replica reset resource restart restrict returning returns revoke right role rollback rollup rootpartition row rows rule savepoint scatter schema scroll search second security segment select sequence serializable session session_user set setof sets share show similar simple smallint some split sql stable standalone start statement statistics stdin stdout storage strict strip subpartition subpartitions substring superuser symmetric sysid system table tablespace temp template temporary text then threshold ties time timestamp to trailing transaction treat trigger trim true truncate trusted type unbounded uncommitted unencrypted union unique unknown unlisten until update user using vacuum valid validation validator value values varchar variadic varying verbose version view volatile web when where whitespace window with within without work writable write xml xmlattributes xmlconcat xmlelement xmlexists xmlforest xmlparse xmlpi xmlroot xmlserialize year yes zone"), + builtin: set("bigint int8 bigserial serial8 bit varying varbit boolean bool box bytea character char varchar cidr circle date double precision float float8 inet integer int int4 interval json jsonb line lseg macaddr macaddr8 money numeric decimal path pg_lsn point polygon real float4 smallint int2 smallserial serial2 serial serial4 text time without zone with timetz timestamp timestamptz tsquery tsvector txid_snapshot uuid xml"), + atoms: set("false true null unknown"), + operatorChars: /^[*+\-%<>!=&|^\/#@?~]/, + dateSQL: set("date time timestamp"), + support: set("ODBCdotTable decimallessFloat zerolessFloat binaryNumber hexNumber nCharCast charsetCast") + }); + + // Spark SQL + CodeMirror.defineMIME("text/x-sparksql", { + name: "sql", + keywords: set("add after all alter analyze and anti archive array as asc at between bucket buckets by cache cascade case cast change clear cluster clustered codegen collection column columns comment commit compact compactions compute concatenate cost create cross cube current current_date current_timestamp database databases datata dbproperties defined delete delimited deny desc describe dfs directories distinct distribute drop else end escaped except exchange exists explain export extended external false fields fileformat first following for format formatted from full function functions global grant group grouping having if ignore import in index indexes inner inpath inputformat insert intersect interval into is items join keys last lateral lazy left like limit lines list load local location lock locks logical macro map minus msck natural no not null nulls of on optimize option options or order out outer outputformat over overwrite partition partitioned partitions percent preceding principals purge range recordreader recordwriter recover reduce refresh regexp rename repair replace reset restrict revoke right rlike role roles rollback rollup row rows schema schemas select semi separated serde serdeproperties set sets show skewed sort sorted start statistics stored stratify struct table tables tablesample tblproperties temp temporary terminated then to touch transaction transactions transform true truncate unarchive unbounded uncache union unlock unset use using values view when where window with"), + builtin: set("tinyint smallint int bigint boolean float double string binary timestamp decimal array map struct uniontype delimited serde sequencefile textfile rcfile inputformat outputformat"), + atoms: set("false true null"), + operatorChars: /^[*\/+\-%<>!=~&|^]/, + dateSQL: set("date time timestamp"), + support: set("ODBCdotTable doubleQuote zerolessFloat") + }); + + // Esper + CodeMirror.defineMIME("text/x-esper", { + name: "sql", + client: set("source"), + // http://www.espertech.com/esper/release-5.5.0/esper-reference/html/appendix_keywords.html + keywords: set("alter and as asc between by count create delete desc distinct drop from group having in insert into is join like not on or order select set table union update values where limit after all and as at asc avedev avg between by case cast coalesce count create current_timestamp day days delete define desc distinct else end escape events every exists false first from full group having hour hours in inner insert instanceof into irstream is istream join last lastweekday left limit like max match_recognize matches median measures metadatasql min minute minutes msec millisecond milliseconds not null offset on or order outer output partition pattern prev prior regexp retain-union retain-intersection right rstream sec second seconds select set some snapshot sql stddev sum then true unidirectional until update variable weekday when where window"), + builtin: {}, + atoms: set("false true null"), + operatorChars: /^[*+\-%<>!=&|^\/#@?~]/, + dateSQL: set("time"), + support: set("decimallessFloat zerolessFloat binaryNumber hexNumber") + }); +}); + +/* + How Properties of Mime Types are used by SQL Mode + ================================================= + + keywords: + A list of keywords you want to be highlighted. + builtin: + A list of builtin types you want to be highlighted (if you want types to be of class "builtin" instead of "keyword"). + operatorChars: + All characters that must be handled as operators. + client: + Commands parsed and executed by the client (not the server). + support: + A list of supported syntaxes which are not common, but are supported by more than 1 DBMS. + * ODBCdotTable: .tableName + * zerolessFloat: .1 + * doubleQuote + * nCharCast: N'string' + * charsetCast: _utf8'string' + * commentHash: use # char for comments + * commentSlashSlash: use // for comments + * commentSpaceRequired: require a space after -- for comments + atoms: + Keywords that must be highlighted as atoms,. Some DBMS's support more atoms than others: + UNKNOWN, INFINITY, UNDERFLOW, NaN... + dateSQL: + Used for date/time SQL standard syntax, because not all DBMS's support same temporal types. +*/ diff --git a/docs/js/node_modules/codemirror/mode/stex/stex.js b/docs/js/node_modules/codemirror/mode/stex/stex.js new file mode 100644 index 000000000..140e5a8af --- /dev/null +++ b/docs/js/node_modules/codemirror/mode/stex/stex.js @@ -0,0 +1,264 @@ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: https://codemirror.net/LICENSE + +/* + * Author: Constantin Jucovschi (c.jucovschi@jacobs-university.de) + * Licence: MIT + */ + +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { + "use strict"; + + CodeMirror.defineMode("stex", function(_config, parserConfig) { + "use strict"; + + function pushCommand(state, command) { + state.cmdState.push(command); + } + + function peekCommand(state) { + if (state.cmdState.length > 0) { + return state.cmdState[state.cmdState.length - 1]; + } else { + return null; + } + } + + function popCommand(state) { + var plug = state.cmdState.pop(); + if (plug) { + plug.closeBracket(); + } + } + + // returns the non-default plugin closest to the end of the list + function getMostPowerful(state) { + var context = state.cmdState; + for (var i = context.length - 1; i >= 0; i--) { + var plug = context[i]; + if (plug.name == "DEFAULT") { + continue; + } + return plug; + } + return { styleIdentifier: function() { return null; } }; + } + + function addPluginPattern(pluginName, cmdStyle, styles) { + return function () { + this.name = pluginName; + this.bracketNo = 0; + this.style = cmdStyle; + this.styles = styles; + this.argument = null; // \begin and \end have arguments that follow. These are stored in the plugin + + this.styleIdentifier = function() { + return this.styles[this.bracketNo - 1] || null; + }; + this.openBracket = function() { + this.bracketNo++; + return "bracket"; + }; + this.closeBracket = function() {}; + }; + } + + var plugins = {}; + + plugins["importmodule"] = addPluginPattern("importmodule", "tag", ["string", "builtin"]); + plugins["documentclass"] = addPluginPattern("documentclass", "tag", ["", "atom"]); + plugins["usepackage"] = addPluginPattern("usepackage", "tag", ["atom"]); + plugins["begin"] = addPluginPattern("begin", "tag", ["atom"]); + plugins["end"] = addPluginPattern("end", "tag", ["atom"]); + + plugins["label" ] = addPluginPattern("label" , "tag", ["atom"]); + plugins["ref" ] = addPluginPattern("ref" , "tag", ["atom"]); + plugins["eqref" ] = addPluginPattern("eqref" , "tag", ["atom"]); + plugins["cite" ] = addPluginPattern("cite" , "tag", ["atom"]); + plugins["bibitem" ] = addPluginPattern("bibitem" , "tag", ["atom"]); + plugins["Bibitem" ] = addPluginPattern("Bibitem" , "tag", ["atom"]); + plugins["RBibitem" ] = addPluginPattern("RBibitem" , "tag", ["atom"]); + + plugins["DEFAULT"] = function () { + this.name = "DEFAULT"; + this.style = "tag"; + + this.styleIdentifier = this.openBracket = this.closeBracket = function() {}; + }; + + function setState(state, f) { + state.f = f; + } + + // called when in a normal (no environment) context + function normal(source, state) { + var plug; + // Do we look like '\command' ? If so, attempt to apply the plugin 'command' + if (source.match(/^\\[a-zA-Z@]+/)) { + var cmdName = source.current().slice(1); + plug = plugins[cmdName] || plugins["DEFAULT"]; + plug = new plug(); + pushCommand(state, plug); + setState(state, beginParams); + return plug.style; + } + + // escape characters + if (source.match(/^\\[$&%#{}_]/)) { + return "tag"; + } + + // white space control characters + if (source.match(/^\\[,;!\/\\]/)) { + return "tag"; + } + + // find if we're starting various math modes + if (source.match("\\[")) { + setState(state, function(source, state){ return inMathMode(source, state, "\\]"); }); + return "keyword"; + } + if (source.match("\\(")) { + setState(state, function(source, state){ return inMathMode(source, state, "\\)"); }); + return "keyword"; + } + if (source.match("$$")) { + setState(state, function(source, state){ return inMathMode(source, state, "$$"); }); + return "keyword"; + } + if (source.match("$")) { + setState(state, function(source, state){ return inMathMode(source, state, "$"); }); + return "keyword"; + } + + var ch = source.next(); + if (ch == "%") { + source.skipToEnd(); + return "comment"; + } else if (ch == '}' || ch == ']') { + plug = peekCommand(state); + if (plug) { + plug.closeBracket(ch); + setState(state, beginParams); + } else { + return "error"; + } + return "bracket"; + } else if (ch == '{' || ch == '[') { + plug = plugins["DEFAULT"]; + plug = new plug(); + pushCommand(state, plug); + return "bracket"; + } else if (/\d/.test(ch)) { + source.eatWhile(/[\w.%]/); + return "atom"; + } else { + source.eatWhile(/[\w\-_]/); + plug = getMostPowerful(state); + if (plug.name == 'begin') { + plug.argument = source.current(); + } + return plug.styleIdentifier(); + } + } + + function inMathMode(source, state, endModeSeq) { + if (source.eatSpace()) { + return null; + } + if (endModeSeq && source.match(endModeSeq)) { + setState(state, normal); + return "keyword"; + } + if (source.match(/^\\[a-zA-Z@]+/)) { + return "tag"; + } + if (source.match(/^[a-zA-Z]+/)) { + return "variable-2"; + } + // escape characters + if (source.match(/^\\[$&%#{}_]/)) { + return "tag"; + } + // white space control characters + if (source.match(/^\\[,;!\/]/)) { + return "tag"; + } + // special math-mode characters + if (source.match(/^[\^_&]/)) { + return "tag"; + } + // non-special characters + if (source.match(/^[+\-<>|=,\/@!*:;'"`~#?]/)) { + return null; + } + if (source.match(/^(\d+\.\d*|\d*\.\d+|\d+)/)) { + return "number"; + } + var ch = source.next(); + if (ch == "{" || ch == "}" || ch == "[" || ch == "]" || ch == "(" || ch == ")") { + return "bracket"; + } + + if (ch == "%") { + source.skipToEnd(); + return "comment"; + } + return "error"; + } + + function beginParams(source, state) { + var ch = source.peek(), lastPlug; + if (ch == '{' || ch == '[') { + lastPlug = peekCommand(state); + lastPlug.openBracket(ch); + source.eat(ch); + setState(state, normal); + return "bracket"; + } + if (/[ \t\r]/.test(ch)) { + source.eat(ch); + return null; + } + setState(state, normal); + popCommand(state); + + return normal(source, state); + } + + return { + startState: function() { + var f = parserConfig.inMathMode ? function(source, state){ return inMathMode(source, state); } : normal; + return { + cmdState: [], + f: f + }; + }, + copyState: function(s) { + return { + cmdState: s.cmdState.slice(), + f: s.f + }; + }, + token: function(stream, state) { + return state.f(stream, state); + }, + blankLine: function(state) { + state.f = normal; + state.cmdState.length = 0; + }, + lineComment: "%" + }; + }); + + CodeMirror.defineMIME("text/x-stex", "stex"); + CodeMirror.defineMIME("text/x-latex", "stex"); + +}); diff --git a/docs/js/node_modules/codemirror/mode/stylus/stylus.js b/docs/js/node_modules/codemirror/mode/stylus/stylus.js new file mode 100644 index 000000000..dbe241d61 --- /dev/null +++ b/docs/js/node_modules/codemirror/mode/stylus/stylus.js @@ -0,0 +1,771 @@ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: https://codemirror.net/LICENSE + +// Stylus mode created by Dmitry Kiselyov http://git.io/AaRB + +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { + "use strict"; + + CodeMirror.defineMode("stylus", function(config) { + var indentUnit = config.indentUnit, + indentUnitString = '', + tagKeywords = keySet(tagKeywords_), + tagVariablesRegexp = /^(a|b|i|s|col|em)$/i, + propertyKeywords = keySet(propertyKeywords_), + nonStandardPropertyKeywords = keySet(nonStandardPropertyKeywords_), + valueKeywords = keySet(valueKeywords_), + colorKeywords = keySet(colorKeywords_), + documentTypes = keySet(documentTypes_), + documentTypesRegexp = wordRegexp(documentTypes_), + mediaFeatures = keySet(mediaFeatures_), + mediaTypes = keySet(mediaTypes_), + fontProperties = keySet(fontProperties_), + operatorsRegexp = /^\s*([.]{2,3}|&&|\|\||\*\*|[?!=:]?=|[-+*\/%<>]=?|\?:|\~)/, + wordOperatorKeywordsRegexp = wordRegexp(wordOperatorKeywords_), + blockKeywords = keySet(blockKeywords_), + vendorPrefixesRegexp = new RegExp(/^\-(moz|ms|o|webkit)-/i), + commonAtoms = keySet(commonAtoms_), + firstWordMatch = "", + states = {}, + ch, + style, + type, + override; + + while (indentUnitString.length < indentUnit) indentUnitString += ' '; + + /** + * Tokenizers + */ + function tokenBase(stream, state) { + firstWordMatch = stream.string.match(/(^[\w-]+\s*=\s*$)|(^\s*[\w-]+\s*=\s*[\w-])|(^\s*(\.|#|@|\$|\&|\[|\d|\+|::?|\{|\>|~|\/)?\s*[\w-]*([a-z0-9-]|\*|\/\*)(\(|,)?)/); + state.context.line.firstWord = firstWordMatch ? firstWordMatch[0].replace(/^\s*/, "") : ""; + state.context.line.indent = stream.indentation(); + ch = stream.peek(); + + // Line comment + if (stream.match("//")) { + stream.skipToEnd(); + return ["comment", "comment"]; + } + // Block comment + if (stream.match("/*")) { + state.tokenize = tokenCComment; + return tokenCComment(stream, state); + } + // String + if (ch == "\"" || ch == "'") { + stream.next(); + state.tokenize = tokenString(ch); + return state.tokenize(stream, state); + } + // Def + if (ch == "@") { + stream.next(); + stream.eatWhile(/[\w\\-]/); + return ["def", stream.current()]; + } + // ID selector or Hex color + if (ch == "#") { + stream.next(); + // Hex color + if (stream.match(/^[0-9a-f]{3}([0-9a-f]([0-9a-f]{2}){0,2})?\b/i)) { + return ["atom", "atom"]; + } + // ID selector + if (stream.match(/^[a-z][\w-]*/i)) { + return ["builtin", "hash"]; + } + } + // Vendor prefixes + if (stream.match(vendorPrefixesRegexp)) { + return ["meta", "vendor-prefixes"]; + } + // Numbers + if (stream.match(/^-?[0-9]?\.?[0-9]/)) { + stream.eatWhile(/[a-z%]/i); + return ["number", "unit"]; + } + // !important|optional + if (ch == "!") { + stream.next(); + return [stream.match(/^(important|optional)/i) ? "keyword": "operator", "important"]; + } + // Class + if (ch == "." && stream.match(/^\.[a-z][\w-]*/i)) { + return ["qualifier", "qualifier"]; + } + // url url-prefix domain regexp + if (stream.match(documentTypesRegexp)) { + if (stream.peek() == "(") state.tokenize = tokenParenthesized; + return ["property", "word"]; + } + // Mixins / Functions + if (stream.match(/^[a-z][\w-]*\(/i)) { + stream.backUp(1); + return ["keyword", "mixin"]; + } + // Block mixins + if (stream.match(/^(\+|-)[a-z][\w-]*\(/i)) { + stream.backUp(1); + return ["keyword", "block-mixin"]; + } + // Parent Reference BEM naming + if (stream.string.match(/^\s*&/) && stream.match(/^[-_]+[a-z][\w-]*/)) { + return ["qualifier", "qualifier"]; + } + // / Root Reference & Parent Reference + if (stream.match(/^(\/|&)(-|_|:|\.|#|[a-z])/)) { + stream.backUp(1); + return ["variable-3", "reference"]; + } + if (stream.match(/^&{1}\s*$/)) { + return ["variable-3", "reference"]; + } + // Word operator + if (stream.match(wordOperatorKeywordsRegexp)) { + return ["operator", "operator"]; + } + // Word + if (stream.match(/^\$?[-_]*[a-z0-9]+[\w-]*/i)) { + // Variable + if (stream.match(/^(\.|\[)[\w-\'\"\]]+/i, false)) { + if (!wordIsTag(stream.current())) { + stream.match(/\./); + return ["variable-2", "variable-name"]; + } + } + return ["variable-2", "word"]; + } + // Operators + if (stream.match(operatorsRegexp)) { + return ["operator", stream.current()]; + } + // Delimiters + if (/[:;,{}\[\]\(\)]/.test(ch)) { + stream.next(); + return [null, ch]; + } + // Non-detected items + stream.next(); + return [null, null]; + } + + /** + * Token comment + */ + function tokenCComment(stream, state) { + var maybeEnd = false, ch; + while ((ch = stream.next()) != null) { + if (maybeEnd && ch == "/") { + state.tokenize = null; + break; + } + maybeEnd = (ch == "*"); + } + return ["comment", "comment"]; + } + + /** + * Token string + */ + function tokenString(quote) { + return function(stream, state) { + var escaped = false, ch; + while ((ch = stream.next()) != null) { + if (ch == quote && !escaped) { + if (quote == ")") stream.backUp(1); + break; + } + escaped = !escaped && ch == "\\"; + } + if (ch == quote || !escaped && quote != ")") state.tokenize = null; + return ["string", "string"]; + }; + } + + /** + * Token parenthesized + */ + function tokenParenthesized(stream, state) { + stream.next(); // Must be "(" + if (!stream.match(/\s*[\"\')]/, false)) + state.tokenize = tokenString(")"); + else + state.tokenize = null; + return [null, "("]; + } + + /** + * Context management + */ + function Context(type, indent, prev, line) { + this.type = type; + this.indent = indent; + this.prev = prev; + this.line = line || {firstWord: "", indent: 0}; + } + + function pushContext(state, stream, type, indent) { + indent = indent >= 0 ? indent : indentUnit; + state.context = new Context(type, stream.indentation() + indent, state.context); + return type; + } + + function popContext(state, currentIndent) { + var contextIndent = state.context.indent - indentUnit; + currentIndent = currentIndent || false; + state.context = state.context.prev; + if (currentIndent) state.context.indent = contextIndent; + return state.context.type; + } + + function pass(type, stream, state) { + return states[state.context.type](type, stream, state); + } + + function popAndPass(type, stream, state, n) { + for (var i = n || 1; i > 0; i--) + state.context = state.context.prev; + return pass(type, stream, state); + } + + + /** + * Parser + */ + function wordIsTag(word) { + return word.toLowerCase() in tagKeywords; + } + + function wordIsProperty(word) { + word = word.toLowerCase(); + return word in propertyKeywords || word in fontProperties; + } + + function wordIsBlock(word) { + return word.toLowerCase() in blockKeywords; + } + + function wordIsVendorPrefix(word) { + return word.toLowerCase().match(vendorPrefixesRegexp); + } + + function wordAsValue(word) { + var wordLC = word.toLowerCase(); + var override = "variable-2"; + if (wordIsTag(word)) override = "tag"; + else if (wordIsBlock(word)) override = "block-keyword"; + else if (wordIsProperty(word)) override = "property"; + else if (wordLC in valueKeywords || wordLC in commonAtoms) override = "atom"; + else if (wordLC == "return" || wordLC in colorKeywords) override = "keyword"; + + // Font family + else if (word.match(/^[A-Z]/)) override = "string"; + return override; + } + + function typeIsBlock(type, stream) { + return ((endOfLine(stream) && (type == "{" || type == "]" || type == "hash" || type == "qualifier")) || type == "block-mixin"); + } + + function typeIsInterpolation(type, stream) { + return type == "{" && stream.match(/^\s*\$?[\w-]+/i, false); + } + + function typeIsPseudo(type, stream) { + return type == ":" && stream.match(/^[a-z-]+/, false); + } + + function startOfLine(stream) { + return stream.sol() || stream.string.match(new RegExp("^\\s*" + escapeRegExp(stream.current()))); + } + + function endOfLine(stream) { + return stream.eol() || stream.match(/^\s*$/, false); + } + + function firstWordOfLine(line) { + var re = /^\s*[-_]*[a-z0-9]+[\w-]*/i; + var result = typeof line == "string" ? line.match(re) : line.string.match(re); + return result ? result[0].replace(/^\s*/, "") : ""; + } + + + /** + * Block + */ + states.block = function(type, stream, state) { + if ((type == "comment" && startOfLine(stream)) || + (type == "," && endOfLine(stream)) || + type == "mixin") { + return pushContext(state, stream, "block", 0); + } + if (typeIsInterpolation(type, stream)) { + return pushContext(state, stream, "interpolation"); + } + if (endOfLine(stream) && type == "]") { + if (!/^\s*(\.|#|:|\[|\*|&)/.test(stream.string) && !wordIsTag(firstWordOfLine(stream))) { + return pushContext(state, stream, "block", 0); + } + } + if (typeIsBlock(type, stream)) { + return pushContext(state, stream, "block"); + } + if (type == "}" && endOfLine(stream)) { + return pushContext(state, stream, "block", 0); + } + if (type == "variable-name") { + if (stream.string.match(/^\s?\$[\w-\.\[\]\'\"]+$/) || wordIsBlock(firstWordOfLine(stream))) { + return pushContext(state, stream, "variableName"); + } + else { + return pushContext(state, stream, "variableName", 0); + } + } + if (type == "=") { + if (!endOfLine(stream) && !wordIsBlock(firstWordOfLine(stream))) { + return pushContext(state, stream, "block", 0); + } + return pushContext(state, stream, "block"); + } + if (type == "*") { + if (endOfLine(stream) || stream.match(/\s*(,|\.|#|\[|:|{)/,false)) { + override = "tag"; + return pushContext(state, stream, "block"); + } + } + if (typeIsPseudo(type, stream)) { + return pushContext(state, stream, "pseudo"); + } + if (/@(font-face|media|supports|(-moz-)?document)/.test(type)) { + return pushContext(state, stream, endOfLine(stream) ? "block" : "atBlock"); + } + if (/@(-(moz|ms|o|webkit)-)?keyframes$/.test(type)) { + return pushContext(state, stream, "keyframes"); + } + if (/@extends?/.test(type)) { + return pushContext(state, stream, "extend", 0); + } + if (type && type.charAt(0) == "@") { + + // Property Lookup + if (stream.indentation() > 0 && wordIsProperty(stream.current().slice(1))) { + override = "variable-2"; + return "block"; + } + if (/(@import|@require|@charset)/.test(type)) { + return pushContext(state, stream, "block", 0); + } + return pushContext(state, stream, "block"); + } + if (type == "reference" && endOfLine(stream)) { + return pushContext(state, stream, "block"); + } + if (type == "(") { + return pushContext(state, stream, "parens"); + } + + if (type == "vendor-prefixes") { + return pushContext(state, stream, "vendorPrefixes"); + } + if (type == "word") { + var word = stream.current(); + override = wordAsValue(word); + + if (override == "property") { + if (startOfLine(stream)) { + return pushContext(state, stream, "block", 0); + } else { + override = "atom"; + return "block"; + } + } + + if (override == "tag") { + + // tag is a css value + if (/embed|menu|pre|progress|sub|table/.test(word)) { + if (wordIsProperty(firstWordOfLine(stream))) { + override = "atom"; + return "block"; + } + } + + // tag is an attribute + if (stream.string.match(new RegExp("\\[\\s*" + word + "|" + word +"\\s*\\]"))) { + override = "atom"; + return "block"; + } + + // tag is a variable + if (tagVariablesRegexp.test(word)) { + if ((startOfLine(stream) && stream.string.match(/=/)) || + (!startOfLine(stream) && + !stream.string.match(/^(\s*\.|#|\&|\[|\/|>|\*)/) && + !wordIsTag(firstWordOfLine(stream)))) { + override = "variable-2"; + if (wordIsBlock(firstWordOfLine(stream))) return "block"; + return pushContext(state, stream, "block", 0); + } + } + + if (endOfLine(stream)) return pushContext(state, stream, "block"); + } + if (override == "block-keyword") { + override = "keyword"; + + // Postfix conditionals + if (stream.current(/(if|unless)/) && !startOfLine(stream)) { + return "block"; + } + return pushContext(state, stream, "block"); + } + if (word == "return") return pushContext(state, stream, "block", 0); + + // Placeholder selector + if (override == "variable-2" && stream.string.match(/^\s?\$[\w-\.\[\]\'\"]+$/)) { + return pushContext(state, stream, "block"); + } + } + return state.context.type; + }; + + + /** + * Parens + */ + states.parens = function(type, stream, state) { + if (type == "(") return pushContext(state, stream, "parens"); + if (type == ")") { + if (state.context.prev.type == "parens") { + return popContext(state); + } + if ((stream.string.match(/^[a-z][\w-]*\(/i) && endOfLine(stream)) || + wordIsBlock(firstWordOfLine(stream)) || + /(\.|#|:|\[|\*|&|>|~|\+|\/)/.test(firstWordOfLine(stream)) || + (!stream.string.match(/^-?[a-z][\w-\.\[\]\'\"]*\s*=/) && + wordIsTag(firstWordOfLine(stream)))) { + return pushContext(state, stream, "block"); + } + if (stream.string.match(/^[\$-]?[a-z][\w-\.\[\]\'\"]*\s*=/) || + stream.string.match(/^\s*(\(|\)|[0-9])/) || + stream.string.match(/^\s+[a-z][\w-]*\(/i) || + stream.string.match(/^\s+[\$-]?[a-z]/i)) { + return pushContext(state, stream, "block", 0); + } + if (endOfLine(stream)) return pushContext(state, stream, "block"); + else return pushContext(state, stream, "block", 0); + } + if (type && type.charAt(0) == "@" && wordIsProperty(stream.current().slice(1))) { + override = "variable-2"; + } + if (type == "word") { + var word = stream.current(); + override = wordAsValue(word); + if (override == "tag" && tagVariablesRegexp.test(word)) { + override = "variable-2"; + } + if (override == "property" || word == "to") override = "atom"; + } + if (type == "variable-name") { + return pushContext(state, stream, "variableName"); + } + if (typeIsPseudo(type, stream)) { + return pushContext(state, stream, "pseudo"); + } + return state.context.type; + }; + + + /** + * Vendor prefixes + */ + states.vendorPrefixes = function(type, stream, state) { + if (type == "word") { + override = "property"; + return pushContext(state, stream, "block", 0); + } + return popContext(state); + }; + + + /** + * Pseudo + */ + states.pseudo = function(type, stream, state) { + if (!wordIsProperty(firstWordOfLine(stream.string))) { + stream.match(/^[a-z-]+/); + override = "variable-3"; + if (endOfLine(stream)) return pushContext(state, stream, "block"); + return popContext(state); + } + return popAndPass(type, stream, state); + }; + + + /** + * atBlock + */ + states.atBlock = function(type, stream, state) { + if (type == "(") return pushContext(state, stream, "atBlock_parens"); + if (typeIsBlock(type, stream)) { + return pushContext(state, stream, "block"); + } + if (typeIsInterpolation(type, stream)) { + return pushContext(state, stream, "interpolation"); + } + if (type == "word") { + var word = stream.current().toLowerCase(); + if (/^(only|not|and|or)$/.test(word)) + override = "keyword"; + else if (documentTypes.hasOwnProperty(word)) + override = "tag"; + else if (mediaTypes.hasOwnProperty(word)) + override = "attribute"; + else if (mediaFeatures.hasOwnProperty(word)) + override = "property"; + else if (nonStandardPropertyKeywords.hasOwnProperty(word)) + override = "string-2"; + else override = wordAsValue(stream.current()); + if (override == "tag" && endOfLine(stream)) { + return pushContext(state, stream, "block"); + } + } + if (type == "operator" && /^(not|and|or)$/.test(stream.current())) { + override = "keyword"; + } + return state.context.type; + }; + + states.atBlock_parens = function(type, stream, state) { + if (type == "{" || type == "}") return state.context.type; + if (type == ")") { + if (endOfLine(stream)) return pushContext(state, stream, "block"); + else return pushContext(state, stream, "atBlock"); + } + if (type == "word") { + var word = stream.current().toLowerCase(); + override = wordAsValue(word); + if (/^(max|min)/.test(word)) override = "property"; + if (override == "tag") { + tagVariablesRegexp.test(word) ? override = "variable-2" : override = "atom"; + } + return state.context.type; + } + return states.atBlock(type, stream, state); + }; + + + /** + * Keyframes + */ + states.keyframes = function(type, stream, state) { + if (stream.indentation() == "0" && ((type == "}" && startOfLine(stream)) || type == "]" || type == "hash" + || type == "qualifier" || wordIsTag(stream.current()))) { + return popAndPass(type, stream, state); + } + if (type == "{") return pushContext(state, stream, "keyframes"); + if (type == "}") { + if (startOfLine(stream)) return popContext(state, true); + else return pushContext(state, stream, "keyframes"); + } + if (type == "unit" && /^[0-9]+\%$/.test(stream.current())) { + return pushContext(state, stream, "keyframes"); + } + if (type == "word") { + override = wordAsValue(stream.current()); + if (override == "block-keyword") { + override = "keyword"; + return pushContext(state, stream, "keyframes"); + } + } + if (/@(font-face|media|supports|(-moz-)?document)/.test(type)) { + return pushContext(state, stream, endOfLine(stream) ? "block" : "atBlock"); + } + if (type == "mixin") { + return pushContext(state, stream, "block", 0); + } + return state.context.type; + }; + + + /** + * Interpolation + */ + states.interpolation = function(type, stream, state) { + if (type == "{") popContext(state) && pushContext(state, stream, "block"); + if (type == "}") { + if (stream.string.match(/^\s*(\.|#|:|\[|\*|&|>|~|\+|\/)/i) || + (stream.string.match(/^\s*[a-z]/i) && wordIsTag(firstWordOfLine(stream)))) { + return pushContext(state, stream, "block"); + } + if (!stream.string.match(/^(\{|\s*\&)/) || + stream.match(/\s*[\w-]/,false)) { + return pushContext(state, stream, "block", 0); + } + return pushContext(state, stream, "block"); + } + if (type == "variable-name") { + return pushContext(state, stream, "variableName", 0); + } + if (type == "word") { + override = wordAsValue(stream.current()); + if (override == "tag") override = "atom"; + } + return state.context.type; + }; + + + /** + * Extend/s + */ + states.extend = function(type, stream, state) { + if (type == "[" || type == "=") return "extend"; + if (type == "]") return popContext(state); + if (type == "word") { + override = wordAsValue(stream.current()); + return "extend"; + } + return popContext(state); + }; + + + /** + * Variable name + */ + states.variableName = function(type, stream, state) { + if (type == "string" || type == "[" || type == "]" || stream.current().match(/^(\.|\$)/)) { + if (stream.current().match(/^\.[\w-]+/i)) override = "variable-2"; + return "variableName"; + } + return popAndPass(type, stream, state); + }; + + + return { + startState: function(base) { + return { + tokenize: null, + state: "block", + context: new Context("block", base || 0, null) + }; + }, + token: function(stream, state) { + if (!state.tokenize && stream.eatSpace()) return null; + style = (state.tokenize || tokenBase)(stream, state); + if (style && typeof style == "object") { + type = style[1]; + style = style[0]; + } + override = style; + state.state = states[state.state](type, stream, state); + return override; + }, + indent: function(state, textAfter, line) { + + var cx = state.context, + ch = textAfter && textAfter.charAt(0), + indent = cx.indent, + lineFirstWord = firstWordOfLine(textAfter), + lineIndent = line.match(/^\s*/)[0].replace(/\t/g, indentUnitString).length, + prevLineFirstWord = state.context.prev ? state.context.prev.line.firstWord : "", + prevLineIndent = state.context.prev ? state.context.prev.line.indent : lineIndent; + + if (cx.prev && + (ch == "}" && (cx.type == "block" || cx.type == "atBlock" || cx.type == "keyframes") || + ch == ")" && (cx.type == "parens" || cx.type == "atBlock_parens") || + ch == "{" && (cx.type == "at"))) { + indent = cx.indent - indentUnit; + } else if (!(/(\})/.test(ch))) { + if (/@|\$|\d/.test(ch) || + /^\{/.test(textAfter) || +/^\s*\/(\/|\*)/.test(textAfter) || + /^\s*\/\*/.test(prevLineFirstWord) || + /^\s*[\w-\.\[\]\'\"]+\s*(\?|:|\+)?=/i.test(textAfter) || +/^(\+|-)?[a-z][\w-]*\(/i.test(textAfter) || +/^return/.test(textAfter) || + wordIsBlock(lineFirstWord)) { + indent = lineIndent; + } else if (/(\.|#|:|\[|\*|&|>|~|\+|\/)/.test(ch) || wordIsTag(lineFirstWord)) { + if (/\,\s*$/.test(prevLineFirstWord)) { + indent = prevLineIndent; + } else if (/^\s+/.test(line) && (/(\.|#|:|\[|\*|&|>|~|\+|\/)/.test(prevLineFirstWord) || wordIsTag(prevLineFirstWord))) { + indent = lineIndent <= prevLineIndent ? prevLineIndent : prevLineIndent + indentUnit; + } else { + indent = lineIndent; + } + } else if (!/,\s*$/.test(line) && (wordIsVendorPrefix(lineFirstWord) || wordIsProperty(lineFirstWord))) { + if (wordIsBlock(prevLineFirstWord)) { + indent = lineIndent <= prevLineIndent ? prevLineIndent : prevLineIndent + indentUnit; + } else if (/^\{/.test(prevLineFirstWord)) { + indent = lineIndent <= prevLineIndent ? lineIndent : prevLineIndent + indentUnit; + } else if (wordIsVendorPrefix(prevLineFirstWord) || wordIsProperty(prevLineFirstWord)) { + indent = lineIndent >= prevLineIndent ? prevLineIndent : lineIndent; + } else if (/^(\.|#|:|\[|\*|&|@|\+|\-|>|~|\/)/.test(prevLineFirstWord) || + /=\s*$/.test(prevLineFirstWord) || + wordIsTag(prevLineFirstWord) || + /^\$[\w-\.\[\]\'\"]/.test(prevLineFirstWord)) { + indent = prevLineIndent + indentUnit; + } else { + indent = lineIndent; + } + } + } + return indent; + }, + electricChars: "}", + lineComment: "//", + fold: "indent" + }; + }); + + // developer.mozilla.org/en-US/docs/Web/HTML/Element + var tagKeywords_ = ["a","abbr","address","area","article","aside","audio", "b", "base","bdi", "bdo","bgsound","blockquote","body","br","button","canvas","caption","cite", "code","col","colgroup","data","datalist","dd","del","details","dfn","div", "dl","dt","em","embed","fieldset","figcaption","figure","footer","form","h1", "h2","h3","h4","h5","h6","head","header","hgroup","hr","html","i","iframe", "img","input","ins","kbd","keygen","label","legend","li","link","main","map", "mark","marquee","menu","menuitem","meta","meter","nav","nobr","noframes", "noscript","object","ol","optgroup","option","output","p","param","pre", "progress","q","rp","rt","ruby","s","samp","script","section","select", "small","source","span","strong","style","sub","summary","sup","table","tbody","td","textarea","tfoot","th","thead","time","tr","track", "u","ul","var","video"]; + + // github.com/codemirror/CodeMirror/blob/master/mode/css/css.js + var documentTypes_ = ["domain", "regexp", "url", "url-prefix"]; + var mediaTypes_ = ["all","aural","braille","handheld","print","projection","screen","tty","tv","embossed"]; + var mediaFeatures_ = ["width","min-width","max-width","height","min-height","max-height","device-width","min-device-width","max-device-width","device-height","min-device-height","max-device-height","aspect-ratio","min-aspect-ratio","max-aspect-ratio","device-aspect-ratio","min-device-aspect-ratio","max-device-aspect-ratio","color","min-color","max-color","color-index","min-color-index","max-color-index","monochrome","min-monochrome","max-monochrome","resolution","min-resolution","max-resolution","scan","grid"]; + var propertyKeywords_ = ["align-content","align-items","align-self","alignment-adjust","alignment-baseline","anchor-point","animation","animation-delay","animation-direction","animation-duration","animation-fill-mode","animation-iteration-count","animation-name","animation-play-state","animation-timing-function","appearance","azimuth","backface-visibility","background","background-attachment","background-clip","background-color","background-image","background-origin","background-position","background-repeat","background-size","baseline-shift","binding","bleed","bookmark-label","bookmark-level","bookmark-state","bookmark-target","border","border-bottom","border-bottom-color","border-bottom-left-radius","border-bottom-right-radius","border-bottom-style","border-bottom-width","border-collapse","border-color","border-image","border-image-outset","border-image-repeat","border-image-slice","border-image-source","border-image-width","border-left","border-left-color","border-left-style","border-left-width","border-radius","border-right","border-right-color","border-right-style","border-right-width","border-spacing","border-style","border-top","border-top-color","border-top-left-radius","border-top-right-radius","border-top-style","border-top-width","border-width","bottom","box-decoration-break","box-shadow","box-sizing","break-after","break-before","break-inside","caption-side","clear","clip","color","color-profile","column-count","column-fill","column-gap","column-rule","column-rule-color","column-rule-style","column-rule-width","column-span","column-width","columns","content","counter-increment","counter-reset","crop","cue","cue-after","cue-before","cursor","direction","display","dominant-baseline","drop-initial-after-adjust","drop-initial-after-align","drop-initial-before-adjust","drop-initial-before-align","drop-initial-size","drop-initial-value","elevation","empty-cells","fit","fit-position","flex","flex-basis","flex-direction","flex-flow","flex-grow","flex-shrink","flex-wrap","float","float-offset","flow-from","flow-into","font","font-feature-settings","font-family","font-kerning","font-language-override","font-size","font-size-adjust","font-stretch","font-style","font-synthesis","font-variant","font-variant-alternates","font-variant-caps","font-variant-east-asian","font-variant-ligatures","font-variant-numeric","font-variant-position","font-weight","grid","grid-area","grid-auto-columns","grid-auto-flow","grid-auto-position","grid-auto-rows","grid-column","grid-column-end","grid-column-start","grid-row","grid-row-end","grid-row-start","grid-template","grid-template-areas","grid-template-columns","grid-template-rows","hanging-punctuation","height","hyphens","icon","image-orientation","image-rendering","image-resolution","inline-box-align","justify-content","left","letter-spacing","line-break","line-height","line-stacking","line-stacking-ruby","line-stacking-shift","line-stacking-strategy","list-style","list-style-image","list-style-position","list-style-type","margin","margin-bottom","margin-left","margin-right","margin-top","marker-offset","marks","marquee-direction","marquee-loop","marquee-play-count","marquee-speed","marquee-style","max-height","max-width","min-height","min-width","move-to","nav-down","nav-index","nav-left","nav-right","nav-up","object-fit","object-position","opacity","order","orphans","outline","outline-color","outline-offset","outline-style","outline-width","overflow","overflow-style","overflow-wrap","overflow-x","overflow-y","padding","padding-bottom","padding-left","padding-right","padding-top","page","page-break-after","page-break-before","page-break-inside","page-policy","pause","pause-after","pause-before","perspective","perspective-origin","pitch","pitch-range","play-during","position","presentation-level","punctuation-trim","quotes","region-break-after","region-break-before","region-break-inside","region-fragment","rendering-intent","resize","rest","rest-after","rest-before","richness","right","rotation","rotation-point","ruby-align","ruby-overhang","ruby-position","ruby-span","shape-image-threshold","shape-inside","shape-margin","shape-outside","size","speak","speak-as","speak-header","speak-numeral","speak-punctuation","speech-rate","stress","string-set","tab-size","table-layout","target","target-name","target-new","target-position","text-align","text-align-last","text-decoration","text-decoration-color","text-decoration-line","text-decoration-skip","text-decoration-style","text-emphasis","text-emphasis-color","text-emphasis-position","text-emphasis-style","text-height","text-indent","text-justify","text-outline","text-overflow","text-shadow","text-size-adjust","text-space-collapse","text-transform","text-underline-position","text-wrap","top","transform","transform-origin","transform-style","transition","transition-delay","transition-duration","transition-property","transition-timing-function","unicode-bidi","vertical-align","visibility","voice-balance","voice-duration","voice-family","voice-pitch","voice-range","voice-rate","voice-stress","voice-volume","volume","white-space","widows","width","will-change","word-break","word-spacing","word-wrap","z-index","clip-path","clip-rule","mask","enable-background","filter","flood-color","flood-opacity","lighting-color","stop-color","stop-opacity","pointer-events","color-interpolation","color-interpolation-filters","color-rendering","fill","fill-opacity","fill-rule","image-rendering","marker","marker-end","marker-mid","marker-start","shape-rendering","stroke","stroke-dasharray","stroke-dashoffset","stroke-linecap","stroke-linejoin","stroke-miterlimit","stroke-opacity","stroke-width","text-rendering","baseline-shift","dominant-baseline","glyph-orientation-horizontal","glyph-orientation-vertical","text-anchor","writing-mode","font-smoothing","osx-font-smoothing"]; + var nonStandardPropertyKeywords_ = ["scrollbar-arrow-color","scrollbar-base-color","scrollbar-dark-shadow-color","scrollbar-face-color","scrollbar-highlight-color","scrollbar-shadow-color","scrollbar-3d-light-color","scrollbar-track-color","shape-inside","searchfield-cancel-button","searchfield-decoration","searchfield-results-button","searchfield-results-decoration","zoom"]; + var fontProperties_ = ["font-family","src","unicode-range","font-variant","font-feature-settings","font-stretch","font-weight","font-style"]; + var colorKeywords_ = ["aliceblue","antiquewhite","aqua","aquamarine","azure","beige","bisque","black","blanchedalmond","blue","blueviolet","brown","burlywood","cadetblue","chartreuse","chocolate","coral","cornflowerblue","cornsilk","crimson","cyan","darkblue","darkcyan","darkgoldenrod","darkgray","darkgreen","darkkhaki","darkmagenta","darkolivegreen","darkorange","darkorchid","darkred","darksalmon","darkseagreen","darkslateblue","darkslategray","darkturquoise","darkviolet","deeppink","deepskyblue","dimgray","dodgerblue","firebrick","floralwhite","forestgreen","fuchsia","gainsboro","ghostwhite","gold","goldenrod","gray","grey","green","greenyellow","honeydew","hotpink","indianred","indigo","ivory","khaki","lavender","lavenderblush","lawngreen","lemonchiffon","lightblue","lightcoral","lightcyan","lightgoldenrodyellow","lightgray","lightgreen","lightpink","lightsalmon","lightseagreen","lightskyblue","lightslategray","lightsteelblue","lightyellow","lime","limegreen","linen","magenta","maroon","mediumaquamarine","mediumblue","mediumorchid","mediumpurple","mediumseagreen","mediumslateblue","mediumspringgreen","mediumturquoise","mediumvioletred","midnightblue","mintcream","mistyrose","moccasin","navajowhite","navy","oldlace","olive","olivedrab","orange","orangered","orchid","palegoldenrod","palegreen","paleturquoise","palevioletred","papayawhip","peachpuff","peru","pink","plum","powderblue","purple","rebeccapurple","red","rosybrown","royalblue","saddlebrown","salmon","sandybrown","seagreen","seashell","sienna","silver","skyblue","slateblue","slategray","snow","springgreen","steelblue","tan","teal","thistle","tomato","turquoise","violet","wheat","white","whitesmoke","yellow","yellowgreen"]; + var valueKeywords_ = ["above","absolute","activeborder","additive","activecaption","afar","after-white-space","ahead","alias","all","all-scroll","alphabetic","alternate","always","amharic","amharic-abegede","antialiased","appworkspace","arabic-indic","armenian","asterisks","attr","auto","avoid","avoid-column","avoid-page","avoid-region","background","backwards","baseline","below","bidi-override","binary","bengali","blink","block","block-axis","bold","bolder","border","border-box","both","bottom","break","break-all","break-word","bullets","button","button-bevel","buttonface","buttonhighlight","buttonshadow","buttontext","calc","cambodian","capitalize","caps-lock-indicator","caption","captiontext","caret","cell","center","checkbox","circle","cjk-decimal","cjk-earthly-branch","cjk-heavenly-stem","cjk-ideographic","clear","clip","close-quote","col-resize","collapse","column","compact","condensed","contain","content","contents","content-box","context-menu","continuous","copy","counter","counters","cover","crop","cross","crosshair","currentcolor","cursive","cyclic","dashed","decimal","decimal-leading-zero","default","default-button","destination-atop","destination-in","destination-out","destination-over","devanagari","disc","discard","disclosure-closed","disclosure-open","document","dot-dash","dot-dot-dash","dotted","double","down","e-resize","ease","ease-in","ease-in-out","ease-out","element","ellipse","ellipsis","embed","end","ethiopic","ethiopic-abegede","ethiopic-abegede-am-et","ethiopic-abegede-gez","ethiopic-abegede-ti-er","ethiopic-abegede-ti-et","ethiopic-halehame-aa-er","ethiopic-halehame-aa-et","ethiopic-halehame-am-et","ethiopic-halehame-gez","ethiopic-halehame-om-et","ethiopic-halehame-sid-et","ethiopic-halehame-so-et","ethiopic-halehame-ti-er","ethiopic-halehame-ti-et","ethiopic-halehame-tig","ethiopic-numeric","ew-resize","expanded","extends","extra-condensed","extra-expanded","fantasy","fast","fill","fixed","flat","flex","footnotes","forwards","from","geometricPrecision","georgian","graytext","groove","gujarati","gurmukhi","hand","hangul","hangul-consonant","hebrew","help","hidden","hide","higher","highlight","highlighttext","hiragana","hiragana-iroha","horizontal","hsl","hsla","icon","ignore","inactiveborder","inactivecaption","inactivecaptiontext","infinite","infobackground","infotext","inherit","initial","inline","inline-axis","inline-block","inline-flex","inline-table","inset","inside","intrinsic","invert","italic","japanese-formal","japanese-informal","justify","kannada","katakana","katakana-iroha","keep-all","khmer","korean-hangul-formal","korean-hanja-formal","korean-hanja-informal","landscape","lao","large","larger","left","level","lighter","line-through","linear","linear-gradient","lines","list-item","listbox","listitem","local","logical","loud","lower","lower-alpha","lower-armenian","lower-greek","lower-hexadecimal","lower-latin","lower-norwegian","lower-roman","lowercase","ltr","malayalam","match","matrix","matrix3d","media-controls-background","media-current-time-display","media-fullscreen-button","media-mute-button","media-play-button","media-return-to-realtime-button","media-rewind-button","media-seek-back-button","media-seek-forward-button","media-slider","media-sliderthumb","media-time-remaining-display","media-volume-slider","media-volume-slider-container","media-volume-sliderthumb","medium","menu","menulist","menulist-button","menulist-text","menulist-textfield","menutext","message-box","middle","min-intrinsic","mix","mongolian","monospace","move","multiple","myanmar","n-resize","narrower","ne-resize","nesw-resize","no-close-quote","no-drop","no-open-quote","no-repeat","none","normal","not-allowed","nowrap","ns-resize","numbers","numeric","nw-resize","nwse-resize","oblique","octal","open-quote","optimizeLegibility","optimizeSpeed","oriya","oromo","outset","outside","outside-shape","overlay","overline","padding","padding-box","painted","page","paused","persian","perspective","plus-darker","plus-lighter","pointer","polygon","portrait","pre","pre-line","pre-wrap","preserve-3d","progress","push-button","radial-gradient","radio","read-only","read-write","read-write-plaintext-only","rectangle","region","relative","repeat","repeating-linear-gradient","repeating-radial-gradient","repeat-x","repeat-y","reset","reverse","rgb","rgba","ridge","right","rotate","rotate3d","rotateX","rotateY","rotateZ","round","row-resize","rtl","run-in","running","s-resize","sans-serif","scale","scale3d","scaleX","scaleY","scaleZ","scroll","scrollbar","scroll-position","se-resize","searchfield","searchfield-cancel-button","searchfield-decoration","searchfield-results-button","searchfield-results-decoration","semi-condensed","semi-expanded","separate","serif","show","sidama","simp-chinese-formal","simp-chinese-informal","single","skew","skewX","skewY","skip-white-space","slide","slider-horizontal","slider-vertical","sliderthumb-horizontal","sliderthumb-vertical","slow","small","small-caps","small-caption","smaller","solid","somali","source-atop","source-in","source-out","source-over","space","spell-out","square","square-button","start","static","status-bar","stretch","stroke","sub","subpixel-antialiased","super","sw-resize","symbolic","symbols","table","table-caption","table-cell","table-column","table-column-group","table-footer-group","table-header-group","table-row","table-row-group","tamil","telugu","text","text-bottom","text-top","textarea","textfield","thai","thick","thin","threeddarkshadow","threedface","threedhighlight","threedlightshadow","threedshadow","tibetan","tigre","tigrinya-er","tigrinya-er-abegede","tigrinya-et","tigrinya-et-abegede","to","top","trad-chinese-formal","trad-chinese-informal","translate","translate3d","translateX","translateY","translateZ","transparent","ultra-condensed","ultra-expanded","underline","up","upper-alpha","upper-armenian","upper-greek","upper-hexadecimal","upper-latin","upper-norwegian","upper-roman","uppercase","urdu","url","var","vertical","vertical-text","visible","visibleFill","visiblePainted","visibleStroke","visual","w-resize","wait","wave","wider","window","windowframe","windowtext","words","x-large","x-small","xor","xx-large","xx-small","bicubic","optimizespeed","grayscale","row","row-reverse","wrap","wrap-reverse","column-reverse","flex-start","flex-end","space-between","space-around", "unset"]; + + var wordOperatorKeywords_ = ["in","and","or","not","is not","is a","is","isnt","defined","if unless"], + blockKeywords_ = ["for","if","else","unless", "from", "to"], + commonAtoms_ = ["null","true","false","href","title","type","not-allowed","readonly","disabled"], + commonDef_ = ["@font-face", "@keyframes", "@media", "@viewport", "@page", "@host", "@supports", "@block", "@css"]; + + var hintWords = tagKeywords_.concat(documentTypes_,mediaTypes_,mediaFeatures_, + propertyKeywords_,nonStandardPropertyKeywords_, + colorKeywords_,valueKeywords_,fontProperties_, + wordOperatorKeywords_,blockKeywords_, + commonAtoms_,commonDef_); + + function wordRegexp(words) { + words = words.sort(function(a,b){return b > a;}); + return new RegExp("^((" + words.join(")|(") + "))\\b"); + } + + function keySet(array) { + var keys = {}; + for (var i = 0; i < array.length; ++i) keys[array[i]] = true; + return keys; + } + + function escapeRegExp(text) { + return text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"); + } + + CodeMirror.registerHelper("hintWords", "stylus", hintWords); + CodeMirror.defineMIME("text/x-styl", "stylus"); +}); diff --git a/docs/js/node_modules/codemirror/mode/swift/swift.js b/docs/js/node_modules/codemirror/mode/swift/swift.js new file mode 100644 index 000000000..55e31e270 --- /dev/null +++ b/docs/js/node_modules/codemirror/mode/swift/swift.js @@ -0,0 +1,223 @@ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: https://codemirror.net/LICENSE + +// Swift mode created by Michael Kaminsky https://github.com/mkaminsky11 + +(function(mod) { + if (typeof exports == "object" && typeof module == "object") + mod(require("../../lib/codemirror")) + else if (typeof define == "function" && define.amd) + define(["../../lib/codemirror"], mod) + else + mod(CodeMirror) +})(function(CodeMirror) { + "use strict" + + function wordSet(words) { + var set = {} + for (var i = 0; i < words.length; i++) set[words[i]] = true + return set + } + + var keywords = wordSet(["_","var","let","class","enum","extension","import","protocol","struct","func","typealias","associatedtype", + "open","public","internal","fileprivate","private","deinit","init","new","override","self","subscript","super", + "convenience","dynamic","final","indirect","lazy","required","static","unowned","unowned(safe)","unowned(unsafe)","weak","as","is", + "break","case","continue","default","else","fallthrough","for","guard","if","in","repeat","switch","where","while", + "defer","return","inout","mutating","nonmutating","catch","do","rethrows","throw","throws","try","didSet","get","set","willSet", + "assignment","associativity","infix","left","none","operator","postfix","precedence","precedencegroup","prefix","right", + "Any","AnyObject","Type","dynamicType","Self","Protocol","__COLUMN__","__FILE__","__FUNCTION__","__LINE__"]) + var definingKeywords = wordSet(["var","let","class","enum","extension","import","protocol","struct","func","typealias","associatedtype","for"]) + var atoms = wordSet(["true","false","nil","self","super","_"]) + var types = wordSet(["Array","Bool","Character","Dictionary","Double","Float","Int","Int8","Int16","Int32","Int64","Never","Optional","Set","String", + "UInt8","UInt16","UInt32","UInt64","Void"]) + var operators = "+-/*%=|&<>~^?!" + var punc = ":;,.(){}[]" + var binary = /^\-?0b[01][01_]*/ + var octal = /^\-?0o[0-7][0-7_]*/ + var hexadecimal = /^\-?0x[\dA-Fa-f][\dA-Fa-f_]*(?:(?:\.[\dA-Fa-f][\dA-Fa-f_]*)?[Pp]\-?\d[\d_]*)?/ + var decimal = /^\-?\d[\d_]*(?:\.\d[\d_]*)?(?:[Ee]\-?\d[\d_]*)?/ + var identifier = /^\$\d+|(`?)[_A-Za-z][_A-Za-z$0-9]*\1/ + var property = /^\.(?:\$\d+|(`?)[_A-Za-z][_A-Za-z$0-9]*\1)/ + var instruction = /^\#[A-Za-z]+/ + var attribute = /^@(?:\$\d+|(`?)[_A-Za-z][_A-Za-z$0-9]*\1)/ + //var regexp = /^\/(?!\s)(?:\/\/)?(?:\\.|[^\/])+\// + + function tokenBase(stream, state, prev) { + if (stream.sol()) state.indented = stream.indentation() + if (stream.eatSpace()) return null + + var ch = stream.peek() + if (ch == "/") { + if (stream.match("//")) { + stream.skipToEnd() + return "comment" + } + if (stream.match("/*")) { + state.tokenize.push(tokenComment) + return tokenComment(stream, state) + } + } + if (stream.match(instruction)) return "builtin" + if (stream.match(attribute)) return "attribute" + if (stream.match(binary)) return "number" + if (stream.match(octal)) return "number" + if (stream.match(hexadecimal)) return "number" + if (stream.match(decimal)) return "number" + if (stream.match(property)) return "property" + if (operators.indexOf(ch) > -1) { + stream.next() + return "operator" + } + if (punc.indexOf(ch) > -1) { + stream.next() + stream.match("..") + return "punctuation" + } + var stringMatch + if (stringMatch = stream.match(/("""|"|')/)) { + var tokenize = tokenString.bind(null, stringMatch[0]) + state.tokenize.push(tokenize) + return tokenize(stream, state) + } + + if (stream.match(identifier)) { + var ident = stream.current() + if (types.hasOwnProperty(ident)) return "variable-2" + if (atoms.hasOwnProperty(ident)) return "atom" + if (keywords.hasOwnProperty(ident)) { + if (definingKeywords.hasOwnProperty(ident)) + state.prev = "define" + return "keyword" + } + if (prev == "define") return "def" + return "variable" + } + + stream.next() + return null + } + + function tokenUntilClosingParen() { + var depth = 0 + return function(stream, state, prev) { + var inner = tokenBase(stream, state, prev) + if (inner == "punctuation") { + if (stream.current() == "(") ++depth + else if (stream.current() == ")") { + if (depth == 0) { + stream.backUp(1) + state.tokenize.pop() + return state.tokenize[state.tokenize.length - 1](stream, state) + } + else --depth + } + } + return inner + } + } + + function tokenString(openQuote, stream, state) { + var singleLine = openQuote.length == 1 + var ch, escaped = false + while (ch = stream.peek()) { + if (escaped) { + stream.next() + if (ch == "(") { + state.tokenize.push(tokenUntilClosingParen()) + return "string" + } + escaped = false + } else if (stream.match(openQuote)) { + state.tokenize.pop() + return "string" + } else { + stream.next() + escaped = ch == "\\" + } + } + if (singleLine) { + state.tokenize.pop() + } + return "string" + } + + function tokenComment(stream, state) { + var ch + while (true) { + stream.match(/^[^/*]+/, true) + ch = stream.next() + if (!ch) break + if (ch === "/" && stream.eat("*")) { + state.tokenize.push(tokenComment) + } else if (ch === "*" && stream.eat("/")) { + state.tokenize.pop() + } + } + return "comment" + } + + function Context(prev, align, indented) { + this.prev = prev + this.align = align + this.indented = indented + } + + function pushContext(state, stream) { + var align = stream.match(/^\s*($|\/[\/\*])/, false) ? null : stream.column() + 1 + state.context = new Context(state.context, align, state.indented) + } + + function popContext(state) { + if (state.context) { + state.indented = state.context.indented + state.context = state.context.prev + } + } + + CodeMirror.defineMode("swift", function(config) { + return { + startState: function() { + return { + prev: null, + context: null, + indented: 0, + tokenize: [] + } + }, + + token: function(stream, state) { + var prev = state.prev + state.prev = null + var tokenize = state.tokenize[state.tokenize.length - 1] || tokenBase + var style = tokenize(stream, state, prev) + if (!style || style == "comment") state.prev = prev + else if (!state.prev) state.prev = style + + if (style == "punctuation") { + var bracket = /[\(\[\{]|([\]\)\}])/.exec(stream.current()) + if (bracket) (bracket[1] ? popContext : pushContext)(state, stream) + } + + return style + }, + + indent: function(state, textAfter) { + var cx = state.context + if (!cx) return 0 + var closing = /^[\]\}\)]/.test(textAfter) + if (cx.align != null) return cx.align - (closing ? 1 : 0) + return cx.indented + (closing ? 0 : config.indentUnit) + }, + + electricInput: /^\s*[\)\}\]]$/, + + lineComment: "//", + blockCommentStart: "/*", + blockCommentEnd: "*/", + fold: "brace", + closeBrackets: "()[]{}''\"\"``" + } + }) + + CodeMirror.defineMIME("text/x-swift","swift") +}); diff --git a/docs/js/node_modules/codemirror/mode/tcl/tcl.js b/docs/js/node_modules/codemirror/mode/tcl/tcl.js new file mode 100644 index 000000000..a7ec89c9e --- /dev/null +++ b/docs/js/node_modules/codemirror/mode/tcl/tcl.js @@ -0,0 +1,139 @@ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: https://codemirror.net/LICENSE + +//tcl mode by Ford_Lawnmower :: Based on Velocity mode by Steve O'Hara + +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + +CodeMirror.defineMode("tcl", function() { + function parseWords(str) { + var obj = {}, words = str.split(" "); + for (var i = 0; i < words.length; ++i) obj[words[i]] = true; + return obj; + } + var keywords = parseWords("Tcl safe after append array auto_execok auto_import auto_load " + + "auto_mkindex auto_mkindex_old auto_qualify auto_reset bgerror " + + "binary break catch cd close concat continue dde eof encoding error " + + "eval exec exit expr fblocked fconfigure fcopy file fileevent filename " + + "filename flush for foreach format gets glob global history http if " + + "incr info interp join lappend lindex linsert list llength load lrange " + + "lreplace lsearch lset lsort memory msgcat namespace open package parray " + + "pid pkg::create pkg_mkIndex proc puts pwd re_syntax read regex regexp " + + "registry regsub rename resource return scan seek set socket source split " + + "string subst switch tcl_endOfWord tcl_findLibrary tcl_startOfNextWord " + + "tcl_wordBreakAfter tcl_startOfPreviousWord tcl_wordBreakBefore tcltest " + + "tclvars tell time trace unknown unset update uplevel upvar variable " + + "vwait"); + var functions = parseWords("if elseif else and not or eq ne in ni for foreach while switch"); + var isOperatorChar = /[+\-*&%=<>!?^\/\|]/; + function chain(stream, state, f) { + state.tokenize = f; + return f(stream, state); + } + function tokenBase(stream, state) { + var beforeParams = state.beforeParams; + state.beforeParams = false; + var ch = stream.next(); + if ((ch == '"' || ch == "'") && state.inParams) { + return chain(stream, state, tokenString(ch)); + } else if (/[\[\]{}\(\),;\.]/.test(ch)) { + if (ch == "(" && beforeParams) state.inParams = true; + else if (ch == ")") state.inParams = false; + return null; + } else if (/\d/.test(ch)) { + stream.eatWhile(/[\w\.]/); + return "number"; + } else if (ch == "#") { + if (stream.eat("*")) + return chain(stream, state, tokenComment); + if (ch == "#" && stream.match(/ *\[ *\[/)) + return chain(stream, state, tokenUnparsed); + stream.skipToEnd(); + return "comment"; + } else if (ch == '"') { + stream.skipTo(/"/); + return "comment"; + } else if (ch == "$") { + stream.eatWhile(/[$_a-z0-9A-Z\.{:]/); + stream.eatWhile(/}/); + state.beforeParams = true; + return "builtin"; + } else if (isOperatorChar.test(ch)) { + stream.eatWhile(isOperatorChar); + return "comment"; + } else { + stream.eatWhile(/[\w\$_{}\xa1-\uffff]/); + var word = stream.current().toLowerCase(); + if (keywords && keywords.propertyIsEnumerable(word)) + return "keyword"; + if (functions && functions.propertyIsEnumerable(word)) { + state.beforeParams = true; + return "keyword"; + } + return null; + } + } + function tokenString(quote) { + return function(stream, state) { + var escaped = false, next, end = false; + while ((next = stream.next()) != null) { + if (next == quote && !escaped) { + end = true; + break; + } + escaped = !escaped && next == "\\"; + } + if (end) state.tokenize = tokenBase; + return "string"; + }; + } + function tokenComment(stream, state) { + var maybeEnd = false, ch; + while (ch = stream.next()) { + if (ch == "#" && maybeEnd) { + state.tokenize = tokenBase; + break; + } + maybeEnd = (ch == "*"); + } + return "comment"; + } + function tokenUnparsed(stream, state) { + var maybeEnd = 0, ch; + while (ch = stream.next()) { + if (ch == "#" && maybeEnd == 2) { + state.tokenize = tokenBase; + break; + } + if (ch == "]") + maybeEnd++; + else if (ch != " ") + maybeEnd = 0; + } + return "meta"; + } + return { + startState: function() { + return { + tokenize: tokenBase, + beforeParams: false, + inParams: false + }; + }, + token: function(stream, state) { + if (stream.eatSpace()) return null; + return state.tokenize(stream, state); + } + }; +}); +CodeMirror.defineMIME("text/x-tcl", "tcl"); + +}); diff --git a/docs/js/node_modules/codemirror/mode/textile/textile.js b/docs/js/node_modules/codemirror/mode/textile/textile.js new file mode 100644 index 000000000..b378fb61f --- /dev/null +++ b/docs/js/node_modules/codemirror/mode/textile/textile.js @@ -0,0 +1,469 @@ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: https://codemirror.net/LICENSE + +(function(mod) { + if (typeof exports == "object" && typeof module == "object") { // CommonJS + mod(require("../../lib/codemirror")); + } else if (typeof define == "function" && define.amd) { // AMD + define(["../../lib/codemirror"], mod); + } else { // Plain browser env + mod(CodeMirror); + } +})(function(CodeMirror) { + "use strict"; + + var TOKEN_STYLES = { + addition: "positive", + attributes: "attribute", + bold: "strong", + cite: "keyword", + code: "atom", + definitionList: "number", + deletion: "negative", + div: "punctuation", + em: "em", + footnote: "variable", + footCite: "qualifier", + header: "header", + html: "comment", + image: "string", + italic: "em", + link: "link", + linkDefinition: "link", + list1: "variable-2", + list2: "variable-3", + list3: "keyword", + notextile: "string-2", + pre: "operator", + p: "property", + quote: "bracket", + span: "quote", + specialChar: "tag", + strong: "strong", + sub: "builtin", + sup: "builtin", + table: "variable-3", + tableHeading: "operator" + }; + + function startNewLine(stream, state) { + state.mode = Modes.newLayout; + state.tableHeading = false; + + if (state.layoutType === "definitionList" && state.spanningLayout && + stream.match(RE("definitionListEnd"), false)) + state.spanningLayout = false; + } + + function handlePhraseModifier(stream, state, ch) { + if (ch === "_") { + if (stream.eat("_")) + return togglePhraseModifier(stream, state, "italic", /__/, 2); + else + return togglePhraseModifier(stream, state, "em", /_/, 1); + } + + if (ch === "*") { + if (stream.eat("*")) { + return togglePhraseModifier(stream, state, "bold", /\*\*/, 2); + } + return togglePhraseModifier(stream, state, "strong", /\*/, 1); + } + + if (ch === "[") { + if (stream.match(/\d+\]/)) state.footCite = true; + return tokenStyles(state); + } + + if (ch === "(") { + var spec = stream.match(/^(r|tm|c)\)/); + if (spec) + return tokenStylesWith(state, TOKEN_STYLES.specialChar); + } + + if (ch === "<" && stream.match(/(\w+)[^>]+>[^<]+<\/\1>/)) + return tokenStylesWith(state, TOKEN_STYLES.html); + + if (ch === "?" && stream.eat("?")) + return togglePhraseModifier(stream, state, "cite", /\?\?/, 2); + + if (ch === "=" && stream.eat("=")) + return togglePhraseModifier(stream, state, "notextile", /==/, 2); + + if (ch === "-" && !stream.eat("-")) + return togglePhraseModifier(stream, state, "deletion", /-/, 1); + + if (ch === "+") + return togglePhraseModifier(stream, state, "addition", /\+/, 1); + + if (ch === "~") + return togglePhraseModifier(stream, state, "sub", /~/, 1); + + if (ch === "^") + return togglePhraseModifier(stream, state, "sup", /\^/, 1); + + if (ch === "%") + return togglePhraseModifier(stream, state, "span", /%/, 1); + + if (ch === "@") + return togglePhraseModifier(stream, state, "code", /@/, 1); + + if (ch === "!") { + var type = togglePhraseModifier(stream, state, "image", /(?:\([^\)]+\))?!/, 1); + stream.match(/^:\S+/); // optional Url portion + return type; + } + return tokenStyles(state); + } + + function togglePhraseModifier(stream, state, phraseModifier, closeRE, openSize) { + var charBefore = stream.pos > openSize ? stream.string.charAt(stream.pos - openSize - 1) : null; + var charAfter = stream.peek(); + if (state[phraseModifier]) { + if ((!charAfter || /\W/.test(charAfter)) && charBefore && /\S/.test(charBefore)) { + var type = tokenStyles(state); + state[phraseModifier] = false; + return type; + } + } else if ((!charBefore || /\W/.test(charBefore)) && charAfter && /\S/.test(charAfter) && + stream.match(new RegExp("^.*\\S" + closeRE.source + "(?:\\W|$)"), false)) { + state[phraseModifier] = true; + state.mode = Modes.attributes; + } + return tokenStyles(state); + }; + + function tokenStyles(state) { + var disabled = textileDisabled(state); + if (disabled) return disabled; + + var styles = []; + if (state.layoutType) styles.push(TOKEN_STYLES[state.layoutType]); + + styles = styles.concat(activeStyles( + state, "addition", "bold", "cite", "code", "deletion", "em", "footCite", + "image", "italic", "link", "span", "strong", "sub", "sup", "table", "tableHeading")); + + if (state.layoutType === "header") + styles.push(TOKEN_STYLES.header + "-" + state.header); + + return styles.length ? styles.join(" ") : null; + } + + function textileDisabled(state) { + var type = state.layoutType; + + switch(type) { + case "notextile": + case "code": + case "pre": + return TOKEN_STYLES[type]; + default: + if (state.notextile) + return TOKEN_STYLES.notextile + (type ? (" " + TOKEN_STYLES[type]) : ""); + return null; + } + } + + function tokenStylesWith(state, extraStyles) { + var disabled = textileDisabled(state); + if (disabled) return disabled; + + var type = tokenStyles(state); + if (extraStyles) + return type ? (type + " " + extraStyles) : extraStyles; + else + return type; + } + + function activeStyles(state) { + var styles = []; + for (var i = 1; i < arguments.length; ++i) { + if (state[arguments[i]]) + styles.push(TOKEN_STYLES[arguments[i]]); + } + return styles; + } + + function blankLine(state) { + var spanningLayout = state.spanningLayout, type = state.layoutType; + + for (var key in state) if (state.hasOwnProperty(key)) + delete state[key]; + + state.mode = Modes.newLayout; + if (spanningLayout) { + state.layoutType = type; + state.spanningLayout = true; + } + } + + var REs = { + cache: {}, + single: { + bc: "bc", + bq: "bq", + definitionList: /- .*?:=+/, + definitionListEnd: /.*=:\s*$/, + div: "div", + drawTable: /\|.*\|/, + foot: /fn\d+/, + header: /h[1-6]/, + html: /\s*<(?:\/)?(\w+)(?:[^>]+)?>(?:[^<]+<\/\1>)?/, + link: /[^"]+":\S/, + linkDefinition: /\[[^\s\]]+\]\S+/, + list: /(?:#+|\*+)/, + notextile: "notextile", + para: "p", + pre: "pre", + table: "table", + tableCellAttributes: /[\/\\]\d+/, + tableHeading: /\|_\./, + tableText: /[^"_\*\[\(\?\+~\^%@|-]+/, + text: /[^!"_=\*\[\(<\?\+~\^%@-]+/ + }, + attributes: { + align: /(?:<>|<|>|=)/, + selector: /\([^\(][^\)]+\)/, + lang: /\[[^\[\]]+\]/, + pad: /(?:\(+|\)+){1,2}/, + css: /\{[^\}]+\}/ + }, + createRe: function(name) { + switch (name) { + case "drawTable": + return REs.makeRe("^", REs.single.drawTable, "$"); + case "html": + return REs.makeRe("^", REs.single.html, "(?:", REs.single.html, ")*", "$"); + case "linkDefinition": + return REs.makeRe("^", REs.single.linkDefinition, "$"); + case "listLayout": + return REs.makeRe("^", REs.single.list, RE("allAttributes"), "*\\s+"); + case "tableCellAttributes": + return REs.makeRe("^", REs.choiceRe(REs.single.tableCellAttributes, + RE("allAttributes")), "+\\."); + case "type": + return REs.makeRe("^", RE("allTypes")); + case "typeLayout": + return REs.makeRe("^", RE("allTypes"), RE("allAttributes"), + "*\\.\\.?", "(\\s+|$)"); + case "attributes": + return REs.makeRe("^", RE("allAttributes"), "+"); + + case "allTypes": + return REs.choiceRe(REs.single.div, REs.single.foot, + REs.single.header, REs.single.bc, REs.single.bq, + REs.single.notextile, REs.single.pre, REs.single.table, + REs.single.para); + + case "allAttributes": + return REs.choiceRe(REs.attributes.selector, REs.attributes.css, + REs.attributes.lang, REs.attributes.align, REs.attributes.pad); + + default: + return REs.makeRe("^", REs.single[name]); + } + }, + makeRe: function() { + var pattern = ""; + for (var i = 0; i < arguments.length; ++i) { + var arg = arguments[i]; + pattern += (typeof arg === "string") ? arg : arg.source; + } + return new RegExp(pattern); + }, + choiceRe: function() { + var parts = [arguments[0]]; + for (var i = 1; i < arguments.length; ++i) { + parts[i * 2 - 1] = "|"; + parts[i * 2] = arguments[i]; + } + + parts.unshift("(?:"); + parts.push(")"); + return REs.makeRe.apply(null, parts); + } + }; + + function RE(name) { + return (REs.cache[name] || (REs.cache[name] = REs.createRe(name))); + } + + var Modes = { + newLayout: function(stream, state) { + if (stream.match(RE("typeLayout"), false)) { + state.spanningLayout = false; + return (state.mode = Modes.blockType)(stream, state); + } + var newMode; + if (!textileDisabled(state)) { + if (stream.match(RE("listLayout"), false)) + newMode = Modes.list; + else if (stream.match(RE("drawTable"), false)) + newMode = Modes.table; + else if (stream.match(RE("linkDefinition"), false)) + newMode = Modes.linkDefinition; + else if (stream.match(RE("definitionList"))) + newMode = Modes.definitionList; + else if (stream.match(RE("html"), false)) + newMode = Modes.html; + } + return (state.mode = (newMode || Modes.text))(stream, state); + }, + + blockType: function(stream, state) { + var match, type; + state.layoutType = null; + + if (match = stream.match(RE("type"))) + type = match[0]; + else + return (state.mode = Modes.text)(stream, state); + + if (match = type.match(RE("header"))) { + state.layoutType = "header"; + state.header = parseInt(match[0][1]); + } else if (type.match(RE("bq"))) { + state.layoutType = "quote"; + } else if (type.match(RE("bc"))) { + state.layoutType = "code"; + } else if (type.match(RE("foot"))) { + state.layoutType = "footnote"; + } else if (type.match(RE("notextile"))) { + state.layoutType = "notextile"; + } else if (type.match(RE("pre"))) { + state.layoutType = "pre"; + } else if (type.match(RE("div"))) { + state.layoutType = "div"; + } else if (type.match(RE("table"))) { + state.layoutType = "table"; + } + + state.mode = Modes.attributes; + return tokenStyles(state); + }, + + text: function(stream, state) { + if (stream.match(RE("text"))) return tokenStyles(state); + + var ch = stream.next(); + if (ch === '"') + return (state.mode = Modes.link)(stream, state); + return handlePhraseModifier(stream, state, ch); + }, + + attributes: function(stream, state) { + state.mode = Modes.layoutLength; + + if (stream.match(RE("attributes"))) + return tokenStylesWith(state, TOKEN_STYLES.attributes); + else + return tokenStyles(state); + }, + + layoutLength: function(stream, state) { + if (stream.eat(".") && stream.eat(".")) + state.spanningLayout = true; + + state.mode = Modes.text; + return tokenStyles(state); + }, + + list: function(stream, state) { + var match = stream.match(RE("list")); + state.listDepth = match[0].length; + var listMod = (state.listDepth - 1) % 3; + if (!listMod) + state.layoutType = "list1"; + else if (listMod === 1) + state.layoutType = "list2"; + else + state.layoutType = "list3"; + + state.mode = Modes.attributes; + return tokenStyles(state); + }, + + link: function(stream, state) { + state.mode = Modes.text; + if (stream.match(RE("link"))) { + stream.match(/\S+/); + return tokenStylesWith(state, TOKEN_STYLES.link); + } + return tokenStyles(state); + }, + + linkDefinition: function(stream, state) { + stream.skipToEnd(); + return tokenStylesWith(state, TOKEN_STYLES.linkDefinition); + }, + + definitionList: function(stream, state) { + stream.match(RE("definitionList")); + + state.layoutType = "definitionList"; + + if (stream.match(/\s*$/)) + state.spanningLayout = true; + else + state.mode = Modes.attributes; + + return tokenStyles(state); + }, + + html: function(stream, state) { + stream.skipToEnd(); + return tokenStylesWith(state, TOKEN_STYLES.html); + }, + + table: function(stream, state) { + state.layoutType = "table"; + return (state.mode = Modes.tableCell)(stream, state); + }, + + tableCell: function(stream, state) { + if (stream.match(RE("tableHeading"))) + state.tableHeading = true; + else + stream.eat("|"); + + state.mode = Modes.tableCellAttributes; + return tokenStyles(state); + }, + + tableCellAttributes: function(stream, state) { + state.mode = Modes.tableText; + + if (stream.match(RE("tableCellAttributes"))) + return tokenStylesWith(state, TOKEN_STYLES.attributes); + else + return tokenStyles(state); + }, + + tableText: function(stream, state) { + if (stream.match(RE("tableText"))) + return tokenStyles(state); + + if (stream.peek() === "|") { // end of cell + state.mode = Modes.tableCell; + return tokenStyles(state); + } + return handlePhraseModifier(stream, state, stream.next()); + } + }; + + CodeMirror.defineMode("textile", function() { + return { + startState: function() { + return { mode: Modes.newLayout }; + }, + token: function(stream, state) { + if (stream.sol()) startNewLine(stream, state); + return state.mode(stream, state); + }, + blankLine: blankLine + }; + }); + + CodeMirror.defineMIME("text/x-textile", "textile"); +}); diff --git a/docs/js/node_modules/codemirror/mode/tiddlywiki/tiddlywiki.css b/docs/js/node_modules/codemirror/mode/tiddlywiki/tiddlywiki.css new file mode 100644 index 000000000..9a69b639f --- /dev/null +++ b/docs/js/node_modules/codemirror/mode/tiddlywiki/tiddlywiki.css @@ -0,0 +1,14 @@ +span.cm-underlined { + text-decoration: underline; +} +span.cm-strikethrough { + text-decoration: line-through; +} +span.cm-brace { + color: #170; + font-weight: bold; +} +span.cm-table { + color: blue; + font-weight: bold; +} diff --git a/docs/js/node_modules/codemirror/mode/tiddlywiki/tiddlywiki.js b/docs/js/node_modules/codemirror/mode/tiddlywiki/tiddlywiki.js new file mode 100644 index 000000000..a4fb89f65 --- /dev/null +++ b/docs/js/node_modules/codemirror/mode/tiddlywiki/tiddlywiki.js @@ -0,0 +1,308 @@ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: https://codemirror.net/LICENSE + +/*** + |''Name''|tiddlywiki.js| + |''Description''|Enables TiddlyWikiy syntax highlighting using CodeMirror| + |''Author''|PMario| + |''Version''|0.1.7| + |''Status''|''stable''| + |''Source''|[[GitHub|https://github.com/pmario/CodeMirror2/blob/tw-syntax/mode/tiddlywiki]]| + |''Documentation''|https://codemirror.tiddlyspace.com/| + |''License''|[[MIT License|http://www.opensource.org/licenses/mit-license.php]]| + |''CoreVersion''|2.5.0| + |''Requires''|codemirror.js| + |''Keywords''|syntax highlighting color code mirror codemirror| + ! Info + CoreVersion parameter is needed for TiddlyWiki only! +***/ + +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + +CodeMirror.defineMode("tiddlywiki", function () { + // Tokenizer + var textwords = {}; + + var keywords = { + "allTags": true, "closeAll": true, "list": true, + "newJournal": true, "newTiddler": true, + "permaview": true, "saveChanges": true, + "search": true, "slider": true, "tabs": true, + "tag": true, "tagging": true, "tags": true, + "tiddler": true, "timeline": true, + "today": true, "version": true, "option": true, + "with": true, "filter": true + }; + + var isSpaceName = /[\w_\-]/i, + reHR = /^\-\-\-\-+$/, //
      + reWikiCommentStart = /^\/\*\*\*$/, // /*** + reWikiCommentStop = /^\*\*\*\/$/, // ***/ + reBlockQuote = /^<<<$/, + + reJsCodeStart = /^\/\/\{\{\{$/, // //{{{ js block start + reJsCodeStop = /^\/\/\}\}\}$/, // //}}} js stop + reXmlCodeStart = /^$/, // xml block start + reXmlCodeStop = /^$/, // xml stop + + reCodeBlockStart = /^\{\{\{$/, // {{{ TW text div block start + reCodeBlockStop = /^\}\}\}$/, // }}} TW text stop + + reUntilCodeStop = /.*?\}\}\}/; + + function chain(stream, state, f) { + state.tokenize = f; + return f(stream, state); + } + + function tokenBase(stream, state) { + var sol = stream.sol(), ch = stream.peek(); + + state.block = false; // indicates the start of a code block. + + // check start of blocks + if (sol && /[<\/\*{}\-]/.test(ch)) { + if (stream.match(reCodeBlockStart)) { + state.block = true; + return chain(stream, state, twTokenCode); + } + if (stream.match(reBlockQuote)) + return 'quote'; + if (stream.match(reWikiCommentStart) || stream.match(reWikiCommentStop)) + return 'comment'; + if (stream.match(reJsCodeStart) || stream.match(reJsCodeStop) || stream.match(reXmlCodeStart) || stream.match(reXmlCodeStop)) + return 'comment'; + if (stream.match(reHR)) + return 'hr'; + } + + stream.next(); + if (sol && /[\/\*!#;:>|]/.test(ch)) { + if (ch == "!") { // tw header + stream.skipToEnd(); + return "header"; + } + if (ch == "*") { // tw list + stream.eatWhile('*'); + return "comment"; + } + if (ch == "#") { // tw numbered list + stream.eatWhile('#'); + return "comment"; + } + if (ch == ";") { // definition list, term + stream.eatWhile(';'); + return "comment"; + } + if (ch == ":") { // definition list, description + stream.eatWhile(':'); + return "comment"; + } + if (ch == ">") { // single line quote + stream.eatWhile(">"); + return "quote"; + } + if (ch == '|') + return 'header'; + } + + if (ch == '{' && stream.match(/\{\{/)) + return chain(stream, state, twTokenCode); + + // rudimentary html:// file:// link matching. TW knows much more ... + if (/[hf]/i.test(ch) && + /[ti]/i.test(stream.peek()) && + stream.match(/\b(ttps?|tp|ile):\/\/[\-A-Z0-9+&@#\/%?=~_|$!:,.;]*[A-Z0-9+&@#\/%=~_|$]/i)) + return "link"; + + // just a little string indicator, don't want to have the whole string covered + if (ch == '"') + return 'string'; + + if (ch == '~') // _no_ CamelCase indicator should be bold + return 'brace'; + + if (/[\[\]]/.test(ch) && stream.match(ch)) // check for [[..]] + return 'brace'; + + if (ch == "@") { // check for space link. TODO fix @@...@@ highlighting + stream.eatWhile(isSpaceName); + return "link"; + } + + if (/\d/.test(ch)) { // numbers + stream.eatWhile(/\d/); + return "number"; + } + + if (ch == "/") { // tw invisible comment + if (stream.eat("%")) { + return chain(stream, state, twTokenComment); + } else if (stream.eat("/")) { // + return chain(stream, state, twTokenEm); + } + } + + if (ch == "_" && stream.eat("_")) // tw underline + return chain(stream, state, twTokenUnderline); + + // strikethrough and mdash handling + if (ch == "-" && stream.eat("-")) { + // if strikethrough looks ugly, change CSS. + if (stream.peek() != ' ') + return chain(stream, state, twTokenStrike); + // mdash + if (stream.peek() == ' ') + return 'brace'; + } + + if (ch == "'" && stream.eat("'")) // tw bold + return chain(stream, state, twTokenStrong); + + if (ch == "<" && stream.eat("<")) // tw macro + return chain(stream, state, twTokenMacro); + + // core macro handling + stream.eatWhile(/[\w\$_]/); + return textwords.propertyIsEnumerable(stream.current()) ? "keyword" : null + } + + // tw invisible comment + function twTokenComment(stream, state) { + var maybeEnd = false, ch; + while (ch = stream.next()) { + if (ch == "/" && maybeEnd) { + state.tokenize = tokenBase; + break; + } + maybeEnd = (ch == "%"); + } + return "comment"; + } + + // tw strong / bold + function twTokenStrong(stream, state) { + var maybeEnd = false, + ch; + while (ch = stream.next()) { + if (ch == "'" && maybeEnd) { + state.tokenize = tokenBase; + break; + } + maybeEnd = (ch == "'"); + } + return "strong"; + } + + // tw code + function twTokenCode(stream, state) { + var sb = state.block; + + if (sb && stream.current()) { + return "comment"; + } + + if (!sb && stream.match(reUntilCodeStop)) { + state.tokenize = tokenBase; + return "comment"; + } + + if (sb && stream.sol() && stream.match(reCodeBlockStop)) { + state.tokenize = tokenBase; + return "comment"; + } + + stream.next(); + return "comment"; + } + + // tw em / italic + function twTokenEm(stream, state) { + var maybeEnd = false, + ch; + while (ch = stream.next()) { + if (ch == "/" && maybeEnd) { + state.tokenize = tokenBase; + break; + } + maybeEnd = (ch == "/"); + } + return "em"; + } + + // tw underlined text + function twTokenUnderline(stream, state) { + var maybeEnd = false, + ch; + while (ch = stream.next()) { + if (ch == "_" && maybeEnd) { + state.tokenize = tokenBase; + break; + } + maybeEnd = (ch == "_"); + } + return "underlined"; + } + + // tw strike through text looks ugly + // change CSS if needed + function twTokenStrike(stream, state) { + var maybeEnd = false, ch; + + while (ch = stream.next()) { + if (ch == "-" && maybeEnd) { + state.tokenize = tokenBase; + break; + } + maybeEnd = (ch == "-"); + } + return "strikethrough"; + } + + // macro + function twTokenMacro(stream, state) { + if (stream.current() == '<<') { + return 'macro'; + } + + var ch = stream.next(); + if (!ch) { + state.tokenize = tokenBase; + return null; + } + if (ch == ">") { + if (stream.peek() == '>') { + stream.next(); + state.tokenize = tokenBase; + return "macro"; + } + } + + stream.eatWhile(/[\w\$_]/); + return keywords.propertyIsEnumerable(stream.current()) ? "keyword" : null + } + + // Interface + return { + startState: function () { + return {tokenize: tokenBase}; + }, + + token: function (stream, state) { + if (stream.eatSpace()) return null; + var style = state.tokenize(stream, state); + return style; + } + }; +}); + +CodeMirror.defineMIME("text/x-tiddlywiki", "tiddlywiki"); +}); diff --git a/docs/js/node_modules/codemirror/mode/tiki/tiki.css b/docs/js/node_modules/codemirror/mode/tiki/tiki.css new file mode 100644 index 000000000..1d8704c78 --- /dev/null +++ b/docs/js/node_modules/codemirror/mode/tiki/tiki.css @@ -0,0 +1,26 @@ +.cm-tw-syntaxerror { + color: #FFF; + background-color: #900; +} + +.cm-tw-deleted { + text-decoration: line-through; +} + +.cm-tw-header5 { + font-weight: bold; +} +.cm-tw-listitem:first-child { /*Added first child to fix duplicate padding when highlighting*/ + padding-left: 10px; +} + +.cm-tw-box { + border-top-width: 0px !important; + border-style: solid; + border-width: 1px; + border-color: inherit; +} + +.cm-tw-underline { + text-decoration: underline; +} \ No newline at end of file diff --git a/docs/js/node_modules/codemirror/mode/tiki/tiki.js b/docs/js/node_modules/codemirror/mode/tiki/tiki.js new file mode 100644 index 000000000..092b85953 --- /dev/null +++ b/docs/js/node_modules/codemirror/mode/tiki/tiki.js @@ -0,0 +1,312 @@ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: https://codemirror.net/LICENSE + +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + +CodeMirror.defineMode('tiki', function(config) { + function inBlock(style, terminator, returnTokenizer) { + return function(stream, state) { + while (!stream.eol()) { + if (stream.match(terminator)) { + state.tokenize = inText; + break; + } + stream.next(); + } + + if (returnTokenizer) state.tokenize = returnTokenizer; + + return style; + }; + } + + function inLine(style) { + return function(stream, state) { + while(!stream.eol()) { + stream.next(); + } + state.tokenize = inText; + return style; + }; + } + + function inText(stream, state) { + function chain(parser) { + state.tokenize = parser; + return parser(stream, state); + } + + var sol = stream.sol(); + var ch = stream.next(); + + //non start of line + switch (ch) { //switch is generally much faster than if, so it is used here + case "{": //plugin + stream.eat("/"); + stream.eatSpace(); + stream.eatWhile(/[^\s\u00a0=\"\'\/?(}]/); + state.tokenize = inPlugin; + return "tag"; + case "_": //bold + if (stream.eat("_")) + return chain(inBlock("strong", "__", inText)); + break; + case "'": //italics + if (stream.eat("'")) + return chain(inBlock("em", "''", inText)); + break; + case "(":// Wiki Link + if (stream.eat("(")) + return chain(inBlock("variable-2", "))", inText)); + break; + case "[":// Weblink + return chain(inBlock("variable-3", "]", inText)); + break; + case "|": //table + if (stream.eat("|")) + return chain(inBlock("comment", "||")); + break; + case "-": + if (stream.eat("=")) {//titleBar + return chain(inBlock("header string", "=-", inText)); + } else if (stream.eat("-")) {//deleted + return chain(inBlock("error tw-deleted", "--", inText)); + } + break; + case "=": //underline + if (stream.match("==")) + return chain(inBlock("tw-underline", "===", inText)); + break; + case ":": + if (stream.eat(":")) + return chain(inBlock("comment", "::")); + break; + case "^": //box + return chain(inBlock("tw-box", "^")); + break; + case "~": //np + if (stream.match("np~")) + return chain(inBlock("meta", "~/np~")); + break; + } + + //start of line types + if (sol) { + switch (ch) { + case "!": //header at start of line + if (stream.match('!!!!!')) { + return chain(inLine("header string")); + } else if (stream.match('!!!!')) { + return chain(inLine("header string")); + } else if (stream.match('!!!')) { + return chain(inLine("header string")); + } else if (stream.match('!!')) { + return chain(inLine("header string")); + } else { + return chain(inLine("header string")); + } + break; + case "*": //unordered list line item, or
    • at start of line + case "#": //ordered list line item, or
    • at start of line + case "+": //ordered list line item, or
    • at start of line + return chain(inLine("tw-listitem bracket")); + break; + } + } + + //stream.eatWhile(/[&{]/); was eating up plugins, turned off to act less like html and more like tiki + return null; + } + + var indentUnit = config.indentUnit; + + // Return variables for tokenizers + var pluginName, type; + function inPlugin(stream, state) { + var ch = stream.next(); + var peek = stream.peek(); + + if (ch == "}") { + state.tokenize = inText; + //type = ch == ")" ? "endPlugin" : "selfclosePlugin"; inPlugin + return "tag"; + } else if (ch == "(" || ch == ")") { + return "bracket"; + } else if (ch == "=") { + type = "equals"; + + if (peek == ">") { + stream.next(); + peek = stream.peek(); + } + + //here we detect values directly after equal character with no quotes + if (!/[\'\"]/.test(peek)) { + state.tokenize = inAttributeNoQuote(); + } + //end detect values + + return "operator"; + } else if (/[\'\"]/.test(ch)) { + state.tokenize = inAttribute(ch); + return state.tokenize(stream, state); + } else { + stream.eatWhile(/[^\s\u00a0=\"\'\/?]/); + return "keyword"; + } + } + + function inAttribute(quote) { + return function(stream, state) { + while (!stream.eol()) { + if (stream.next() == quote) { + state.tokenize = inPlugin; + break; + } + } + return "string"; + }; + } + + function inAttributeNoQuote() { + return function(stream, state) { + while (!stream.eol()) { + var ch = stream.next(); + var peek = stream.peek(); + if (ch == " " || ch == "," || /[ )}]/.test(peek)) { + state.tokenize = inPlugin; + break; + } + } + return "string"; +}; + } + +var curState, setStyle; +function pass() { + for (var i = arguments.length - 1; i >= 0; i--) curState.cc.push(arguments[i]); +} + +function cont() { + pass.apply(null, arguments); + return true; +} + +function pushContext(pluginName, startOfLine) { + var noIndent = curState.context && curState.context.noIndent; + curState.context = { + prev: curState.context, + pluginName: pluginName, + indent: curState.indented, + startOfLine: startOfLine, + noIndent: noIndent + }; +} + +function popContext() { + if (curState.context) curState.context = curState.context.prev; +} + +function element(type) { + if (type == "openPlugin") {curState.pluginName = pluginName; return cont(attributes, endplugin(curState.startOfLine));} + else if (type == "closePlugin") { + var err = false; + if (curState.context) { + err = curState.context.pluginName != pluginName; + popContext(); + } else { + err = true; + } + if (err) setStyle = "error"; + return cont(endcloseplugin(err)); + } + else if (type == "string") { + if (!curState.context || curState.context.name != "!cdata") pushContext("!cdata"); + if (curState.tokenize == inText) popContext(); + return cont(); + } + else return cont(); +} + +function endplugin(startOfLine) { + return function(type) { + if ( + type == "selfclosePlugin" || + type == "endPlugin" + ) + return cont(); + if (type == "endPlugin") {pushContext(curState.pluginName, startOfLine); return cont();} + return cont(); + }; +} + +function endcloseplugin(err) { + return function(type) { + if (err) setStyle = "error"; + if (type == "endPlugin") return cont(); + return pass(); + }; +} + +function attributes(type) { + if (type == "keyword") {setStyle = "attribute"; return cont(attributes);} + if (type == "equals") return cont(attvalue, attributes); + return pass(); +} +function attvalue(type) { + if (type == "keyword") {setStyle = "string"; return cont();} + if (type == "string") return cont(attvaluemaybe); + return pass(); +} +function attvaluemaybe(type) { + if (type == "string") return cont(attvaluemaybe); + else return pass(); +} +return { + startState: function() { + return {tokenize: inText, cc: [], indented: 0, startOfLine: true, pluginName: null, context: null}; + }, + token: function(stream, state) { + if (stream.sol()) { + state.startOfLine = true; + state.indented = stream.indentation(); + } + if (stream.eatSpace()) return null; + + setStyle = type = pluginName = null; + var style = state.tokenize(stream, state); + if ((style || type) && style != "comment") { + curState = state; + while (true) { + var comb = state.cc.pop() || element; + if (comb(type || style)) break; + } + } + state.startOfLine = false; + return setStyle || style; + }, + indent: function(state, textAfter) { + var context = state.context; + if (context && context.noIndent) return 0; + if (context && /^{\//.test(textAfter)) + context = context.prev; + while (context && !context.startOfLine) + context = context.prev; + if (context) return context.indent + indentUnit; + else return 0; + }, + electricChars: "/" +}; +}); + +CodeMirror.defineMIME("text/tiki", "tiki"); + +}); diff --git a/docs/js/node_modules/codemirror/mode/toml/toml.js b/docs/js/node_modules/codemirror/mode/toml/toml.js new file mode 100644 index 000000000..891f384b5 --- /dev/null +++ b/docs/js/node_modules/codemirror/mode/toml/toml.js @@ -0,0 +1,88 @@ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: https://codemirror.net/LICENSE + +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + +CodeMirror.defineMode("toml", function () { + return { + startState: function () { + return { + inString: false, + stringType: "", + lhs: true, + inArray: 0 + }; + }, + token: function (stream, state) { + //check for state changes + if (!state.inString && ((stream.peek() == '"') || (stream.peek() == "'"))) { + state.stringType = stream.peek(); + stream.next(); // Skip quote + state.inString = true; // Update state + } + if (stream.sol() && state.inArray === 0) { + state.lhs = true; + } + //return state + if (state.inString) { + while (state.inString && !stream.eol()) { + if (stream.peek() === state.stringType) { + stream.next(); // Skip quote + state.inString = false; // Clear flag + } else if (stream.peek() === '\\') { + stream.next(); + stream.next(); + } else { + stream.match(/^.[^\\\"\']*/); + } + } + return state.lhs ? "property string" : "string"; // Token style + } else if (state.inArray && stream.peek() === ']') { + stream.next(); + state.inArray--; + return 'bracket'; + } else if (state.lhs && stream.peek() === '[' && stream.skipTo(']')) { + stream.next();//skip closing ] + // array of objects has an extra open & close [] + if (stream.peek() === ']') stream.next(); + return "atom"; + } else if (stream.peek() === "#") { + stream.skipToEnd(); + return "comment"; + } else if (stream.eatSpace()) { + return null; + } else if (state.lhs && stream.eatWhile(function (c) { return c != '=' && c != ' '; })) { + return "property"; + } else if (state.lhs && stream.peek() === "=") { + stream.next(); + state.lhs = false; + return null; + } else if (!state.lhs && stream.match(/^\d\d\d\d[\d\-\:\.T]*Z/)) { + return 'atom'; //date + } else if (!state.lhs && (stream.match('true') || stream.match('false'))) { + return 'atom'; + } else if (!state.lhs && stream.peek() === '[') { + state.inArray++; + stream.next(); + return 'bracket'; + } else if (!state.lhs && stream.match(/^\-?\d+(?:\.\d+)?/)) { + return 'number'; + } else if (!stream.eatSpace()) { + stream.next(); + } + return null; + } + }; +}); + +CodeMirror.defineMIME('text/x-toml', 'toml'); + +}); diff --git a/docs/js/node_modules/codemirror/mode/tornado/tornado.js b/docs/js/node_modules/codemirror/mode/tornado/tornado.js new file mode 100644 index 000000000..aa589a08c --- /dev/null +++ b/docs/js/node_modules/codemirror/mode/tornado/tornado.js @@ -0,0 +1,68 @@ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: https://codemirror.net/LICENSE + +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror"), require("../htmlmixed/htmlmixed"), + require("../../addon/mode/overlay")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror", "../htmlmixed/htmlmixed", + "../../addon/mode/overlay"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { + "use strict"; + + CodeMirror.defineMode("tornado:inner", function() { + var keywords = ["and","as","assert","autoescape","block","break","class","comment","context", + "continue","datetime","def","del","elif","else","end","escape","except", + "exec","extends","false","finally","for","from","global","if","import","in", + "include","is","json_encode","lambda","length","linkify","load","module", + "none","not","or","pass","print","put","raise","raw","return","self","set", + "squeeze","super","true","try","url_escape","while","with","without","xhtml_escape","yield"]; + keywords = new RegExp("^((" + keywords.join(")|(") + "))\\b"); + + function tokenBase (stream, state) { + stream.eatWhile(/[^\{]/); + var ch = stream.next(); + if (ch == "{") { + if (ch = stream.eat(/\{|%|#/)) { + state.tokenize = inTag(ch); + return "tag"; + } + } + } + function inTag (close) { + if (close == "{") { + close = "}"; + } + return function (stream, state) { + var ch = stream.next(); + if ((ch == close) && stream.eat("}")) { + state.tokenize = tokenBase; + return "tag"; + } + if (stream.match(keywords)) { + return "keyword"; + } + return close == "#" ? "comment" : "string"; + }; + } + return { + startState: function () { + return {tokenize: tokenBase}; + }, + token: function (stream, state) { + return state.tokenize(stream, state); + } + }; + }); + + CodeMirror.defineMode("tornado", function(config) { + var htmlBase = CodeMirror.getMode(config, "text/html"); + var tornadoInner = CodeMirror.getMode(config, "tornado:inner"); + return CodeMirror.overlayMode(htmlBase, tornadoInner); + }); + + CodeMirror.defineMIME("text/x-tornado", "tornado"); +}); diff --git a/docs/js/node_modules/codemirror/mode/troff/troff.js b/docs/js/node_modules/codemirror/mode/troff/troff.js new file mode 100644 index 000000000..0c2220d2c --- /dev/null +++ b/docs/js/node_modules/codemirror/mode/troff/troff.js @@ -0,0 +1,84 @@ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: https://codemirror.net/LICENSE + +(function(mod) { + if (typeof exports == "object" && typeof module == "object") + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) + define(["../../lib/codemirror"], mod); + else + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + +CodeMirror.defineMode('troff', function() { + + var words = {}; + + function tokenBase(stream) { + if (stream.eatSpace()) return null; + + var sol = stream.sol(); + var ch = stream.next(); + + if (ch === '\\') { + if (stream.match('fB') || stream.match('fR') || stream.match('fI') || + stream.match('u') || stream.match('d') || + stream.match('%') || stream.match('&')) { + return 'string'; + } + if (stream.match('m[')) { + stream.skipTo(']'); + stream.next(); + return 'string'; + } + if (stream.match('s+') || stream.match('s-')) { + stream.eatWhile(/[\d-]/); + return 'string'; + } + if (stream.match('\(') || stream.match('*\(')) { + stream.eatWhile(/[\w-]/); + return 'string'; + } + return 'string'; + } + if (sol && (ch === '.' || ch === '\'')) { + if (stream.eat('\\') && stream.eat('\"')) { + stream.skipToEnd(); + return 'comment'; + } + } + if (sol && ch === '.') { + if (stream.match('B ') || stream.match('I ') || stream.match('R ')) { + return 'attribute'; + } + if (stream.match('TH ') || stream.match('SH ') || stream.match('SS ') || stream.match('HP ')) { + stream.skipToEnd(); + return 'quote'; + } + if ((stream.match(/[A-Z]/) && stream.match(/[A-Z]/)) || (stream.match(/[a-z]/) && stream.match(/[a-z]/))) { + return 'attribute'; + } + } + stream.eatWhile(/[\w-]/); + var cur = stream.current(); + return words.hasOwnProperty(cur) ? words[cur] : null; + } + + function tokenize(stream, state) { + return (state.tokens[0] || tokenBase) (stream, state); + }; + + return { + startState: function() {return {tokens:[]};}, + token: function(stream, state) { + return tokenize(stream, state); + } + }; +}); + +CodeMirror.defineMIME('text/troff', 'troff'); +CodeMirror.defineMIME('text/x-troff', 'troff'); +CodeMirror.defineMIME('application/x-troff', 'troff'); + +}); diff --git a/docs/js/node_modules/codemirror/mode/ttcn-cfg/ttcn-cfg.js b/docs/js/node_modules/codemirror/mode/ttcn-cfg/ttcn-cfg.js new file mode 100644 index 000000000..9d4b8405a --- /dev/null +++ b/docs/js/node_modules/codemirror/mode/ttcn-cfg/ttcn-cfg.js @@ -0,0 +1,214 @@ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: https://codemirror.net/LICENSE + +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { + "use strict"; + + CodeMirror.defineMode("ttcn-cfg", function(config, parserConfig) { + var indentUnit = config.indentUnit, + keywords = parserConfig.keywords || {}, + fileNCtrlMaskOptions = parserConfig.fileNCtrlMaskOptions || {}, + externalCommands = parserConfig.externalCommands || {}, + multiLineStrings = parserConfig.multiLineStrings, + indentStatements = parserConfig.indentStatements !== false; + var isOperatorChar = /[\|]/; + var curPunc; + + function tokenBase(stream, state) { + var ch = stream.next(); + if (ch == '"' || ch == "'") { + state.tokenize = tokenString(ch); + return state.tokenize(stream, state); + } + if (/[:=]/.test(ch)) { + curPunc = ch; + return "punctuation"; + } + if (ch == "#"){ + stream.skipToEnd(); + return "comment"; + } + if (/\d/.test(ch)) { + stream.eatWhile(/[\w\.]/); + return "number"; + } + if (isOperatorChar.test(ch)) { + stream.eatWhile(isOperatorChar); + return "operator"; + } + if (ch == "["){ + stream.eatWhile(/[\w_\]]/); + return "number sectionTitle"; + } + + stream.eatWhile(/[\w\$_]/); + var cur = stream.current(); + if (keywords.propertyIsEnumerable(cur)) return "keyword"; + if (fileNCtrlMaskOptions.propertyIsEnumerable(cur)) + return "negative fileNCtrlMaskOptions"; + if (externalCommands.propertyIsEnumerable(cur)) return "negative externalCommands"; + + return "variable"; + } + + function tokenString(quote) { + return function(stream, state) { + var escaped = false, next, end = false; + while ((next = stream.next()) != null) { + if (next == quote && !escaped){ + var afterNext = stream.peek(); + //look if the character if the quote is like the B in '10100010'B + if (afterNext){ + afterNext = afterNext.toLowerCase(); + if(afterNext == "b" || afterNext == "h" || afterNext == "o") + stream.next(); + } + end = true; break; + } + escaped = !escaped && next == "\\"; + } + if (end || !(escaped || multiLineStrings)) + state.tokenize = null; + return "string"; + }; + } + + function Context(indented, column, type, align, prev) { + this.indented = indented; + this.column = column; + this.type = type; + this.align = align; + this.prev = prev; + } + function pushContext(state, col, type) { + var indent = state.indented; + if (state.context && state.context.type == "statement") + indent = state.context.indented; + return state.context = new Context(indent, col, type, null, state.context); + } + function popContext(state) { + var t = state.context.type; + if (t == ")" || t == "]" || t == "}") + state.indented = state.context.indented; + return state.context = state.context.prev; + } + + //Interface + return { + startState: function(basecolumn) { + return { + tokenize: null, + context: new Context((basecolumn || 0) - indentUnit, 0, "top", false), + indented: 0, + startOfLine: true + }; + }, + + token: function(stream, state) { + var ctx = state.context; + if (stream.sol()) { + if (ctx.align == null) ctx.align = false; + state.indented = stream.indentation(); + state.startOfLine = true; + } + if (stream.eatSpace()) return null; + curPunc = null; + var style = (state.tokenize || tokenBase)(stream, state); + if (style == "comment") return style; + if (ctx.align == null) ctx.align = true; + + if ((curPunc == ";" || curPunc == ":" || curPunc == ",") + && ctx.type == "statement"){ + popContext(state); + } + else if (curPunc == "{") pushContext(state, stream.column(), "}"); + else if (curPunc == "[") pushContext(state, stream.column(), "]"); + else if (curPunc == "(") pushContext(state, stream.column(), ")"); + else if (curPunc == "}") { + while (ctx.type == "statement") ctx = popContext(state); + if (ctx.type == "}") ctx = popContext(state); + while (ctx.type == "statement") ctx = popContext(state); + } + else if (curPunc == ctx.type) popContext(state); + else if (indentStatements && (((ctx.type == "}" || ctx.type == "top") + && curPunc != ';') || (ctx.type == "statement" + && curPunc == "newstatement"))) + pushContext(state, stream.column(), "statement"); + state.startOfLine = false; + return style; + }, + + electricChars: "{}", + lineComment: "#", + fold: "brace" + }; + }); + + function words(str) { + var obj = {}, words = str.split(" "); + for (var i = 0; i < words.length; ++i) + obj[words[i]] = true; + return obj; + } + + CodeMirror.defineMIME("text/x-ttcn-cfg", { + name: "ttcn-cfg", + keywords: words("Yes No LogFile FileMask ConsoleMask AppendFile" + + " TimeStampFormat LogEventTypes SourceInfoFormat" + + " LogEntityName LogSourceInfo DiskFullAction" + + " LogFileNumber LogFileSize MatchingHints Detailed" + + " Compact SubCategories Stack Single None Seconds" + + " DateTime Time Stop Error Retry Delete TCPPort KillTimer" + + " NumHCs UnixSocketsEnabled LocalAddress"), + fileNCtrlMaskOptions: words("TTCN_EXECUTOR TTCN_ERROR TTCN_WARNING" + + " TTCN_PORTEVENT TTCN_TIMEROP TTCN_VERDICTOP" + + " TTCN_DEFAULTOP TTCN_TESTCASE TTCN_ACTION" + + " TTCN_USER TTCN_FUNCTION TTCN_STATISTICS" + + " TTCN_PARALLEL TTCN_MATCHING TTCN_DEBUG" + + " EXECUTOR ERROR WARNING PORTEVENT TIMEROP" + + " VERDICTOP DEFAULTOP TESTCASE ACTION USER" + + " FUNCTION STATISTICS PARALLEL MATCHING DEBUG" + + " LOG_ALL LOG_NOTHING ACTION_UNQUALIFIED" + + " DEBUG_ENCDEC DEBUG_TESTPORT" + + " DEBUG_UNQUALIFIED DEFAULTOP_ACTIVATE" + + " DEFAULTOP_DEACTIVATE DEFAULTOP_EXIT" + + " DEFAULTOP_UNQUALIFIED ERROR_UNQUALIFIED" + + " EXECUTOR_COMPONENT EXECUTOR_CONFIGDATA" + + " EXECUTOR_EXTCOMMAND EXECUTOR_LOGOPTIONS" + + " EXECUTOR_RUNTIME EXECUTOR_UNQUALIFIED" + + " FUNCTION_RND FUNCTION_UNQUALIFIED" + + " MATCHING_DONE MATCHING_MCSUCCESS" + + " MATCHING_MCUNSUCC MATCHING_MMSUCCESS" + + " MATCHING_MMUNSUCC MATCHING_PCSUCCESS" + + " MATCHING_PCUNSUCC MATCHING_PMSUCCESS" + + " MATCHING_PMUNSUCC MATCHING_PROBLEM" + + " MATCHING_TIMEOUT MATCHING_UNQUALIFIED" + + " PARALLEL_PORTCONN PARALLEL_PORTMAP" + + " PARALLEL_PTC PARALLEL_UNQUALIFIED" + + " PORTEVENT_DUALRECV PORTEVENT_DUALSEND" + + " PORTEVENT_MCRECV PORTEVENT_MCSEND" + + " PORTEVENT_MMRECV PORTEVENT_MMSEND" + + " PORTEVENT_MQUEUE PORTEVENT_PCIN" + + " PORTEVENT_PCOUT PORTEVENT_PMIN" + + " PORTEVENT_PMOUT PORTEVENT_PQUEUE" + + " PORTEVENT_STATE PORTEVENT_UNQUALIFIED" + + " STATISTICS_UNQUALIFIED STATISTICS_VERDICT" + + " TESTCASE_FINISH TESTCASE_START" + + " TESTCASE_UNQUALIFIED TIMEROP_GUARD" + + " TIMEROP_READ TIMEROP_START TIMEROP_STOP" + + " TIMEROP_TIMEOUT TIMEROP_UNQUALIFIED" + + " USER_UNQUALIFIED VERDICTOP_FINAL" + + " VERDICTOP_GETVERDICT VERDICTOP_SETVERDICT" + + " VERDICTOP_UNQUALIFIED WARNING_UNQUALIFIED"), + externalCommands: words("BeginControlPart EndControlPart BeginTestCase" + + " EndTestCase"), + multiLineStrings: true + }); +}); \ No newline at end of file diff --git a/docs/js/node_modules/codemirror/mode/ttcn/ttcn.js b/docs/js/node_modules/codemirror/mode/ttcn/ttcn.js new file mode 100644 index 000000000..0304e7c53 --- /dev/null +++ b/docs/js/node_modules/codemirror/mode/ttcn/ttcn.js @@ -0,0 +1,283 @@ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: https://codemirror.net/LICENSE + +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { + "use strict"; + + CodeMirror.defineMode("ttcn", function(config, parserConfig) { + var indentUnit = config.indentUnit, + keywords = parserConfig.keywords || {}, + builtin = parserConfig.builtin || {}, + timerOps = parserConfig.timerOps || {}, + portOps = parserConfig.portOps || {}, + configOps = parserConfig.configOps || {}, + verdictOps = parserConfig.verdictOps || {}, + sutOps = parserConfig.sutOps || {}, + functionOps = parserConfig.functionOps || {}, + + verdictConsts = parserConfig.verdictConsts || {}, + booleanConsts = parserConfig.booleanConsts || {}, + otherConsts = parserConfig.otherConsts || {}, + + types = parserConfig.types || {}, + visibilityModifiers = parserConfig.visibilityModifiers || {}, + templateMatch = parserConfig.templateMatch || {}, + multiLineStrings = parserConfig.multiLineStrings, + indentStatements = parserConfig.indentStatements !== false; + var isOperatorChar = /[+\-*&@=<>!\/]/; + var curPunc; + + function tokenBase(stream, state) { + var ch = stream.next(); + + if (ch == '"' || ch == "'") { + state.tokenize = tokenString(ch); + return state.tokenize(stream, state); + } + if (/[\[\]{}\(\),;\\:\?\.]/.test(ch)) { + curPunc = ch; + return "punctuation"; + } + if (ch == "#"){ + stream.skipToEnd(); + return "atom preprocessor"; + } + if (ch == "%"){ + stream.eatWhile(/\b/); + return "atom ttcn3Macros"; + } + if (/\d/.test(ch)) { + stream.eatWhile(/[\w\.]/); + return "number"; + } + if (ch == "/") { + if (stream.eat("*")) { + state.tokenize = tokenComment; + return tokenComment(stream, state); + } + if (stream.eat("/")) { + stream.skipToEnd(); + return "comment"; + } + } + if (isOperatorChar.test(ch)) { + if(ch == "@"){ + if(stream.match("try") || stream.match("catch") + || stream.match("lazy")){ + return "keyword"; + } + } + stream.eatWhile(isOperatorChar); + return "operator"; + } + stream.eatWhile(/[\w\$_\xa1-\uffff]/); + var cur = stream.current(); + + if (keywords.propertyIsEnumerable(cur)) return "keyword"; + if (builtin.propertyIsEnumerable(cur)) return "builtin"; + + if (timerOps.propertyIsEnumerable(cur)) return "def timerOps"; + if (configOps.propertyIsEnumerable(cur)) return "def configOps"; + if (verdictOps.propertyIsEnumerable(cur)) return "def verdictOps"; + if (portOps.propertyIsEnumerable(cur)) return "def portOps"; + if (sutOps.propertyIsEnumerable(cur)) return "def sutOps"; + if (functionOps.propertyIsEnumerable(cur)) return "def functionOps"; + + if (verdictConsts.propertyIsEnumerable(cur)) return "string verdictConsts"; + if (booleanConsts.propertyIsEnumerable(cur)) return "string booleanConsts"; + if (otherConsts.propertyIsEnumerable(cur)) return "string otherConsts"; + + if (types.propertyIsEnumerable(cur)) return "builtin types"; + if (visibilityModifiers.propertyIsEnumerable(cur)) + return "builtin visibilityModifiers"; + if (templateMatch.propertyIsEnumerable(cur)) return "atom templateMatch"; + + return "variable"; + } + + function tokenString(quote) { + return function(stream, state) { + var escaped = false, next, end = false; + while ((next = stream.next()) != null) { + if (next == quote && !escaped){ + var afterQuote = stream.peek(); + //look if the character after the quote is like the B in '10100010'B + if (afterQuote){ + afterQuote = afterQuote.toLowerCase(); + if(afterQuote == "b" || afterQuote == "h" || afterQuote == "o") + stream.next(); + } + end = true; break; + } + escaped = !escaped && next == "\\"; + } + if (end || !(escaped || multiLineStrings)) + state.tokenize = null; + return "string"; + }; + } + + function tokenComment(stream, state) { + var maybeEnd = false, ch; + while (ch = stream.next()) { + if (ch == "/" && maybeEnd) { + state.tokenize = null; + break; + } + maybeEnd = (ch == "*"); + } + return "comment"; + } + + function Context(indented, column, type, align, prev) { + this.indented = indented; + this.column = column; + this.type = type; + this.align = align; + this.prev = prev; + } + + function pushContext(state, col, type) { + var indent = state.indented; + if (state.context && state.context.type == "statement") + indent = state.context.indented; + return state.context = new Context(indent, col, type, null, state.context); + } + + function popContext(state) { + var t = state.context.type; + if (t == ")" || t == "]" || t == "}") + state.indented = state.context.indented; + return state.context = state.context.prev; + } + + //Interface + return { + startState: function(basecolumn) { + return { + tokenize: null, + context: new Context((basecolumn || 0) - indentUnit, 0, "top", false), + indented: 0, + startOfLine: true + }; + }, + + token: function(stream, state) { + var ctx = state.context; + if (stream.sol()) { + if (ctx.align == null) ctx.align = false; + state.indented = stream.indentation(); + state.startOfLine = true; + } + if (stream.eatSpace()) return null; + curPunc = null; + var style = (state.tokenize || tokenBase)(stream, state); + if (style == "comment") return style; + if (ctx.align == null) ctx.align = true; + + if ((curPunc == ";" || curPunc == ":" || curPunc == ",") + && ctx.type == "statement"){ + popContext(state); + } + else if (curPunc == "{") pushContext(state, stream.column(), "}"); + else if (curPunc == "[") pushContext(state, stream.column(), "]"); + else if (curPunc == "(") pushContext(state, stream.column(), ")"); + else if (curPunc == "}") { + while (ctx.type == "statement") ctx = popContext(state); + if (ctx.type == "}") ctx = popContext(state); + while (ctx.type == "statement") ctx = popContext(state); + } + else if (curPunc == ctx.type) popContext(state); + else if (indentStatements && + (((ctx.type == "}" || ctx.type == "top") && curPunc != ';') || + (ctx.type == "statement" && curPunc == "newstatement"))) + pushContext(state, stream.column(), "statement"); + + state.startOfLine = false; + + return style; + }, + + electricChars: "{}", + blockCommentStart: "/*", + blockCommentEnd: "*/", + lineComment: "//", + fold: "brace" + }; + }); + + function words(str) { + var obj = {}, words = str.split(" "); + for (var i = 0; i < words.length; ++i) obj[words[i]] = true; + return obj; + } + + function def(mimes, mode) { + if (typeof mimes == "string") mimes = [mimes]; + var words = []; + function add(obj) { + if (obj) for (var prop in obj) if (obj.hasOwnProperty(prop)) + words.push(prop); + } + + add(mode.keywords); + add(mode.builtin); + add(mode.timerOps); + add(mode.portOps); + + if (words.length) { + mode.helperType = mimes[0]; + CodeMirror.registerHelper("hintWords", mimes[0], words); + } + + for (var i = 0; i < mimes.length; ++i) + CodeMirror.defineMIME(mimes[i], mode); + } + + def(["text/x-ttcn", "text/x-ttcn3", "text/x-ttcnpp"], { + name: "ttcn", + keywords: words("activate address alive all alt altstep and and4b any" + + " break case component const continue control deactivate" + + " display do else encode enumerated except exception" + + " execute extends extension external for from function" + + " goto group if import in infinity inout interleave" + + " label language length log match message mixed mod" + + " modifies module modulepar mtc noblock not not4b nowait" + + " of on optional or or4b out override param pattern port" + + " procedure record recursive rem repeat return runs select" + + " self sender set signature system template testcase to" + + " type union value valueof var variant while with xor xor4b"), + builtin: words("bit2hex bit2int bit2oct bit2str char2int char2oct encvalue" + + " decomp decvalue float2int float2str hex2bit hex2int" + + " hex2oct hex2str int2bit int2char int2float int2hex" + + " int2oct int2str int2unichar isbound ischosen ispresent" + + " isvalue lengthof log2str oct2bit oct2char oct2hex oct2int" + + " oct2str regexp replace rnd sizeof str2bit str2float" + + " str2hex str2int str2oct substr unichar2int unichar2char" + + " enum2int"), + types: words("anytype bitstring boolean char charstring default float" + + " hexstring integer objid octetstring universal verdicttype timer"), + timerOps: words("read running start stop timeout"), + portOps: words("call catch check clear getcall getreply halt raise receive" + + " reply send trigger"), + configOps: words("create connect disconnect done kill killed map unmap"), + verdictOps: words("getverdict setverdict"), + sutOps: words("action"), + functionOps: words("apply derefers refers"), + + verdictConsts: words("error fail inconc none pass"), + booleanConsts: words("true false"), + otherConsts: words("null NULL omit"), + + visibilityModifiers: words("private public friend"), + templateMatch: words("complement ifpresent subset superset permutation"), + multiLineStrings: true + }); +}); diff --git a/docs/js/node_modules/codemirror/mode/turtle/turtle.js b/docs/js/node_modules/codemirror/mode/turtle/turtle.js new file mode 100644 index 000000000..695239661 --- /dev/null +++ b/docs/js/node_modules/codemirror/mode/turtle/turtle.js @@ -0,0 +1,162 @@ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: https://codemirror.net/LICENSE + +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + +CodeMirror.defineMode("turtle", function(config) { + var indentUnit = config.indentUnit; + var curPunc; + + function wordRegexp(words) { + return new RegExp("^(?:" + words.join("|") + ")$", "i"); + } + var ops = wordRegexp([]); + var keywords = wordRegexp(["@prefix", "@base", "a"]); + var operatorChars = /[*+\-<>=&|]/; + + function tokenBase(stream, state) { + var ch = stream.next(); + curPunc = null; + if (ch == "<" && !stream.match(/^[\s\u00a0=]/, false)) { + stream.match(/^[^\s\u00a0>]*>?/); + return "atom"; + } + else if (ch == "\"" || ch == "'") { + state.tokenize = tokenLiteral(ch); + return state.tokenize(stream, state); + } + else if (/[{}\(\),\.;\[\]]/.test(ch)) { + curPunc = ch; + return null; + } + else if (ch == "#") { + stream.skipToEnd(); + return "comment"; + } + else if (operatorChars.test(ch)) { + stream.eatWhile(operatorChars); + return null; + } + else if (ch == ":") { + return "operator"; + } else { + stream.eatWhile(/[_\w\d]/); + if(stream.peek() == ":") { + return "variable-3"; + } else { + var word = stream.current(); + + if(keywords.test(word)) { + return "meta"; + } + + if(ch >= "A" && ch <= "Z") { + return "comment"; + } else { + return "keyword"; + } + } + var word = stream.current(); + if (ops.test(word)) + return null; + else if (keywords.test(word)) + return "meta"; + else + return "variable"; + } + } + + function tokenLiteral(quote) { + return function(stream, state) { + var escaped = false, ch; + while ((ch = stream.next()) != null) { + if (ch == quote && !escaped) { + state.tokenize = tokenBase; + break; + } + escaped = !escaped && ch == "\\"; + } + return "string"; + }; + } + + function pushContext(state, type, col) { + state.context = {prev: state.context, indent: state.indent, col: col, type: type}; + } + function popContext(state) { + state.indent = state.context.indent; + state.context = state.context.prev; + } + + return { + startState: function() { + return {tokenize: tokenBase, + context: null, + indent: 0, + col: 0}; + }, + + token: function(stream, state) { + if (stream.sol()) { + if (state.context && state.context.align == null) state.context.align = false; + state.indent = stream.indentation(); + } + if (stream.eatSpace()) return null; + var style = state.tokenize(stream, state); + + if (style != "comment" && state.context && state.context.align == null && state.context.type != "pattern") { + state.context.align = true; + } + + if (curPunc == "(") pushContext(state, ")", stream.column()); + else if (curPunc == "[") pushContext(state, "]", stream.column()); + else if (curPunc == "{") pushContext(state, "}", stream.column()); + else if (/[\]\}\)]/.test(curPunc)) { + while (state.context && state.context.type == "pattern") popContext(state); + if (state.context && curPunc == state.context.type) popContext(state); + } + else if (curPunc == "." && state.context && state.context.type == "pattern") popContext(state); + else if (/atom|string|variable/.test(style) && state.context) { + if (/[\}\]]/.test(state.context.type)) + pushContext(state, "pattern", stream.column()); + else if (state.context.type == "pattern" && !state.context.align) { + state.context.align = true; + state.context.col = stream.column(); + } + } + + return style; + }, + + indent: function(state, textAfter) { + var firstChar = textAfter && textAfter.charAt(0); + var context = state.context; + if (/[\]\}]/.test(firstChar)) + while (context && context.type == "pattern") context = context.prev; + + var closing = context && firstChar == context.type; + if (!context) + return 0; + else if (context.type == "pattern") + return context.col; + else if (context.align) + return context.col + (closing ? 0 : 1); + else + return context.indent + (closing ? 0 : indentUnit); + }, + + lineComment: "#" + }; +}); + +CodeMirror.defineMIME("text/turtle", "turtle"); + +}); diff --git a/docs/js/node_modules/codemirror/mode/twig/twig.js b/docs/js/node_modules/codemirror/mode/twig/twig.js new file mode 100644 index 000000000..a6dd3f1a6 --- /dev/null +++ b/docs/js/node_modules/codemirror/mode/twig/twig.js @@ -0,0 +1,141 @@ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: https://codemirror.net/LICENSE + +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror"), require("../../addon/mode/multiplex")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror", "../../addon/mode/multiplex"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { + "use strict"; + + CodeMirror.defineMode("twig:inner", function() { + var keywords = ["and", "as", "autoescape", "endautoescape", "block", "do", "endblock", "else", "elseif", "extends", "for", "endfor", "embed", "endembed", "filter", "endfilter", "flush", "from", "if", "endif", "in", "is", "include", "import", "not", "or", "set", "spaceless", "endspaceless", "with", "endwith", "trans", "endtrans", "blocktrans", "endblocktrans", "macro", "endmacro", "use", "verbatim", "endverbatim"], + operator = /^[+\-*&%=<>!?|~^]/, + sign = /^[:\[\(\{]/, + atom = ["true", "false", "null", "empty", "defined", "divisibleby", "divisible by", "even", "odd", "iterable", "sameas", "same as"], + number = /^(\d[+\-\*\/])?\d+(\.\d+)?/; + + keywords = new RegExp("((" + keywords.join(")|(") + "))\\b"); + atom = new RegExp("((" + atom.join(")|(") + "))\\b"); + + function tokenBase (stream, state) { + var ch = stream.peek(); + + //Comment + if (state.incomment) { + if (!stream.skipTo("#}")) { + stream.skipToEnd(); + } else { + stream.eatWhile(/\#|}/); + state.incomment = false; + } + return "comment"; + //Tag + } else if (state.intag) { + //After operator + if (state.operator) { + state.operator = false; + if (stream.match(atom)) { + return "atom"; + } + if (stream.match(number)) { + return "number"; + } + } + //After sign + if (state.sign) { + state.sign = false; + if (stream.match(atom)) { + return "atom"; + } + if (stream.match(number)) { + return "number"; + } + } + + if (state.instring) { + if (ch == state.instring) { + state.instring = false; + } + stream.next(); + return "string"; + } else if (ch == "'" || ch == '"') { + state.instring = ch; + stream.next(); + return "string"; + } else if (stream.match(state.intag + "}") || stream.eat("-") && stream.match(state.intag + "}")) { + state.intag = false; + return "tag"; + } else if (stream.match(operator)) { + state.operator = true; + return "operator"; + } else if (stream.match(sign)) { + state.sign = true; + } else { + if (stream.eat(" ") || stream.sol()) { + if (stream.match(keywords)) { + return "keyword"; + } + if (stream.match(atom)) { + return "atom"; + } + if (stream.match(number)) { + return "number"; + } + if (stream.sol()) { + stream.next(); + } + } else { + stream.next(); + } + + } + return "variable"; + } else if (stream.eat("{")) { + if (stream.eat("#")) { + state.incomment = true; + if (!stream.skipTo("#}")) { + stream.skipToEnd(); + } else { + stream.eatWhile(/\#|}/); + state.incomment = false; + } + return "comment"; + //Open tag + } else if (ch = stream.eat(/\{|%/)) { + //Cache close tag + state.intag = ch; + if (ch == "{") { + state.intag = "}"; + } + stream.eat("-"); + return "tag"; + } + } + stream.next(); + }; + + return { + startState: function () { + return {}; + }, + token: function (stream, state) { + return tokenBase(stream, state); + } + }; + }); + + CodeMirror.defineMode("twig", function(config, parserConfig) { + var twigInner = CodeMirror.getMode(config, "twig:inner"); + if (!parserConfig || !parserConfig.base) return twigInner; + return CodeMirror.multiplexingMode( + CodeMirror.getMode(config, parserConfig.base), { + open: /\{[{#%]/, close: /[}#%]\}/, mode: twigInner, parseDelimiters: true + } + ); + }); + CodeMirror.defineMIME("text/x-twig", "twig"); +}); diff --git a/docs/js/node_modules/codemirror/mode/vb/vb.js b/docs/js/node_modules/codemirror/mode/vb/vb.js new file mode 100644 index 000000000..6e4b47630 --- /dev/null +++ b/docs/js/node_modules/codemirror/mode/vb/vb.js @@ -0,0 +1,275 @@ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: https://codemirror.net/LICENSE + +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + +CodeMirror.defineMode("vb", function(conf, parserConf) { + var ERRORCLASS = 'error'; + + function wordRegexp(words) { + return new RegExp("^((" + words.join(")|(") + "))\\b", "i"); + } + + var singleOperators = new RegExp("^[\\+\\-\\*/%&\\\\|\\^~<>!]"); + var singleDelimiters = new RegExp('^[\\(\\)\\[\\]\\{\\}@,:`=;\\.]'); + var doubleOperators = new RegExp("^((==)|(<>)|(<=)|(>=)|(<>)|(<<)|(>>)|(//)|(\\*\\*))"); + var doubleDelimiters = new RegExp("^((\\+=)|(\\-=)|(\\*=)|(%=)|(/=)|(&=)|(\\|=)|(\\^=))"); + var tripleDelimiters = new RegExp("^((//=)|(>>=)|(<<=)|(\\*\\*=))"); + var identifiers = new RegExp("^[_A-Za-z][_A-Za-z0-9]*"); + + var openingKeywords = ['class','module', 'sub','enum','select','while','if','function', 'get','set','property', 'try', 'structure', 'synclock', 'using', 'with']; + var middleKeywords = ['else','elseif','case', 'catch', 'finally']; + var endKeywords = ['next','loop']; + + var operatorKeywords = ['and', "andalso", 'or', 'orelse', 'xor', 'in', 'not', 'is', 'isnot', 'like']; + var wordOperators = wordRegexp(operatorKeywords); + + var commonKeywords = ["#const", "#else", "#elseif", "#end", "#if", "#region", "addhandler", "addressof", "alias", "as", "byref", "byval", "cbool", "cbyte", "cchar", "cdate", "cdbl", "cdec", "cint", "clng", "cobj", "compare", "const", "continue", "csbyte", "cshort", "csng", "cstr", "cuint", "culng", "cushort", "declare", "default", "delegate", "dim", "directcast", "each", "erase", "error", "event", "exit", "explicit", "false", "for", "friend", "gettype", "goto", "handles", "implements", "imports", "infer", "inherits", "interface", "isfalse", "istrue", "lib", "me", "mod", "mustinherit", "mustoverride", "my", "mybase", "myclass", "namespace", "narrowing", "new", "nothing", "notinheritable", "notoverridable", "of", "off", "on", "operator", "option", "optional", "out", "overloads", "overridable", "overrides", "paramarray", "partial", "private", "protected", "public", "raiseevent", "readonly", "redim", "removehandler", "resume", "return", "shadows", "shared", "static", "step", "stop", "strict", "then", "throw", "to", "true", "trycast", "typeof", "until", "until", "when", "widening", "withevents", "writeonly"]; + + var commontypes = ['object', 'boolean', 'char', 'string', 'byte', 'sbyte', 'short', 'ushort', 'int16', 'uint16', 'integer', 'uinteger', 'int32', 'uint32', 'long', 'ulong', 'int64', 'uint64', 'decimal', 'single', 'double', 'float', 'date', 'datetime', 'intptr', 'uintptr']; + + var keywords = wordRegexp(commonKeywords); + var types = wordRegexp(commontypes); + var stringPrefixes = '"'; + + var opening = wordRegexp(openingKeywords); + var middle = wordRegexp(middleKeywords); + var closing = wordRegexp(endKeywords); + var doubleClosing = wordRegexp(['end']); + var doOpening = wordRegexp(['do']); + + var indentInfo = null; + + CodeMirror.registerHelper("hintWords", "vb", openingKeywords.concat(middleKeywords).concat(endKeywords) + .concat(operatorKeywords).concat(commonKeywords).concat(commontypes)); + + function indent(_stream, state) { + state.currentIndent++; + } + + function dedent(_stream, state) { + state.currentIndent--; + } + // tokenizers + function tokenBase(stream, state) { + if (stream.eatSpace()) { + return null; + } + + var ch = stream.peek(); + + // Handle Comments + if (ch === "'") { + stream.skipToEnd(); + return 'comment'; + } + + + // Handle Number Literals + if (stream.match(/^((&H)|(&O))?[0-9\.a-f]/i, false)) { + var floatLiteral = false; + // Floats + if (stream.match(/^\d*\.\d+F?/i)) { floatLiteral = true; } + else if (stream.match(/^\d+\.\d*F?/)) { floatLiteral = true; } + else if (stream.match(/^\.\d+F?/)) { floatLiteral = true; } + + if (floatLiteral) { + // Float literals may be "imaginary" + stream.eat(/J/i); + return 'number'; + } + // Integers + var intLiteral = false; + // Hex + if (stream.match(/^&H[0-9a-f]+/i)) { intLiteral = true; } + // Octal + else if (stream.match(/^&O[0-7]+/i)) { intLiteral = true; } + // Decimal + else if (stream.match(/^[1-9]\d*F?/)) { + // Decimal literals may be "imaginary" + stream.eat(/J/i); + // TODO - Can you have imaginary longs? + intLiteral = true; + } + // Zero by itself with no other piece of number. + else if (stream.match(/^0(?![\dx])/i)) { intLiteral = true; } + if (intLiteral) { + // Integer literals may be "long" + stream.eat(/L/i); + return 'number'; + } + } + + // Handle Strings + if (stream.match(stringPrefixes)) { + state.tokenize = tokenStringFactory(stream.current()); + return state.tokenize(stream, state); + } + + // Handle operators and Delimiters + if (stream.match(tripleDelimiters) || stream.match(doubleDelimiters)) { + return null; + } + if (stream.match(doubleOperators) + || stream.match(singleOperators) + || stream.match(wordOperators)) { + return 'operator'; + } + if (stream.match(singleDelimiters)) { + return null; + } + if (stream.match(doOpening)) { + indent(stream,state); + state.doInCurrentLine = true; + return 'keyword'; + } + if (stream.match(opening)) { + if (! state.doInCurrentLine) + indent(stream,state); + else + state.doInCurrentLine = false; + return 'keyword'; + } + if (stream.match(middle)) { + return 'keyword'; + } + + if (stream.match(doubleClosing)) { + dedent(stream,state); + dedent(stream,state); + return 'keyword'; + } + if (stream.match(closing)) { + dedent(stream,state); + return 'keyword'; + } + + if (stream.match(types)) { + return 'keyword'; + } + + if (stream.match(keywords)) { + return 'keyword'; + } + + if (stream.match(identifiers)) { + return 'variable'; + } + + // Handle non-detected items + stream.next(); + return ERRORCLASS; + } + + function tokenStringFactory(delimiter) { + var singleline = delimiter.length == 1; + var OUTCLASS = 'string'; + + return function(stream, state) { + while (!stream.eol()) { + stream.eatWhile(/[^'"]/); + if (stream.match(delimiter)) { + state.tokenize = tokenBase; + return OUTCLASS; + } else { + stream.eat(/['"]/); + } + } + if (singleline) { + if (parserConf.singleLineStringErrors) { + return ERRORCLASS; + } else { + state.tokenize = tokenBase; + } + } + return OUTCLASS; + }; + } + + + function tokenLexer(stream, state) { + var style = state.tokenize(stream, state); + var current = stream.current(); + + // Handle '.' connected identifiers + if (current === '.') { + style = state.tokenize(stream, state); + if (style === 'variable') { + return 'variable'; + } else { + return ERRORCLASS; + } + } + + + var delimiter_index = '[({'.indexOf(current); + if (delimiter_index !== -1) { + indent(stream, state ); + } + if (indentInfo === 'dedent') { + if (dedent(stream, state)) { + return ERRORCLASS; + } + } + delimiter_index = '])}'.indexOf(current); + if (delimiter_index !== -1) { + if (dedent(stream, state)) { + return ERRORCLASS; + } + } + + return style; + } + + var external = { + electricChars:"dDpPtTfFeE ", + startState: function() { + return { + tokenize: tokenBase, + lastToken: null, + currentIndent: 0, + nextLineIndent: 0, + doInCurrentLine: false + + + }; + }, + + token: function(stream, state) { + if (stream.sol()) { + state.currentIndent += state.nextLineIndent; + state.nextLineIndent = 0; + state.doInCurrentLine = 0; + } + var style = tokenLexer(stream, state); + + state.lastToken = {style:style, content: stream.current()}; + + + + return style; + }, + + indent: function(state, textAfter) { + var trueText = textAfter.replace(/^\s+|\s+$/g, '') ; + if (trueText.match(closing) || trueText.match(doubleClosing) || trueText.match(middle)) return conf.indentUnit*(state.currentIndent-1); + if(state.currentIndent < 0) return 0; + return state.currentIndent * conf.indentUnit; + }, + + lineComment: "'" + }; + return external; +}); + +CodeMirror.defineMIME("text/x-vb", "vb"); + +}); diff --git a/docs/js/node_modules/codemirror/mode/vbscript/vbscript.js b/docs/js/node_modules/codemirror/mode/vbscript/vbscript.js new file mode 100644 index 000000000..0670c0cee --- /dev/null +++ b/docs/js/node_modules/codemirror/mode/vbscript/vbscript.js @@ -0,0 +1,350 @@ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: https://codemirror.net/LICENSE + +/* +For extra ASP classic objects, initialize CodeMirror instance with this option: + isASP: true + +E.G.: + var editor = CodeMirror.fromTextArea(document.getElementById("code"), { + lineNumbers: true, + isASP: true + }); +*/ + +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + +CodeMirror.defineMode("vbscript", function(conf, parserConf) { + var ERRORCLASS = 'error'; + + function wordRegexp(words) { + return new RegExp("^((" + words.join(")|(") + "))\\b", "i"); + } + + var singleOperators = new RegExp("^[\\+\\-\\*/&\\\\\\^<>=]"); + var doubleOperators = new RegExp("^((<>)|(<=)|(>=))"); + var singleDelimiters = new RegExp('^[\\.,]'); + var brakets = new RegExp('^[\\(\\)]'); + var identifiers = new RegExp("^[A-Za-z][_A-Za-z0-9]*"); + + var openingKeywords = ['class','sub','select','while','if','function', 'property', 'with', 'for']; + var middleKeywords = ['else','elseif','case']; + var endKeywords = ['next','loop','wend']; + + var wordOperators = wordRegexp(['and', 'or', 'not', 'xor', 'is', 'mod', 'eqv', 'imp']); + var commonkeywords = ['dim', 'redim', 'then', 'until', 'randomize', + 'byval','byref','new','property', 'exit', 'in', + 'const','private', 'public', + 'get','set','let', 'stop', 'on error resume next', 'on error goto 0', 'option explicit', 'call', 'me']; + + //This list was from: http://msdn.microsoft.com/en-us/library/f8tbc79x(v=vs.84).aspx + var atomWords = ['true', 'false', 'nothing', 'empty', 'null']; + //This list was from: http://msdn.microsoft.com/en-us/library/3ca8tfek(v=vs.84).aspx + var builtinFuncsWords = ['abs', 'array', 'asc', 'atn', 'cbool', 'cbyte', 'ccur', 'cdate', 'cdbl', 'chr', 'cint', 'clng', 'cos', 'csng', 'cstr', 'date', 'dateadd', 'datediff', 'datepart', + 'dateserial', 'datevalue', 'day', 'escape', 'eval', 'execute', 'exp', 'filter', 'formatcurrency', 'formatdatetime', 'formatnumber', 'formatpercent', 'getlocale', 'getobject', + 'getref', 'hex', 'hour', 'inputbox', 'instr', 'instrrev', 'int', 'fix', 'isarray', 'isdate', 'isempty', 'isnull', 'isnumeric', 'isobject', 'join', 'lbound', 'lcase', 'left', + 'len', 'loadpicture', 'log', 'ltrim', 'rtrim', 'trim', 'maths', 'mid', 'minute', 'month', 'monthname', 'msgbox', 'now', 'oct', 'replace', 'rgb', 'right', 'rnd', 'round', + 'scriptengine', 'scriptenginebuildversion', 'scriptenginemajorversion', 'scriptengineminorversion', 'second', 'setlocale', 'sgn', 'sin', 'space', 'split', 'sqr', 'strcomp', + 'string', 'strreverse', 'tan', 'time', 'timer', 'timeserial', 'timevalue', 'typename', 'ubound', 'ucase', 'unescape', 'vartype', 'weekday', 'weekdayname', 'year']; + + //This list was from: http://msdn.microsoft.com/en-us/library/ydz4cfk3(v=vs.84).aspx + var builtinConsts = ['vbBlack', 'vbRed', 'vbGreen', 'vbYellow', 'vbBlue', 'vbMagenta', 'vbCyan', 'vbWhite', 'vbBinaryCompare', 'vbTextCompare', + 'vbSunday', 'vbMonday', 'vbTuesday', 'vbWednesday', 'vbThursday', 'vbFriday', 'vbSaturday', 'vbUseSystemDayOfWeek', 'vbFirstJan1', 'vbFirstFourDays', 'vbFirstFullWeek', + 'vbGeneralDate', 'vbLongDate', 'vbShortDate', 'vbLongTime', 'vbShortTime', 'vbObjectError', + 'vbOKOnly', 'vbOKCancel', 'vbAbortRetryIgnore', 'vbYesNoCancel', 'vbYesNo', 'vbRetryCancel', 'vbCritical', 'vbQuestion', 'vbExclamation', 'vbInformation', 'vbDefaultButton1', 'vbDefaultButton2', + 'vbDefaultButton3', 'vbDefaultButton4', 'vbApplicationModal', 'vbSystemModal', 'vbOK', 'vbCancel', 'vbAbort', 'vbRetry', 'vbIgnore', 'vbYes', 'vbNo', + 'vbCr', 'VbCrLf', 'vbFormFeed', 'vbLf', 'vbNewLine', 'vbNullChar', 'vbNullString', 'vbTab', 'vbVerticalTab', 'vbUseDefault', 'vbTrue', 'vbFalse', + 'vbEmpty', 'vbNull', 'vbInteger', 'vbLong', 'vbSingle', 'vbDouble', 'vbCurrency', 'vbDate', 'vbString', 'vbObject', 'vbError', 'vbBoolean', 'vbVariant', 'vbDataObject', 'vbDecimal', 'vbByte', 'vbArray']; + //This list was from: http://msdn.microsoft.com/en-us/library/hkc375ea(v=vs.84).aspx + var builtinObjsWords = ['WScript', 'err', 'debug', 'RegExp']; + var knownProperties = ['description', 'firstindex', 'global', 'helpcontext', 'helpfile', 'ignorecase', 'length', 'number', 'pattern', 'source', 'value', 'count']; + var knownMethods = ['clear', 'execute', 'raise', 'replace', 'test', 'write', 'writeline', 'close', 'open', 'state', 'eof', 'update', 'addnew', 'end', 'createobject', 'quit']; + + var aspBuiltinObjsWords = ['server', 'response', 'request', 'session', 'application']; + var aspKnownProperties = ['buffer', 'cachecontrol', 'charset', 'contenttype', 'expires', 'expiresabsolute', 'isclientconnected', 'pics', 'status', //response + 'clientcertificate', 'cookies', 'form', 'querystring', 'servervariables', 'totalbytes', //request + 'contents', 'staticobjects', //application + 'codepage', 'lcid', 'sessionid', 'timeout', //session + 'scripttimeout']; //server + var aspKnownMethods = ['addheader', 'appendtolog', 'binarywrite', 'end', 'flush', 'redirect', //response + 'binaryread', //request + 'remove', 'removeall', 'lock', 'unlock', //application + 'abandon', //session + 'getlasterror', 'htmlencode', 'mappath', 'transfer', 'urlencode']; //server + + var knownWords = knownMethods.concat(knownProperties); + + builtinObjsWords = builtinObjsWords.concat(builtinConsts); + + if (conf.isASP){ + builtinObjsWords = builtinObjsWords.concat(aspBuiltinObjsWords); + knownWords = knownWords.concat(aspKnownMethods, aspKnownProperties); + }; + + var keywords = wordRegexp(commonkeywords); + var atoms = wordRegexp(atomWords); + var builtinFuncs = wordRegexp(builtinFuncsWords); + var builtinObjs = wordRegexp(builtinObjsWords); + var known = wordRegexp(knownWords); + var stringPrefixes = '"'; + + var opening = wordRegexp(openingKeywords); + var middle = wordRegexp(middleKeywords); + var closing = wordRegexp(endKeywords); + var doubleClosing = wordRegexp(['end']); + var doOpening = wordRegexp(['do']); + var noIndentWords = wordRegexp(['on error resume next', 'exit']); + var comment = wordRegexp(['rem']); + + + function indent(_stream, state) { + state.currentIndent++; + } + + function dedent(_stream, state) { + state.currentIndent--; + } + // tokenizers + function tokenBase(stream, state) { + if (stream.eatSpace()) { + return 'space'; + //return null; + } + + var ch = stream.peek(); + + // Handle Comments + if (ch === "'") { + stream.skipToEnd(); + return 'comment'; + } + if (stream.match(comment)){ + stream.skipToEnd(); + return 'comment'; + } + + + // Handle Number Literals + if (stream.match(/^((&H)|(&O))?[0-9\.]/i, false) && !stream.match(/^((&H)|(&O))?[0-9\.]+[a-z_]/i, false)) { + var floatLiteral = false; + // Floats + if (stream.match(/^\d*\.\d+/i)) { floatLiteral = true; } + else if (stream.match(/^\d+\.\d*/)) { floatLiteral = true; } + else if (stream.match(/^\.\d+/)) { floatLiteral = true; } + + if (floatLiteral) { + // Float literals may be "imaginary" + stream.eat(/J/i); + return 'number'; + } + // Integers + var intLiteral = false; + // Hex + if (stream.match(/^&H[0-9a-f]+/i)) { intLiteral = true; } + // Octal + else if (stream.match(/^&O[0-7]+/i)) { intLiteral = true; } + // Decimal + else if (stream.match(/^[1-9]\d*F?/)) { + // Decimal literals may be "imaginary" + stream.eat(/J/i); + // TODO - Can you have imaginary longs? + intLiteral = true; + } + // Zero by itself with no other piece of number. + else if (stream.match(/^0(?![\dx])/i)) { intLiteral = true; } + if (intLiteral) { + // Integer literals may be "long" + stream.eat(/L/i); + return 'number'; + } + } + + // Handle Strings + if (stream.match(stringPrefixes)) { + state.tokenize = tokenStringFactory(stream.current()); + return state.tokenize(stream, state); + } + + // Handle operators and Delimiters + if (stream.match(doubleOperators) + || stream.match(singleOperators) + || stream.match(wordOperators)) { + return 'operator'; + } + if (stream.match(singleDelimiters)) { + return null; + } + + if (stream.match(brakets)) { + return "bracket"; + } + + if (stream.match(noIndentWords)) { + state.doInCurrentLine = true; + + return 'keyword'; + } + + if (stream.match(doOpening)) { + indent(stream,state); + state.doInCurrentLine = true; + + return 'keyword'; + } + if (stream.match(opening)) { + if (! state.doInCurrentLine) + indent(stream,state); + else + state.doInCurrentLine = false; + + return 'keyword'; + } + if (stream.match(middle)) { + return 'keyword'; + } + + + if (stream.match(doubleClosing)) { + dedent(stream,state); + dedent(stream,state); + + return 'keyword'; + } + if (stream.match(closing)) { + if (! state.doInCurrentLine) + dedent(stream,state); + else + state.doInCurrentLine = false; + + return 'keyword'; + } + + if (stream.match(keywords)) { + return 'keyword'; + } + + if (stream.match(atoms)) { + return 'atom'; + } + + if (stream.match(known)) { + return 'variable-2'; + } + + if (stream.match(builtinFuncs)) { + return 'builtin'; + } + + if (stream.match(builtinObjs)){ + return 'variable-2'; + } + + if (stream.match(identifiers)) { + return 'variable'; + } + + // Handle non-detected items + stream.next(); + return ERRORCLASS; + } + + function tokenStringFactory(delimiter) { + var singleline = delimiter.length == 1; + var OUTCLASS = 'string'; + + return function(stream, state) { + while (!stream.eol()) { + stream.eatWhile(/[^'"]/); + if (stream.match(delimiter)) { + state.tokenize = tokenBase; + return OUTCLASS; + } else { + stream.eat(/['"]/); + } + } + if (singleline) { + if (parserConf.singleLineStringErrors) { + return ERRORCLASS; + } else { + state.tokenize = tokenBase; + } + } + return OUTCLASS; + }; + } + + + function tokenLexer(stream, state) { + var style = state.tokenize(stream, state); + var current = stream.current(); + + // Handle '.' connected identifiers + if (current === '.') { + style = state.tokenize(stream, state); + + current = stream.current(); + if (style && (style.substr(0, 8) === 'variable' || style==='builtin' || style==='keyword')){//|| knownWords.indexOf(current.substring(1)) > -1) { + if (style === 'builtin' || style === 'keyword') style='variable'; + if (knownWords.indexOf(current.substr(1)) > -1) style='variable-2'; + + return style; + } else { + return ERRORCLASS; + } + } + + return style; + } + + var external = { + electricChars:"dDpPtTfFeE ", + startState: function() { + return { + tokenize: tokenBase, + lastToken: null, + currentIndent: 0, + nextLineIndent: 0, + doInCurrentLine: false, + ignoreKeyword: false + + + }; + }, + + token: function(stream, state) { + if (stream.sol()) { + state.currentIndent += state.nextLineIndent; + state.nextLineIndent = 0; + state.doInCurrentLine = 0; + } + var style = tokenLexer(stream, state); + + state.lastToken = {style:style, content: stream.current()}; + + if (style==='space') style=null; + + return style; + }, + + indent: function(state, textAfter) { + var trueText = textAfter.replace(/^\s+|\s+$/g, '') ; + if (trueText.match(closing) || trueText.match(doubleClosing) || trueText.match(middle)) return conf.indentUnit*(state.currentIndent-1); + if(state.currentIndent < 0) return 0; + return state.currentIndent * conf.indentUnit; + } + + }; + return external; +}); + +CodeMirror.defineMIME("text/vbscript", "vbscript"); + +}); diff --git a/docs/js/node_modules/codemirror/mode/velocity/velocity.js b/docs/js/node_modules/codemirror/mode/velocity/velocity.js new file mode 100644 index 000000000..56caa671b --- /dev/null +++ b/docs/js/node_modules/codemirror/mode/velocity/velocity.js @@ -0,0 +1,201 @@ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: https://codemirror.net/LICENSE + +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + +CodeMirror.defineMode("velocity", function() { + function parseWords(str) { + var obj = {}, words = str.split(" "); + for (var i = 0; i < words.length; ++i) obj[words[i]] = true; + return obj; + } + + var keywords = parseWords("#end #else #break #stop #[[ #]] " + + "#{end} #{else} #{break} #{stop}"); + var functions = parseWords("#if #elseif #foreach #set #include #parse #macro #define #evaluate " + + "#{if} #{elseif} #{foreach} #{set} #{include} #{parse} #{macro} #{define} #{evaluate}"); + var specials = parseWords("$foreach.count $foreach.hasNext $foreach.first $foreach.last $foreach.topmost $foreach.parent.count $foreach.parent.hasNext $foreach.parent.first $foreach.parent.last $foreach.parent $velocityCount $!bodyContent $bodyContent"); + var isOperatorChar = /[+\-*&%=<>!?:\/|]/; + + function chain(stream, state, f) { + state.tokenize = f; + return f(stream, state); + } + function tokenBase(stream, state) { + var beforeParams = state.beforeParams; + state.beforeParams = false; + var ch = stream.next(); + // start of unparsed string? + if ((ch == "'") && !state.inString && state.inParams) { + state.lastTokenWasBuiltin = false; + return chain(stream, state, tokenString(ch)); + } + // start of parsed string? + else if ((ch == '"')) { + state.lastTokenWasBuiltin = false; + if (state.inString) { + state.inString = false; + return "string"; + } + else if (state.inParams) + return chain(stream, state, tokenString(ch)); + } + // is it one of the special signs []{}().,;? Seperator? + else if (/[\[\]{}\(\),;\.]/.test(ch)) { + if (ch == "(" && beforeParams) + state.inParams = true; + else if (ch == ")") { + state.inParams = false; + state.lastTokenWasBuiltin = true; + } + return null; + } + // start of a number value? + else if (/\d/.test(ch)) { + state.lastTokenWasBuiltin = false; + stream.eatWhile(/[\w\.]/); + return "number"; + } + // multi line comment? + else if (ch == "#" && stream.eat("*")) { + state.lastTokenWasBuiltin = false; + return chain(stream, state, tokenComment); + } + // unparsed content? + else if (ch == "#" && stream.match(/ *\[ *\[/)) { + state.lastTokenWasBuiltin = false; + return chain(stream, state, tokenUnparsed); + } + // single line comment? + else if (ch == "#" && stream.eat("#")) { + state.lastTokenWasBuiltin = false; + stream.skipToEnd(); + return "comment"; + } + // variable? + else if (ch == "$") { + stream.eatWhile(/[\w\d\$_\.{}-]/); + // is it one of the specials? + if (specials && specials.propertyIsEnumerable(stream.current())) { + return "keyword"; + } + else { + state.lastTokenWasBuiltin = true; + state.beforeParams = true; + return "builtin"; + } + } + // is it a operator? + else if (isOperatorChar.test(ch)) { + state.lastTokenWasBuiltin = false; + stream.eatWhile(isOperatorChar); + return "operator"; + } + else { + // get the whole word + stream.eatWhile(/[\w\$_{}@]/); + var word = stream.current(); + // is it one of the listed keywords? + if (keywords && keywords.propertyIsEnumerable(word)) + return "keyword"; + // is it one of the listed functions? + if (functions && functions.propertyIsEnumerable(word) || + (stream.current().match(/^#@?[a-z0-9_]+ *$/i) && stream.peek()=="(") && + !(functions && functions.propertyIsEnumerable(word.toLowerCase()))) { + state.beforeParams = true; + state.lastTokenWasBuiltin = false; + return "keyword"; + } + if (state.inString) { + state.lastTokenWasBuiltin = false; + return "string"; + } + if (stream.pos > word.length && stream.string.charAt(stream.pos-word.length-1)=="." && state.lastTokenWasBuiltin) + return "builtin"; + // default: just a "word" + state.lastTokenWasBuiltin = false; + return null; + } + } + + function tokenString(quote) { + return function(stream, state) { + var escaped = false, next, end = false; + while ((next = stream.next()) != null) { + if ((next == quote) && !escaped) { + end = true; + break; + } + if (quote=='"' && stream.peek() == '$' && !escaped) { + state.inString = true; + end = true; + break; + } + escaped = !escaped && next == "\\"; + } + if (end) state.tokenize = tokenBase; + return "string"; + }; + } + + function tokenComment(stream, state) { + var maybeEnd = false, ch; + while (ch = stream.next()) { + if (ch == "#" && maybeEnd) { + state.tokenize = tokenBase; + break; + } + maybeEnd = (ch == "*"); + } + return "comment"; + } + + function tokenUnparsed(stream, state) { + var maybeEnd = 0, ch; + while (ch = stream.next()) { + if (ch == "#" && maybeEnd == 2) { + state.tokenize = tokenBase; + break; + } + if (ch == "]") + maybeEnd++; + else if (ch != " ") + maybeEnd = 0; + } + return "meta"; + } + // Interface + + return { + startState: function() { + return { + tokenize: tokenBase, + beforeParams: false, + inParams: false, + inString: false, + lastTokenWasBuiltin: false + }; + }, + + token: function(stream, state) { + if (stream.eatSpace()) return null; + return state.tokenize(stream, state); + }, + blockCommentStart: "#*", + blockCommentEnd: "*#", + lineComment: "##", + fold: "velocity" + }; +}); + +CodeMirror.defineMIME("text/velocity", "velocity"); + +}); diff --git a/docs/js/node_modules/codemirror/mode/verilog/verilog.js b/docs/js/node_modules/codemirror/mode/verilog/verilog.js new file mode 100644 index 000000000..2b6850659 --- /dev/null +++ b/docs/js/node_modules/codemirror/mode/verilog/verilog.js @@ -0,0 +1,675 @@ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: https://codemirror.net/LICENSE + +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + +CodeMirror.defineMode("verilog", function(config, parserConfig) { + + var indentUnit = config.indentUnit, + statementIndentUnit = parserConfig.statementIndentUnit || indentUnit, + dontAlignCalls = parserConfig.dontAlignCalls, + noIndentKeywords = parserConfig.noIndentKeywords || [], + multiLineStrings = parserConfig.multiLineStrings, + hooks = parserConfig.hooks || {}; + + function words(str) { + var obj = {}, words = str.split(" "); + for (var i = 0; i < words.length; ++i) obj[words[i]] = true; + return obj; + } + + /** + * Keywords from IEEE 1800-2012 + */ + var keywords = words( + "accept_on alias always always_comb always_ff always_latch and assert assign assume automatic before begin bind " + + "bins binsof bit break buf bufif0 bufif1 byte case casex casez cell chandle checker class clocking cmos config " + + "const constraint context continue cover covergroup coverpoint cross deassign default defparam design disable " + + "dist do edge else end endcase endchecker endclass endclocking endconfig endfunction endgenerate endgroup " + + "endinterface endmodule endpackage endprimitive endprogram endproperty endspecify endsequence endtable endtask " + + "enum event eventually expect export extends extern final first_match for force foreach forever fork forkjoin " + + "function generate genvar global highz0 highz1 if iff ifnone ignore_bins illegal_bins implements implies import " + + "incdir include initial inout input inside instance int integer interconnect interface intersect join join_any " + + "join_none large let liblist library local localparam logic longint macromodule matches medium modport module " + + "nand negedge nettype new nexttime nmos nor noshowcancelled not notif0 notif1 null or output package packed " + + "parameter pmos posedge primitive priority program property protected pull0 pull1 pulldown pullup " + + "pulsestyle_ondetect pulsestyle_onevent pure rand randc randcase randsequence rcmos real realtime ref reg " + + "reject_on release repeat restrict return rnmos rpmos rtran rtranif0 rtranif1 s_always s_eventually s_nexttime " + + "s_until s_until_with scalared sequence shortint shortreal showcancelled signed small soft solve specify " + + "specparam static string strong strong0 strong1 struct super supply0 supply1 sync_accept_on sync_reject_on " + + "table tagged task this throughout time timeprecision timeunit tran tranif0 tranif1 tri tri0 tri1 triand trior " + + "trireg type typedef union unique unique0 unsigned until until_with untyped use uwire var vectored virtual void " + + "wait wait_order wand weak weak0 weak1 while wildcard wire with within wor xnor xor"); + + /** Operators from IEEE 1800-2012 + unary_operator ::= + + | - | ! | ~ | & | ~& | | | ~| | ^ | ~^ | ^~ + binary_operator ::= + + | - | * | / | % | == | != | === | !== | ==? | !=? | && | || | ** + | < | <= | > | >= | & | | | ^ | ^~ | ~^ | >> | << | >>> | <<< + | -> | <-> + inc_or_dec_operator ::= ++ | -- + unary_module_path_operator ::= + ! | ~ | & | ~& | | | ~| | ^ | ~^ | ^~ + binary_module_path_operator ::= + == | != | && | || | & | | | ^ | ^~ | ~^ + */ + var isOperatorChar = /[\+\-\*\/!~&|^%=?:]/; + var isBracketChar = /[\[\]{}()]/; + + var unsignedNumber = /\d[0-9_]*/; + var decimalLiteral = /\d*\s*'s?d\s*\d[0-9_]*/i; + var binaryLiteral = /\d*\s*'s?b\s*[xz01][xz01_]*/i; + var octLiteral = /\d*\s*'s?o\s*[xz0-7][xz0-7_]*/i; + var hexLiteral = /\d*\s*'s?h\s*[0-9a-fxz?][0-9a-fxz?_]*/i; + var realLiteral = /(\d[\d_]*(\.\d[\d_]*)?E-?[\d_]+)|(\d[\d_]*\.\d[\d_]*)/i; + + var closingBracketOrWord = /^((\w+)|[)}\]])/; + var closingBracket = /[)}\]]/; + + var curPunc; + var curKeyword; + + // Block openings which are closed by a matching keyword in the form of ("end" + keyword) + // E.g. "task" => "endtask" + var blockKeywords = words( + "case checker class clocking config function generate interface module package " + + "primitive program property specify sequence table task" + ); + + // Opening/closing pairs + var openClose = {}; + for (var keyword in blockKeywords) { + openClose[keyword] = "end" + keyword; + } + openClose["begin"] = "end"; + openClose["casex"] = "endcase"; + openClose["casez"] = "endcase"; + openClose["do" ] = "while"; + openClose["fork" ] = "join;join_any;join_none"; + openClose["covergroup"] = "endgroup"; + + for (var i in noIndentKeywords) { + var keyword = noIndentKeywords[i]; + if (openClose[keyword]) { + openClose[keyword] = undefined; + } + } + + // Keywords which open statements that are ended with a semi-colon + var statementKeywords = words("always always_comb always_ff always_latch assert assign assume else export for foreach forever if import initial repeat while"); + + function tokenBase(stream, state) { + var ch = stream.peek(), style; + if (hooks[ch] && (style = hooks[ch](stream, state)) != false) return style; + if (hooks.tokenBase && (style = hooks.tokenBase(stream, state)) != false) + return style; + + if (/[,;:\.]/.test(ch)) { + curPunc = stream.next(); + return null; + } + if (isBracketChar.test(ch)) { + curPunc = stream.next(); + return "bracket"; + } + // Macros (tick-defines) + if (ch == '`') { + stream.next(); + if (stream.eatWhile(/[\w\$_]/)) { + return "def"; + } else { + return null; + } + } + // System calls + if (ch == '$') { + stream.next(); + if (stream.eatWhile(/[\w\$_]/)) { + return "meta"; + } else { + return null; + } + } + // Time literals + if (ch == '#') { + stream.next(); + stream.eatWhile(/[\d_.]/); + return "def"; + } + // Strings + if (ch == '"') { + stream.next(); + state.tokenize = tokenString(ch); + return state.tokenize(stream, state); + } + // Comments + if (ch == "/") { + stream.next(); + if (stream.eat("*")) { + state.tokenize = tokenComment; + return tokenComment(stream, state); + } + if (stream.eat("/")) { + stream.skipToEnd(); + return "comment"; + } + stream.backUp(1); + } + + // Numeric literals + if (stream.match(realLiteral) || + stream.match(decimalLiteral) || + stream.match(binaryLiteral) || + stream.match(octLiteral) || + stream.match(hexLiteral) || + stream.match(unsignedNumber) || + stream.match(realLiteral)) { + return "number"; + } + + // Operators + if (stream.eatWhile(isOperatorChar)) { + return "meta"; + } + + // Keywords / plain variables + if (stream.eatWhile(/[\w\$_]/)) { + var cur = stream.current(); + if (keywords[cur]) { + if (openClose[cur]) { + curPunc = "newblock"; + } + if (statementKeywords[cur]) { + curPunc = "newstatement"; + } + curKeyword = cur; + return "keyword"; + } + return "variable"; + } + + stream.next(); + return null; + } + + function tokenString(quote) { + return function(stream, state) { + var escaped = false, next, end = false; + while ((next = stream.next()) != null) { + if (next == quote && !escaped) {end = true; break;} + escaped = !escaped && next == "\\"; + } + if (end || !(escaped || multiLineStrings)) + state.tokenize = tokenBase; + return "string"; + }; + } + + function tokenComment(stream, state) { + var maybeEnd = false, ch; + while (ch = stream.next()) { + if (ch == "/" && maybeEnd) { + state.tokenize = tokenBase; + break; + } + maybeEnd = (ch == "*"); + } + return "comment"; + } + + function Context(indented, column, type, align, prev) { + this.indented = indented; + this.column = column; + this.type = type; + this.align = align; + this.prev = prev; + } + function pushContext(state, col, type) { + var indent = state.indented; + var c = new Context(indent, col, type, null, state.context); + return state.context = c; + } + function popContext(state) { + var t = state.context.type; + if (t == ")" || t == "]" || t == "}") { + state.indented = state.context.indented; + } + return state.context = state.context.prev; + } + + function isClosing(text, contextClosing) { + if (text == contextClosing) { + return true; + } else { + // contextClosing may be multiple keywords separated by ; + var closingKeywords = contextClosing.split(";"); + for (var i in closingKeywords) { + if (text == closingKeywords[i]) { + return true; + } + } + return false; + } + } + + function buildElectricInputRegEx() { + // Reindentation should occur on any bracket char: {}()[] + // or on a match of any of the block closing keywords, at + // the end of a line + var allClosings = []; + for (var i in openClose) { + if (openClose[i]) { + var closings = openClose[i].split(";"); + for (var j in closings) { + allClosings.push(closings[j]); + } + } + } + var re = new RegExp("[{}()\\[\\]]|(" + allClosings.join("|") + ")$"); + return re; + } + + // Interface + return { + + // Regex to force current line to reindent + electricInput: buildElectricInputRegEx(), + + startState: function(basecolumn) { + var state = { + tokenize: null, + context: new Context((basecolumn || 0) - indentUnit, 0, "top", false), + indented: 0, + startOfLine: true + }; + if (hooks.startState) hooks.startState(state); + return state; + }, + + token: function(stream, state) { + var ctx = state.context; + if (stream.sol()) { + if (ctx.align == null) ctx.align = false; + state.indented = stream.indentation(); + state.startOfLine = true; + } + if (hooks.token) { + // Call hook, with an optional return value of a style to override verilog styling. + var style = hooks.token(stream, state); + if (style !== undefined) { + return style; + } + } + if (stream.eatSpace()) return null; + curPunc = null; + curKeyword = null; + var style = (state.tokenize || tokenBase)(stream, state); + if (style == "comment" || style == "meta" || style == "variable") return style; + if (ctx.align == null) ctx.align = true; + + if (curPunc == ctx.type) { + popContext(state); + } else if ((curPunc == ";" && ctx.type == "statement") || + (ctx.type && isClosing(curKeyword, ctx.type))) { + ctx = popContext(state); + while (ctx && ctx.type == "statement") ctx = popContext(state); + } else if (curPunc == "{") { + pushContext(state, stream.column(), "}"); + } else if (curPunc == "[") { + pushContext(state, stream.column(), "]"); + } else if (curPunc == "(") { + pushContext(state, stream.column(), ")"); + } else if (ctx && ctx.type == "endcase" && curPunc == ":") { + pushContext(state, stream.column(), "statement"); + } else if (curPunc == "newstatement") { + pushContext(state, stream.column(), "statement"); + } else if (curPunc == "newblock") { + if (curKeyword == "function" && ctx && (ctx.type == "statement" || ctx.type == "endgroup")) { + // The 'function' keyword can appear in some other contexts where it actually does not + // indicate a function (import/export DPI and covergroup definitions). + // Do nothing in this case + } else if (curKeyword == "task" && ctx && ctx.type == "statement") { + // Same thing for task + } else { + var close = openClose[curKeyword]; + pushContext(state, stream.column(), close); + } + } + + state.startOfLine = false; + return style; + }, + + indent: function(state, textAfter) { + if (state.tokenize != tokenBase && state.tokenize != null) return CodeMirror.Pass; + if (hooks.indent) { + var fromHook = hooks.indent(state); + if (fromHook >= 0) return fromHook; + } + var ctx = state.context, firstChar = textAfter && textAfter.charAt(0); + if (ctx.type == "statement" && firstChar == "}") ctx = ctx.prev; + var closing = false; + var possibleClosing = textAfter.match(closingBracketOrWord); + if (possibleClosing) + closing = isClosing(possibleClosing[0], ctx.type); + if (ctx.type == "statement") return ctx.indented + (firstChar == "{" ? 0 : statementIndentUnit); + else if (closingBracket.test(ctx.type) && ctx.align && !dontAlignCalls) return ctx.column + (closing ? 0 : 1); + else if (ctx.type == ")" && !closing) return ctx.indented + statementIndentUnit; + else return ctx.indented + (closing ? 0 : indentUnit); + }, + + blockCommentStart: "/*", + blockCommentEnd: "*/", + lineComment: "//" + }; +}); + + CodeMirror.defineMIME("text/x-verilog", { + name: "verilog" + }); + + CodeMirror.defineMIME("text/x-systemverilog", { + name: "verilog" + }); + + + + // TL-Verilog mode. + // See tl-x.org for language spec. + // See the mode in action at makerchip.com. + // Contact: steve.hoover@redwoodeda.com + + // TLV Identifier prefixes. + // Note that sign is not treated separately, so "+/-" versions of numeric identifiers + // are included. + var tlvIdentifierStyle = { + "|": "link", + ">": "property", // Should condition this off for > TLV 1c. + "$": "variable", + "$$": "variable", + "?$": "qualifier", + "?*": "qualifier", + "-": "hr", + "/": "property", + "/-": "property", + "@": "variable-3", + "@-": "variable-3", + "@++": "variable-3", + "@+=": "variable-3", + "@+=-": "variable-3", + "@--": "variable-3", + "@-=": "variable-3", + "%+": "tag", + "%-": "tag", + "%": "tag", + ">>": "tag", + "<<": "tag", + "<>": "tag", + "#": "tag", // Need to choose a style for this. + "^": "attribute", + "^^": "attribute", + "^!": "attribute", + "*": "variable-2", + "**": "variable-2", + "\\": "keyword", + "\"": "comment" + }; + + // Lines starting with these characters define scope (result in indentation). + var tlvScopePrefixChars = { + "/": "beh-hier", + ">": "beh-hier", + "-": "phys-hier", + "|": "pipe", + "?": "when", + "@": "stage", + "\\": "keyword" + }; + var tlvIndentUnit = 3; + var tlvTrackStatements = false; + var tlvIdentMatch = /^([~!@#\$%\^&\*-\+=\?\/\\\|'"<>]+)([\d\w_]*)/; // Matches an identifiere. + // Note that ':' is excluded, because of it's use in [:]. + var tlvFirstLevelIndentMatch = /^[! ] /; + var tlvLineIndentationMatch = /^[! ] */; + var tlvCommentMatch = /^\/[\/\*]/; + + + // Returns a style specific to the scope at the given indentation column. + // Type is one of: "indent", "scope-ident", "before-scope-ident". + function tlvScopeStyle(state, indentation, type) { + // Begin scope. + var depth = indentation / tlvIndentUnit; // TODO: Pass this in instead. + return "tlv-" + state.tlvIndentationStyle[depth] + "-" + type; + } + + // Return true if the next thing in the stream is an identifier with a mnemonic. + function tlvIdentNext(stream) { + var match; + return (match = stream.match(tlvIdentMatch, false)) && match[2].length > 0; + } + + CodeMirror.defineMIME("text/x-tlv", { + name: "verilog", + + hooks: { + + electricInput: false, + + + // Return undefined for verilog tokenizing, or style for TLV token (null not used). + // Standard CM styles are used for most formatting, but some TL-Verilog-specific highlighting + // can be enabled with the definition of cm-tlv-* styles, including highlighting for: + // - M4 tokens + // - TLV scope indentation + // - Statement delimitation (enabled by tlvTrackStatements) + token: function(stream, state) { + var style = undefined; + var match; // Return value of pattern matches. + + // Set highlighting mode based on code region (TLV or SV). + if (stream.sol() && ! state.tlvInBlockComment) { + // Process region. + if (stream.peek() == '\\') { + style = "def"; + stream.skipToEnd(); + if (stream.string.match(/\\SV/)) { + state.tlvCodeActive = false; + } else if (stream.string.match(/\\TLV/)){ + state.tlvCodeActive = true; + } + } + // Correct indentation in the face of a line prefix char. + if (state.tlvCodeActive && stream.pos == 0 && + (state.indented == 0) && (match = stream.match(tlvLineIndentationMatch, false))) { + state.indented = match[0].length; + } + + // Compute indentation state: + // o Auto indentation on next line + // o Indentation scope styles + var indented = state.indented; + var depth = indented / tlvIndentUnit; + if (depth <= state.tlvIndentationStyle.length) { + // not deeper than current scope + + var blankline = stream.string.length == indented; + var chPos = depth * tlvIndentUnit; + if (chPos < stream.string.length) { + var bodyString = stream.string.slice(chPos); + var ch = bodyString[0]; + if (tlvScopePrefixChars[ch] && ((match = bodyString.match(tlvIdentMatch)) && + tlvIdentifierStyle[match[1]])) { + // This line begins scope. + // Next line gets indented one level. + indented += tlvIndentUnit; + // Style the next level of indentation (except non-region keyword identifiers, + // which are statements themselves) + if (!(ch == "\\" && chPos > 0)) { + state.tlvIndentationStyle[depth] = tlvScopePrefixChars[ch]; + if (tlvTrackStatements) {state.statementComment = false;} + depth++; + } + } + } + // Clear out deeper indentation levels unless line is blank. + if (!blankline) { + while (state.tlvIndentationStyle.length > depth) { + state.tlvIndentationStyle.pop(); + } + } + } + // Set next level of indentation. + state.tlvNextIndent = indented; + } + + if (state.tlvCodeActive) { + // Highlight as TLV. + + var beginStatement = false; + if (tlvTrackStatements) { + // This starts a statement if the position is at the scope level + // and we're not within a statement leading comment. + beginStatement = + (stream.peek() != " ") && // not a space + (style === undefined) && // not a region identifier + !state.tlvInBlockComment && // not in block comment + //!stream.match(tlvCommentMatch, false) && // not comment start + (stream.column() == state.tlvIndentationStyle.length * tlvIndentUnit); // at scope level + if (beginStatement) { + if (state.statementComment) { + // statement already started by comment + beginStatement = false; + } + state.statementComment = + stream.match(tlvCommentMatch, false); // comment start + } + } + + var match; + if (style !== undefined) { + // Region line. + style += " " + tlvScopeStyle(state, 0, "scope-ident") + } else if (((stream.pos / tlvIndentUnit) < state.tlvIndentationStyle.length) && + (match = stream.match(stream.sol() ? tlvFirstLevelIndentMatch : /^ /))) { + // Indentation + style = // make this style distinct from the previous one to prevent + // codemirror from combining spans + "tlv-indent-" + (((stream.pos % 2) == 0) ? "even" : "odd") + + // and style it + " " + tlvScopeStyle(state, stream.pos - tlvIndentUnit, "indent"); + // Style the line prefix character. + if (match[0].charAt(0) == "!") { + style += " tlv-alert-line-prefix"; + } + // Place a class before a scope identifier. + if (tlvIdentNext(stream)) { + style += " " + tlvScopeStyle(state, stream.pos, "before-scope-ident"); + } + } else if (state.tlvInBlockComment) { + // In a block comment. + if (stream.match(/^.*?\*\//)) { + // Exit block comment. + state.tlvInBlockComment = false; + if (tlvTrackStatements && !stream.eol()) { + // Anything after comment is assumed to be real statement content. + state.statementComment = false; + } + } else { + stream.skipToEnd(); + } + style = "comment"; + } else if ((match = stream.match(tlvCommentMatch)) && !state.tlvInBlockComment) { + // Start comment. + if (match[0] == "//") { + // Line comment. + stream.skipToEnd(); + } else { + // Block comment. + state.tlvInBlockComment = true; + } + style = "comment"; + } else if (match = stream.match(tlvIdentMatch)) { + // looks like an identifier (or identifier prefix) + var prefix = match[1]; + var mnemonic = match[2]; + if (// is identifier prefix + tlvIdentifierStyle.hasOwnProperty(prefix) && + // has mnemonic or we're at the end of the line (maybe it hasn't been typed yet) + (mnemonic.length > 0 || stream.eol())) { + style = tlvIdentifierStyle[prefix]; + if (stream.column() == state.indented) { + // Begin scope. + style += " " + tlvScopeStyle(state, stream.column(), "scope-ident") + } + } else { + // Just swallow one character and try again. + // This enables subsequent identifier match with preceding symbol character, which + // is legal within a statement. (Eg, !$reset). It also enables detection of + // comment start with preceding symbols. + stream.backUp(stream.current().length - 1); + style = "tlv-default"; + } + } else if (stream.match(/^\t+/)) { + // Highlight tabs, which are illegal. + style = "tlv-tab"; + } else if (stream.match(/^[\[\]{}\(\);\:]+/)) { + // [:], (), {}, ;. + style = "meta"; + } else if (match = stream.match(/^[mM]4([\+_])?[\w\d_]*/)) { + // m4 pre proc + style = (match[1] == "+") ? "tlv-m4-plus" : "tlv-m4"; + } else if (stream.match(/^ +/)){ + // Skip over spaces. + if (stream.eol()) { + // Trailing spaces. + style = "error"; + } else { + // Non-trailing spaces. + style = "tlv-default"; + } + } else if (stream.match(/^[\w\d_]+/)) { + // alpha-numeric token. + style = "number"; + } else { + // Eat the next char w/ no formatting. + stream.next(); + style = "tlv-default"; + } + if (beginStatement) { + style += " tlv-statement"; + } + } else { + if (stream.match(/^[mM]4([\w\d_]*)/)) { + // m4 pre proc + style = "tlv-m4"; + } + } + return style; + }, + + indent: function(state) { + return (state.tlvCodeActive == true) ? state.tlvNextIndent : -1; + }, + + startState: function(state) { + state.tlvIndentationStyle = []; // Styles to use for each level of indentation. + state.tlvCodeActive = true; // True when we're in a TLV region (and at beginning of file). + state.tlvNextIndent = -1; // The number of spaces to autoindent the next line if tlvCodeActive. + state.tlvInBlockComment = false; // True inside /**/ comment. + if (tlvTrackStatements) { + state.statementComment = false; // True inside a statement's header comment. + } + } + + } + }); +}); diff --git a/docs/js/node_modules/codemirror/mode/vhdl/vhdl.js b/docs/js/node_modules/codemirror/mode/vhdl/vhdl.js new file mode 100644 index 000000000..133e67a26 --- /dev/null +++ b/docs/js/node_modules/codemirror/mode/vhdl/vhdl.js @@ -0,0 +1,189 @@ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: https://codemirror.net/LICENSE + +// Originally written by Alf Nielsen, re-written by Michael Zhou +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + +function words(str) { + var obj = {}, words = str.split(","); + for (var i = 0; i < words.length; ++i) { + var allCaps = words[i].toUpperCase(); + var firstCap = words[i].charAt(0).toUpperCase() + words[i].slice(1); + obj[words[i]] = true; + obj[allCaps] = true; + obj[firstCap] = true; + } + return obj; +} + +function metaHook(stream) { + stream.eatWhile(/[\w\$_]/); + return "meta"; +} + +CodeMirror.defineMode("vhdl", function(config, parserConfig) { + var indentUnit = config.indentUnit, + atoms = parserConfig.atoms || words("null"), + hooks = parserConfig.hooks || {"`": metaHook, "$": metaHook}, + multiLineStrings = parserConfig.multiLineStrings; + + var keywords = words("abs,access,after,alias,all,and,architecture,array,assert,attribute,begin,block," + + "body,buffer,bus,case,component,configuration,constant,disconnect,downto,else,elsif,end,end block,end case," + + "end component,end for,end generate,end if,end loop,end process,end record,end units,entity,exit,file,for," + + "function,generate,generic,generic map,group,guarded,if,impure,in,inertial,inout,is,label,library,linkage," + + "literal,loop,map,mod,nand,new,next,nor,null,of,on,open,or,others,out,package,package body,port,port map," + + "postponed,procedure,process,pure,range,record,register,reject,rem,report,return,rol,ror,select,severity,signal," + + "sla,sll,sra,srl,subtype,then,to,transport,type,unaffected,units,until,use,variable,wait,when,while,with,xnor,xor"); + + var blockKeywords = words("architecture,entity,begin,case,port,else,elsif,end,for,function,if"); + + var isOperatorChar = /[&|~>?]/; +var integers = /^-?([1-9][0-9]*|0[Xx][0-9A-Fa-f]+|0[0-7]*)/; +var floats = /^-?(([0-9]+\.[0-9]*|[0-9]*\.[0-9]+)([Ee][+-]?[0-9]+)?|[0-9]+[Ee][+-]?[0-9]+)/; +var identifiers = /^_?[A-Za-z][0-9A-Z_a-z-]*/; +var identifiersEnd = /^_?[A-Za-z][0-9A-Z_a-z-]*(?=\s*;)/; +var strings = /^"[^"]*"/; +var multilineComments = /^\/\*.*?\*\//; +var multilineCommentsStart = /^\/\*.*/; +var multilineCommentsEnd = /^.*?\*\//; + +function readToken(stream, state) { + // whitespace + if (stream.eatSpace()) return null; + + // comment + if (state.inComment) { + if (stream.match(multilineCommentsEnd)) { + state.inComment = false; + return "comment"; + } + stream.skipToEnd(); + return "comment"; + } + if (stream.match("//")) { + stream.skipToEnd(); + return "comment"; + } + if (stream.match(multilineComments)) return "comment"; + if (stream.match(multilineCommentsStart)) { + state.inComment = true; + return "comment"; + } + + // integer and float + if (stream.match(/^-?[0-9\.]/, false)) { + if (stream.match(integers) || stream.match(floats)) return "number"; + } + + // string + if (stream.match(strings)) return "string"; + + // identifier + if (state.startDef && stream.match(identifiers)) return "def"; + + if (state.endDef && stream.match(identifiersEnd)) { + state.endDef = false; + return "def"; + } + + if (stream.match(keywords)) return "keyword"; + + if (stream.match(types)) { + var lastToken = state.lastToken; + var nextToken = (stream.match(/^\s*(.+?)\b/, false) || [])[1]; + + if (lastToken === ":" || lastToken === "implements" || + nextToken === "implements" || nextToken === "=") { + // Used as identifier + return "builtin"; + } else { + // Used as type + return "variable-3"; + } + } + + if (stream.match(builtins)) return "builtin"; + if (stream.match(atoms)) return "atom"; + if (stream.match(identifiers)) return "variable"; + + // other + if (stream.match(singleOperators)) return "operator"; + + // unrecognized + stream.next(); + return null; +}; + +CodeMirror.defineMode("webidl", function() { + return { + startState: function() { + return { + // Is in multiline comment + inComment: false, + // Last non-whitespace, matched token + lastToken: "", + // Next token is a definition + startDef: false, + // Last token of the statement is a definition + endDef: false + }; + }, + token: function(stream, state) { + var style = readToken(stream, state); + + if (style) { + var cur = stream.current(); + state.lastToken = cur; + if (style === "keyword") { + state.startDef = startDefs.test(cur); + state.endDef = state.endDef || endDefs.test(cur); + } else { + state.startDef = false; + } + } + + return style; + } + }; +}); + +CodeMirror.defineMIME("text/x-webidl", "webidl"); +}); diff --git a/docs/js/node_modules/codemirror/mode/xml/xml.js b/docs/js/node_modules/codemirror/mode/xml/xml.js new file mode 100644 index 000000000..73c6e0e0d --- /dev/null +++ b/docs/js/node_modules/codemirror/mode/xml/xml.js @@ -0,0 +1,413 @@ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: https://codemirror.net/LICENSE + +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + +var htmlConfig = { + autoSelfClosers: {'area': true, 'base': true, 'br': true, 'col': true, 'command': true, + 'embed': true, 'frame': true, 'hr': true, 'img': true, 'input': true, + 'keygen': true, 'link': true, 'meta': true, 'param': true, 'source': true, + 'track': true, 'wbr': true, 'menuitem': true}, + implicitlyClosed: {'dd': true, 'li': true, 'optgroup': true, 'option': true, 'p': true, + 'rp': true, 'rt': true, 'tbody': true, 'td': true, 'tfoot': true, + 'th': true, 'tr': true}, + contextGrabbers: { + 'dd': {'dd': true, 'dt': true}, + 'dt': {'dd': true, 'dt': true}, + 'li': {'li': true}, + 'option': {'option': true, 'optgroup': true}, + 'optgroup': {'optgroup': true}, + 'p': {'address': true, 'article': true, 'aside': true, 'blockquote': true, 'dir': true, + 'div': true, 'dl': true, 'fieldset': true, 'footer': true, 'form': true, + 'h1': true, 'h2': true, 'h3': true, 'h4': true, 'h5': true, 'h6': true, + 'header': true, 'hgroup': true, 'hr': true, 'menu': true, 'nav': true, 'ol': true, + 'p': true, 'pre': true, 'section': true, 'table': true, 'ul': true}, + 'rp': {'rp': true, 'rt': true}, + 'rt': {'rp': true, 'rt': true}, + 'tbody': {'tbody': true, 'tfoot': true}, + 'td': {'td': true, 'th': true}, + 'tfoot': {'tbody': true}, + 'th': {'td': true, 'th': true}, + 'thead': {'tbody': true, 'tfoot': true}, + 'tr': {'tr': true} + }, + doNotIndent: {"pre": true}, + allowUnquoted: true, + allowMissing: true, + caseFold: true +} + +var xmlConfig = { + autoSelfClosers: {}, + implicitlyClosed: {}, + contextGrabbers: {}, + doNotIndent: {}, + allowUnquoted: false, + allowMissing: false, + allowMissingTagName: false, + caseFold: false +} + +CodeMirror.defineMode("xml", function(editorConf, config_) { + var indentUnit = editorConf.indentUnit + var config = {} + var defaults = config_.htmlMode ? htmlConfig : xmlConfig + for (var prop in defaults) config[prop] = defaults[prop] + for (var prop in config_) config[prop] = config_[prop] + + // Return variables for tokenizers + var type, setStyle; + + function inText(stream, state) { + function chain(parser) { + state.tokenize = parser; + return parser(stream, state); + } + + var ch = stream.next(); + if (ch == "<") { + if (stream.eat("!")) { + if (stream.eat("[")) { + if (stream.match("CDATA[")) return chain(inBlock("atom", "]]>")); + else return null; + } else if (stream.match("--")) { + return chain(inBlock("comment", "-->")); + } else if (stream.match("DOCTYPE", true, true)) { + stream.eatWhile(/[\w\._\-]/); + return chain(doctype(1)); + } else { + return null; + } + } else if (stream.eat("?")) { + stream.eatWhile(/[\w\._\-]/); + state.tokenize = inBlock("meta", "?>"); + return "meta"; + } else { + type = stream.eat("/") ? "closeTag" : "openTag"; + state.tokenize = inTag; + return "tag bracket"; + } + } else if (ch == "&") { + var ok; + if (stream.eat("#")) { + if (stream.eat("x")) { + ok = stream.eatWhile(/[a-fA-F\d]/) && stream.eat(";"); + } else { + ok = stream.eatWhile(/[\d]/) && stream.eat(";"); + } + } else { + ok = stream.eatWhile(/[\w\.\-:]/) && stream.eat(";"); + } + return ok ? "atom" : "error"; + } else { + stream.eatWhile(/[^&<]/); + return null; + } + } + inText.isInText = true; + + function inTag(stream, state) { + var ch = stream.next(); + if (ch == ">" || (ch == "/" && stream.eat(">"))) { + state.tokenize = inText; + type = ch == ">" ? "endTag" : "selfcloseTag"; + return "tag bracket"; + } else if (ch == "=") { + type = "equals"; + return null; + } else if (ch == "<") { + state.tokenize = inText; + state.state = baseState; + state.tagName = state.tagStart = null; + var next = state.tokenize(stream, state); + return next ? next + " tag error" : "tag error"; + } else if (/[\'\"]/.test(ch)) { + state.tokenize = inAttribute(ch); + state.stringStartCol = stream.column(); + return state.tokenize(stream, state); + } else { + stream.match(/^[^\s\u00a0=<>\"\']*[^\s\u00a0=<>\"\'\/]/); + return "word"; + } + } + + function inAttribute(quote) { + var closure = function(stream, state) { + while (!stream.eol()) { + if (stream.next() == quote) { + state.tokenize = inTag; + break; + } + } + return "string"; + }; + closure.isInAttribute = true; + return closure; + } + + function inBlock(style, terminator) { + return function(stream, state) { + while (!stream.eol()) { + if (stream.match(terminator)) { + state.tokenize = inText; + break; + } + stream.next(); + } + return style; + } + } + + function doctype(depth) { + return function(stream, state) { + var ch; + while ((ch = stream.next()) != null) { + if (ch == "<") { + state.tokenize = doctype(depth + 1); + return state.tokenize(stream, state); + } else if (ch == ">") { + if (depth == 1) { + state.tokenize = inText; + break; + } else { + state.tokenize = doctype(depth - 1); + return state.tokenize(stream, state); + } + } + } + return "meta"; + }; + } + + function Context(state, tagName, startOfLine) { + this.prev = state.context; + this.tagName = tagName; + this.indent = state.indented; + this.startOfLine = startOfLine; + if (config.doNotIndent.hasOwnProperty(tagName) || (state.context && state.context.noIndent)) + this.noIndent = true; + } + function popContext(state) { + if (state.context) state.context = state.context.prev; + } + function maybePopContext(state, nextTagName) { + var parentTagName; + while (true) { + if (!state.context) { + return; + } + parentTagName = state.context.tagName; + if (!config.contextGrabbers.hasOwnProperty(parentTagName) || + !config.contextGrabbers[parentTagName].hasOwnProperty(nextTagName)) { + return; + } + popContext(state); + } + } + + function baseState(type, stream, state) { + if (type == "openTag") { + state.tagStart = stream.column(); + return tagNameState; + } else if (type == "closeTag") { + return closeTagNameState; + } else { + return baseState; + } + } + function tagNameState(type, stream, state) { + if (type == "word") { + state.tagName = stream.current(); + setStyle = "tag"; + return attrState; + } else if (config.allowMissingTagName && type == "endTag") { + setStyle = "tag bracket"; + return attrState(type, stream, state); + } else { + setStyle = "error"; + return tagNameState; + } + } + function closeTagNameState(type, stream, state) { + if (type == "word") { + var tagName = stream.current(); + if (state.context && state.context.tagName != tagName && + config.implicitlyClosed.hasOwnProperty(state.context.tagName)) + popContext(state); + if ((state.context && state.context.tagName == tagName) || config.matchClosing === false) { + setStyle = "tag"; + return closeState; + } else { + setStyle = "tag error"; + return closeStateErr; + } + } else if (config.allowMissingTagName && type == "endTag") { + setStyle = "tag bracket"; + return closeState(type, stream, state); + } else { + setStyle = "error"; + return closeStateErr; + } + } + + function closeState(type, _stream, state) { + if (type != "endTag") { + setStyle = "error"; + return closeState; + } + popContext(state); + return baseState; + } + function closeStateErr(type, stream, state) { + setStyle = "error"; + return closeState(type, stream, state); + } + + function attrState(type, _stream, state) { + if (type == "word") { + setStyle = "attribute"; + return attrEqState; + } else if (type == "endTag" || type == "selfcloseTag") { + var tagName = state.tagName, tagStart = state.tagStart; + state.tagName = state.tagStart = null; + if (type == "selfcloseTag" || + config.autoSelfClosers.hasOwnProperty(tagName)) { + maybePopContext(state, tagName); + } else { + maybePopContext(state, tagName); + state.context = new Context(state, tagName, tagStart == state.indented); + } + return baseState; + } + setStyle = "error"; + return attrState; + } + function attrEqState(type, stream, state) { + if (type == "equals") return attrValueState; + if (!config.allowMissing) setStyle = "error"; + return attrState(type, stream, state); + } + function attrValueState(type, stream, state) { + if (type == "string") return attrContinuedState; + if (type == "word" && config.allowUnquoted) {setStyle = "string"; return attrState;} + setStyle = "error"; + return attrState(type, stream, state); + } + function attrContinuedState(type, stream, state) { + if (type == "string") return attrContinuedState; + return attrState(type, stream, state); + } + + return { + startState: function(baseIndent) { + var state = {tokenize: inText, + state: baseState, + indented: baseIndent || 0, + tagName: null, tagStart: null, + context: null} + if (baseIndent != null) state.baseIndent = baseIndent + return state + }, + + token: function(stream, state) { + if (!state.tagName && stream.sol()) + state.indented = stream.indentation(); + + if (stream.eatSpace()) return null; + type = null; + var style = state.tokenize(stream, state); + if ((style || type) && style != "comment") { + setStyle = null; + state.state = state.state(type || style, stream, state); + if (setStyle) + style = setStyle == "error" ? style + " error" : setStyle; + } + return style; + }, + + indent: function(state, textAfter, fullLine) { + var context = state.context; + // Indent multi-line strings (e.g. css). + if (state.tokenize.isInAttribute) { + if (state.tagStart == state.indented) + return state.stringStartCol + 1; + else + return state.indented + indentUnit; + } + if (context && context.noIndent) return CodeMirror.Pass; + if (state.tokenize != inTag && state.tokenize != inText) + return fullLine ? fullLine.match(/^(\s*)/)[0].length : 0; + // Indent the starts of attribute names. + if (state.tagName) { + if (config.multilineTagIndentPastTag !== false) + return state.tagStart + state.tagName.length + 2; + else + return state.tagStart + indentUnit * (config.multilineTagIndentFactor || 1); + } + if (config.alignCDATA && /$/, + blockCommentStart: "", + + configuration: config.htmlMode ? "html" : "xml", + helperType: config.htmlMode ? "html" : "xml", + + skipAttribute: function(state) { + if (state.state == attrValueState) + state.state = attrState + }, + + xmlCurrentTag: function(state) { + return state.tagName ? {name: state.tagName, close: state.type == "closeTag"} : null + }, + + xmlCurrentContext: function(state) { + var context = [] + for (var cx = state.context; cx; cx = cx.prev) + if (cx.tagName) context.push(cx.tagName) + return context.reverse() + } + }; +}); + +CodeMirror.defineMIME("text/xml", "xml"); +CodeMirror.defineMIME("application/xml", "xml"); +if (!CodeMirror.mimeModes.hasOwnProperty("text/html")) + CodeMirror.defineMIME("text/html", {name: "xml", htmlMode: true}); + +}); diff --git a/docs/js/node_modules/codemirror/mode/xquery/xquery.js b/docs/js/node_modules/codemirror/mode/xquery/xquery.js new file mode 100644 index 000000000..395b6a701 --- /dev/null +++ b/docs/js/node_modules/codemirror/mode/xquery/xquery.js @@ -0,0 +1,448 @@ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: https://codemirror.net/LICENSE + +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + +CodeMirror.defineMode("xquery", function() { + + // The keywords object is set to the result of this self executing + // function. Each keyword is a property of the keywords object whose + // value is {type: atype, style: astyle} + var keywords = function(){ + // convenience functions used to build keywords object + function kw(type) {return {type: type, style: "keyword"};} + var operator = kw("operator") + , atom = {type: "atom", style: "atom"} + , punctuation = {type: "punctuation", style: null} + , qualifier = {type: "axis_specifier", style: "qualifier"}; + + // kwObj is what is return from this function at the end + var kwObj = { + ',': punctuation + }; + + // a list of 'basic' keywords. For each add a property to kwObj with the value of + // {type: basic[i], style: "keyword"} e.g. 'after' --> {type: "after", style: "keyword"} + var basic = ['after', 'all', 'allowing', 'ancestor', 'ancestor-or-self', 'any', 'array', 'as', + 'ascending', 'at', 'attribute', 'base-uri', 'before', 'boundary-space', 'by', 'case', 'cast', + 'castable', 'catch', 'child', 'collation', 'comment', 'construction', 'contains', 'content', + 'context', 'copy', 'copy-namespaces', 'count', 'decimal-format', 'declare', 'default', 'delete', + 'descendant', 'descendant-or-self', 'descending', 'diacritics', 'different', 'distance', + 'document', 'document-node', 'element', 'else', 'empty', 'empty-sequence', 'encoding', 'end', + 'entire', 'every', 'exactly', 'except', 'external', 'first', 'following', 'following-sibling', + 'for', 'from', 'ftand', 'ftnot', 'ft-option', 'ftor', 'function', 'fuzzy', 'greatest', 'group', + 'if', 'import', 'in', 'inherit', 'insensitive', 'insert', 'instance', 'intersect', 'into', + 'invoke', 'is', 'item', 'language', 'last', 'lax', 'least', 'let', 'levels', 'lowercase', 'map', + 'modify', 'module', 'most', 'namespace', 'next', 'no', 'node', 'nodes', 'no-inherit', + 'no-preserve', 'not', 'occurs', 'of', 'only', 'option', 'order', 'ordered', 'ordering', + 'paragraph', 'paragraphs', 'parent', 'phrase', 'preceding', 'preceding-sibling', 'preserve', + 'previous', 'processing-instruction', 'relationship', 'rename', 'replace', 'return', + 'revalidation', 'same', 'satisfies', 'schema', 'schema-attribute', 'schema-element', 'score', + 'self', 'sensitive', 'sentence', 'sentences', 'sequence', 'skip', 'sliding', 'some', 'stable', + 'start', 'stemming', 'stop', 'strict', 'strip', 'switch', 'text', 'then', 'thesaurus', 'times', + 'to', 'transform', 'treat', 'try', 'tumbling', 'type', 'typeswitch', 'union', 'unordered', + 'update', 'updating', 'uppercase', 'using', 'validate', 'value', 'variable', 'version', + 'weight', 'when', 'where', 'wildcards', 'window', 'with', 'without', 'word', 'words', 'xquery']; + for(var i=0, l=basic.length; i < l; i++) { kwObj[basic[i]] = kw(basic[i]);}; + + // a list of types. For each add a property to kwObj with the value of + // {type: "atom", style: "atom"} + var types = ['xs:anyAtomicType', 'xs:anySimpleType', 'xs:anyType', 'xs:anyURI', + 'xs:base64Binary', 'xs:boolean', 'xs:byte', 'xs:date', 'xs:dateTime', 'xs:dateTimeStamp', + 'xs:dayTimeDuration', 'xs:decimal', 'xs:double', 'xs:duration', 'xs:ENTITIES', 'xs:ENTITY', + 'xs:float', 'xs:gDay', 'xs:gMonth', 'xs:gMonthDay', 'xs:gYear', 'xs:gYearMonth', 'xs:hexBinary', + 'xs:ID', 'xs:IDREF', 'xs:IDREFS', 'xs:int', 'xs:integer', 'xs:item', 'xs:java', 'xs:language', + 'xs:long', 'xs:Name', 'xs:NCName', 'xs:negativeInteger', 'xs:NMTOKEN', 'xs:NMTOKENS', + 'xs:nonNegativeInteger', 'xs:nonPositiveInteger', 'xs:normalizedString', 'xs:NOTATION', + 'xs:numeric', 'xs:positiveInteger', 'xs:precisionDecimal', 'xs:QName', 'xs:short', 'xs:string', + 'xs:time', 'xs:token', 'xs:unsignedByte', 'xs:unsignedInt', 'xs:unsignedLong', + 'xs:unsignedShort', 'xs:untyped', 'xs:untypedAtomic', 'xs:yearMonthDuration']; + for(var i=0, l=types.length; i < l; i++) { kwObj[types[i]] = atom;}; + + // each operator will add a property to kwObj with value of {type: "operator", style: "keyword"} + var operators = ['eq', 'ne', 'lt', 'le', 'gt', 'ge', ':=', '=', '>', '>=', '<', '<=', '.', '|', '?', 'and', 'or', 'div', 'idiv', 'mod', '*', '/', '+', '-']; + for(var i=0, l=operators.length; i < l; i++) { kwObj[operators[i]] = operator;}; + + // each axis_specifiers will add a property to kwObj with value of {type: "axis_specifier", style: "qualifier"} + var axis_specifiers = ["self::", "attribute::", "child::", "descendant::", "descendant-or-self::", "parent::", + "ancestor::", "ancestor-or-self::", "following::", "preceding::", "following-sibling::", "preceding-sibling::"]; + for(var i=0, l=axis_specifiers.length; i < l; i++) { kwObj[axis_specifiers[i]] = qualifier; }; + + return kwObj; + }(); + + function chain(stream, state, f) { + state.tokenize = f; + return f(stream, state); + } + + // the primary mode tokenizer + function tokenBase(stream, state) { + var ch = stream.next(), + mightBeFunction = false, + isEQName = isEQNameAhead(stream); + + // an XML tag (if not in some sub, chained tokenizer) + if (ch == "<") { + if(stream.match("!--", true)) + return chain(stream, state, tokenXMLComment); + + if(stream.match("![CDATA", false)) { + state.tokenize = tokenCDATA; + return "tag"; + } + + if(stream.match("?", false)) { + return chain(stream, state, tokenPreProcessing); + } + + var isclose = stream.eat("/"); + stream.eatSpace(); + var tagName = "", c; + while ((c = stream.eat(/[^\s\u00a0=<>\"\'\/?]/))) tagName += c; + + return chain(stream, state, tokenTag(tagName, isclose)); + } + // start code block + else if(ch == "{") { + pushStateStack(state, { type: "codeblock"}); + return null; + } + // end code block + else if(ch == "}") { + popStateStack(state); + return null; + } + // if we're in an XML block + else if(isInXmlBlock(state)) { + if(ch == ">") + return "tag"; + else if(ch == "/" && stream.eat(">")) { + popStateStack(state); + return "tag"; + } + else + return "variable"; + } + // if a number + else if (/\d/.test(ch)) { + stream.match(/^\d*(?:\.\d*)?(?:E[+\-]?\d+)?/); + return "atom"; + } + // comment start + else if (ch === "(" && stream.eat(":")) { + pushStateStack(state, { type: "comment"}); + return chain(stream, state, tokenComment); + } + // quoted string + else if (!isEQName && (ch === '"' || ch === "'")) + return chain(stream, state, tokenString(ch)); + // variable + else if(ch === "$") { + return chain(stream, state, tokenVariable); + } + // assignment + else if(ch ===":" && stream.eat("=")) { + return "keyword"; + } + // open paren + else if(ch === "(") { + pushStateStack(state, { type: "paren"}); + return null; + } + // close paren + else if(ch === ")") { + popStateStack(state); + return null; + } + // open paren + else if(ch === "[") { + pushStateStack(state, { type: "bracket"}); + return null; + } + // close paren + else if(ch === "]") { + popStateStack(state); + return null; + } + else { + var known = keywords.propertyIsEnumerable(ch) && keywords[ch]; + + // if there's a EQName ahead, consume the rest of the string portion, it's likely a function + if(isEQName && ch === '\"') while(stream.next() !== '"'){} + if(isEQName && ch === '\'') while(stream.next() !== '\''){} + + // gobble up a word if the character is not known + if(!known) stream.eatWhile(/[\w\$_-]/); + + // gobble a colon in the case that is a lib func type call fn:doc + var foundColon = stream.eat(":"); + + // if there's not a second colon, gobble another word. Otherwise, it's probably an axis specifier + // which should get matched as a keyword + if(!stream.eat(":") && foundColon) { + stream.eatWhile(/[\w\$_-]/); + } + // if the next non whitespace character is an open paren, this is probably a function (if not a keyword of other sort) + if(stream.match(/^[ \t]*\(/, false)) { + mightBeFunction = true; + } + // is the word a keyword? + var word = stream.current(); + known = keywords.propertyIsEnumerable(word) && keywords[word]; + + // if we think it's a function call but not yet known, + // set style to variable for now for lack of something better + if(mightBeFunction && !known) known = {type: "function_call", style: "variable def"}; + + // if the previous word was element, attribute, axis specifier, this word should be the name of that + if(isInXmlConstructor(state)) { + popStateStack(state); + return "variable"; + } + // as previously checked, if the word is element,attribute, axis specifier, call it an "xmlconstructor" and + // push the stack so we know to look for it on the next word + if(word == "element" || word == "attribute" || known.type == "axis_specifier") pushStateStack(state, {type: "xmlconstructor"}); + + // if the word is known, return the details of that else just call this a generic 'word' + return known ? known.style : "variable"; + } + } + + // handle comments, including nested + function tokenComment(stream, state) { + var maybeEnd = false, maybeNested = false, nestedCount = 0, ch; + while (ch = stream.next()) { + if (ch == ")" && maybeEnd) { + if(nestedCount > 0) + nestedCount--; + else { + popStateStack(state); + break; + } + } + else if(ch == ":" && maybeNested) { + nestedCount++; + } + maybeEnd = (ch == ":"); + maybeNested = (ch == "("); + } + + return "comment"; + } + + // tokenizer for string literals + // optionally pass a tokenizer function to set state.tokenize back to when finished + function tokenString(quote, f) { + return function(stream, state) { + var ch; + + if(isInString(state) && stream.current() == quote) { + popStateStack(state); + if(f) state.tokenize = f; + return "string"; + } + + pushStateStack(state, { type: "string", name: quote, tokenize: tokenString(quote, f) }); + + // if we're in a string and in an XML block, allow an embedded code block + if(stream.match("{", false) && isInXmlAttributeBlock(state)) { + state.tokenize = tokenBase; + return "string"; + } + + + while (ch = stream.next()) { + if (ch == quote) { + popStateStack(state); + if(f) state.tokenize = f; + break; + } + else { + // if we're in a string and in an XML block, allow an embedded code block in an attribute + if(stream.match("{", false) && isInXmlAttributeBlock(state)) { + state.tokenize = tokenBase; + return "string"; + } + + } + } + + return "string"; + }; + } + + // tokenizer for variables + function tokenVariable(stream, state) { + var isVariableChar = /[\w\$_-]/; + + // a variable may start with a quoted EQName so if the next character is quote, consume to the next quote + if(stream.eat("\"")) { + while(stream.next() !== '\"'){}; + stream.eat(":"); + } else { + stream.eatWhile(isVariableChar); + if(!stream.match(":=", false)) stream.eat(":"); + } + stream.eatWhile(isVariableChar); + state.tokenize = tokenBase; + return "variable"; + } + + // tokenizer for XML tags + function tokenTag(name, isclose) { + return function(stream, state) { + stream.eatSpace(); + if(isclose && stream.eat(">")) { + popStateStack(state); + state.tokenize = tokenBase; + return "tag"; + } + // self closing tag without attributes? + if(!stream.eat("/")) + pushStateStack(state, { type: "tag", name: name, tokenize: tokenBase}); + if(!stream.eat(">")) { + state.tokenize = tokenAttribute; + return "tag"; + } + else { + state.tokenize = tokenBase; + } + return "tag"; + }; + } + + // tokenizer for XML attributes + function tokenAttribute(stream, state) { + var ch = stream.next(); + + if(ch == "/" && stream.eat(">")) { + if(isInXmlAttributeBlock(state)) popStateStack(state); + if(isInXmlBlock(state)) popStateStack(state); + return "tag"; + } + if(ch == ">") { + if(isInXmlAttributeBlock(state)) popStateStack(state); + return "tag"; + } + if(ch == "=") + return null; + // quoted string + if (ch == '"' || ch == "'") + return chain(stream, state, tokenString(ch, tokenAttribute)); + + if(!isInXmlAttributeBlock(state)) + pushStateStack(state, { type: "attribute", tokenize: tokenAttribute}); + + stream.eat(/[a-zA-Z_:]/); + stream.eatWhile(/[-a-zA-Z0-9_:.]/); + stream.eatSpace(); + + // the case where the attribute has not value and the tag was closed + if(stream.match(">", false) || stream.match("/", false)) { + popStateStack(state); + state.tokenize = tokenBase; + } + + return "attribute"; + } + + // handle comments, including nested + function tokenXMLComment(stream, state) { + var ch; + while (ch = stream.next()) { + if (ch == "-" && stream.match("->", true)) { + state.tokenize = tokenBase; + return "comment"; + } + } + } + + + // handle CDATA + function tokenCDATA(stream, state) { + var ch; + while (ch = stream.next()) { + if (ch == "]" && stream.match("]", true)) { + state.tokenize = tokenBase; + return "comment"; + } + } + } + + // handle preprocessing instructions + function tokenPreProcessing(stream, state) { + var ch; + while (ch = stream.next()) { + if (ch == "?" && stream.match(">", true)) { + state.tokenize = tokenBase; + return "comment meta"; + } + } + } + + + // functions to test the current context of the state + function isInXmlBlock(state) { return isIn(state, "tag"); } + function isInXmlAttributeBlock(state) { return isIn(state, "attribute"); } + function isInXmlConstructor(state) { return isIn(state, "xmlconstructor"); } + function isInString(state) { return isIn(state, "string"); } + + function isEQNameAhead(stream) { + // assume we've already eaten a quote (") + if(stream.current() === '"') + return stream.match(/^[^\"]+\"\:/, false); + else if(stream.current() === '\'') + return stream.match(/^[^\"]+\'\:/, false); + else + return false; + } + + function isIn(state, type) { + return (state.stack.length && state.stack[state.stack.length - 1].type == type); + } + + function pushStateStack(state, newState) { + state.stack.push(newState); + } + + function popStateStack(state) { + state.stack.pop(); + var reinstateTokenize = state.stack.length && state.stack[state.stack.length-1].tokenize; + state.tokenize = reinstateTokenize || tokenBase; + } + + // the interface for the mode API + return { + startState: function() { + return { + tokenize: tokenBase, + cc: [], + stack: [] + }; + }, + + token: function(stream, state) { + if (stream.eatSpace()) return null; + var style = state.tokenize(stream, state); + return style; + }, + + blockCommentStart: "(:", + blockCommentEnd: ":)" + + }; + +}); + +CodeMirror.defineMIME("application/xquery", "xquery"); + +}); diff --git a/docs/js/node_modules/codemirror/mode/yacas/yacas.js b/docs/js/node_modules/codemirror/mode/yacas/yacas.js new file mode 100644 index 000000000..b7ac96b71 --- /dev/null +++ b/docs/js/node_modules/codemirror/mode/yacas/yacas.js @@ -0,0 +1,204 @@ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: https://codemirror.net/LICENSE + +// Yacas mode copyright (c) 2015 by Grzegorz Mazur +// Loosely based on mathematica mode by Calin Barbat + +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + +CodeMirror.defineMode('yacas', function(_config, _parserConfig) { + + function words(str) { + var obj = {}, words = str.split(" "); + for (var i = 0; i < words.length; ++i) obj[words[i]] = true; + return obj; + } + + var bodiedOps = words("Assert BackQuote D Defun Deriv For ForEach FromFile " + + "FromString Function Integrate InverseTaylor Limit " + + "LocalSymbols Macro MacroRule MacroRulePattern " + + "NIntegrate Rule RulePattern Subst TD TExplicitSum " + + "TSum Taylor Taylor1 Taylor2 Taylor3 ToFile " + + "ToStdout ToString TraceRule Until While"); + + // patterns + var pFloatForm = "(?:(?:\\.\\d+|\\d+\\.\\d*|\\d+)(?:[eE][+-]?\\d+)?)"; + var pIdentifier = "(?:[a-zA-Z\\$'][a-zA-Z0-9\\$']*)"; + + // regular expressions + var reFloatForm = new RegExp(pFloatForm); + var reIdentifier = new RegExp(pIdentifier); + var rePattern = new RegExp(pIdentifier + "?_" + pIdentifier); + var reFunctionLike = new RegExp(pIdentifier + "\\s*\\("); + + function tokenBase(stream, state) { + var ch; + + // get next character + ch = stream.next(); + + // string + if (ch === '"') { + state.tokenize = tokenString; + return state.tokenize(stream, state); + } + + // comment + if (ch === '/') { + if (stream.eat('*')) { + state.tokenize = tokenComment; + return state.tokenize(stream, state); + } + if (stream.eat("/")) { + stream.skipToEnd(); + return "comment"; + } + } + + // go back one character + stream.backUp(1); + + // update scope info + var m = stream.match(/^(\w+)\s*\(/, false); + if (m !== null && bodiedOps.hasOwnProperty(m[1])) + state.scopes.push('bodied'); + + var scope = currentScope(state); + + if (scope === 'bodied' && ch === '[') + state.scopes.pop(); + + if (ch === '[' || ch === '{' || ch === '(') + state.scopes.push(ch); + + scope = currentScope(state); + + if (scope === '[' && ch === ']' || + scope === '{' && ch === '}' || + scope === '(' && ch === ')') + state.scopes.pop(); + + if (ch === ';') { + while (scope === 'bodied') { + state.scopes.pop(); + scope = currentScope(state); + } + } + + // look for ordered rules + if (stream.match(/\d+ *#/, true, false)) { + return 'qualifier'; + } + + // look for numbers + if (stream.match(reFloatForm, true, false)) { + return 'number'; + } + + // look for placeholders + if (stream.match(rePattern, true, false)) { + return 'variable-3'; + } + + // match all braces separately + if (stream.match(/(?:\[|\]|{|}|\(|\))/, true, false)) { + return 'bracket'; + } + + // literals looking like function calls + if (stream.match(reFunctionLike, true, false)) { + stream.backUp(1); + return 'variable'; + } + + // all other identifiers + if (stream.match(reIdentifier, true, false)) { + return 'variable-2'; + } + + // operators; note that operators like @@ or /; are matched separately for each symbol. + if (stream.match(/(?:\\|\+|\-|\*|\/|,|;|\.|:|@|~|=|>|<|&|\||_|`|'|\^|\?|!|%|#)/, true, false)) { + return 'operator'; + } + + // everything else is an error + return 'error'; + } + + function tokenString(stream, state) { + var next, end = false, escaped = false; + while ((next = stream.next()) != null) { + if (next === '"' && !escaped) { + end = true; + break; + } + escaped = !escaped && next === '\\'; + } + if (end && !escaped) { + state.tokenize = tokenBase; + } + return 'string'; + }; + + function tokenComment(stream, state) { + var prev, next; + while((next = stream.next()) != null) { + if (prev === '*' && next === '/') { + state.tokenize = tokenBase; + break; + } + prev = next; + } + return 'comment'; + } + + function currentScope(state) { + var scope = null; + if (state.scopes.length > 0) + scope = state.scopes[state.scopes.length - 1]; + return scope; + } + + return { + startState: function() { + return { + tokenize: tokenBase, + scopes: [] + }; + }, + token: function(stream, state) { + if (stream.eatSpace()) return null; + return state.tokenize(stream, state); + }, + indent: function(state, textAfter) { + if (state.tokenize !== tokenBase && state.tokenize !== null) + return CodeMirror.Pass; + + var delta = 0; + if (textAfter === ']' || textAfter === '];' || + textAfter === '}' || textAfter === '};' || + textAfter === ');') + delta = -1; + + return (state.scopes.length + delta) * _config.indentUnit; + }, + electricChars: "{}[]();", + blockCommentStart: "/*", + blockCommentEnd: "*/", + lineComment: "//" + }; +}); + +CodeMirror.defineMIME('text/x-yacas', { + name: 'yacas' +}); + +}); diff --git a/docs/js/node_modules/codemirror/mode/yaml-frontmatter/yaml-frontmatter.js b/docs/js/node_modules/codemirror/mode/yaml-frontmatter/yaml-frontmatter.js new file mode 100644 index 000000000..87fdf80d0 --- /dev/null +++ b/docs/js/node_modules/codemirror/mode/yaml-frontmatter/yaml-frontmatter.js @@ -0,0 +1,68 @@ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: https://codemirror.net/LICENSE + +(function (mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror"), require("../yaml/yaml")) + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror", "../yaml/yaml"], mod) + else // Plain browser env + mod(CodeMirror) +})(function (CodeMirror) { + + var START = 0, FRONTMATTER = 1, BODY = 2 + + // a mixed mode for Markdown text with an optional YAML front matter + CodeMirror.defineMode("yaml-frontmatter", function (config, parserConfig) { + var yamlMode = CodeMirror.getMode(config, "yaml") + var innerMode = CodeMirror.getMode(config, parserConfig && parserConfig.base || "gfm") + + function curMode(state) { + return state.state == BODY ? innerMode : yamlMode + } + + return { + startState: function () { + return { + state: START, + inner: CodeMirror.startState(yamlMode) + } + }, + copyState: function (state) { + return { + state: state.state, + inner: CodeMirror.copyState(curMode(state), state.inner) + } + }, + token: function (stream, state) { + if (state.state == START) { + if (stream.match(/---/, false)) { + state.state = FRONTMATTER + return yamlMode.token(stream, state.inner) + } else { + state.state = BODY + state.inner = CodeMirror.startState(innerMode) + return innerMode.token(stream, state.inner) + } + } else if (state.state == FRONTMATTER) { + var end = stream.sol() && stream.match(/(---|\.\.\.)/, false) + var style = yamlMode.token(stream, state.inner) + if (end) { + state.state = BODY + state.inner = CodeMirror.startState(innerMode) + } + return style + } else { + return innerMode.token(stream, state.inner) + } + }, + innerMode: function (state) { + return {mode: curMode(state), state: state.inner} + }, + blankLine: function (state) { + var mode = curMode(state) + if (mode.blankLine) return mode.blankLine(state.inner) + } + } + }) +}); diff --git a/docs/js/node_modules/codemirror/mode/yaml/yaml.js b/docs/js/node_modules/codemirror/mode/yaml/yaml.js new file mode 100644 index 000000000..a29d7ea4a --- /dev/null +++ b/docs/js/node_modules/codemirror/mode/yaml/yaml.js @@ -0,0 +1,120 @@ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: https://codemirror.net/LICENSE + +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + +CodeMirror.defineMode("yaml", function() { + + var cons = ['true', 'false', 'on', 'off', 'yes', 'no']; + var keywordRegex = new RegExp("\\b(("+cons.join(")|(")+"))$", 'i'); + + return { + token: function(stream, state) { + var ch = stream.peek(); + var esc = state.escaped; + state.escaped = false; + /* comments */ + if (ch == "#" && (stream.pos == 0 || /\s/.test(stream.string.charAt(stream.pos - 1)))) { + stream.skipToEnd(); + return "comment"; + } + + if (stream.match(/^('([^']|\\.)*'?|"([^"]|\\.)*"?)/)) + return "string"; + + if (state.literal && stream.indentation() > state.keyCol) { + stream.skipToEnd(); return "string"; + } else if (state.literal) { state.literal = false; } + if (stream.sol()) { + state.keyCol = 0; + state.pair = false; + state.pairStart = false; + /* document start */ + if(stream.match(/---/)) { return "def"; } + /* document end */ + if (stream.match(/\.\.\./)) { return "def"; } + /* array list item */ + if (stream.match(/\s*-\s+/)) { return 'meta'; } + } + /* inline pairs/lists */ + if (stream.match(/^(\{|\}|\[|\])/)) { + if (ch == '{') + state.inlinePairs++; + else if (ch == '}') + state.inlinePairs--; + else if (ch == '[') + state.inlineList++; + else + state.inlineList--; + return 'meta'; + } + + /* list seperator */ + if (state.inlineList > 0 && !esc && ch == ',') { + stream.next(); + return 'meta'; + } + /* pairs seperator */ + if (state.inlinePairs > 0 && !esc && ch == ',') { + state.keyCol = 0; + state.pair = false; + state.pairStart = false; + stream.next(); + return 'meta'; + } + + /* start of value of a pair */ + if (state.pairStart) { + /* block literals */ + if (stream.match(/^\s*(\||\>)\s*/)) { state.literal = true; return 'meta'; }; + /* references */ + if (stream.match(/^\s*(\&|\*)[a-z0-9\._-]+\b/i)) { return 'variable-2'; } + /* numbers */ + if (state.inlinePairs == 0 && stream.match(/^\s*-?[0-9\.\,]+\s?$/)) { return 'number'; } + if (state.inlinePairs > 0 && stream.match(/^\s*-?[0-9\.\,]+\s?(?=(,|}))/)) { return 'number'; } + /* keywords */ + if (stream.match(keywordRegex)) { return 'keyword'; } + } + + /* pairs (associative arrays) -> key */ + if (!state.pair && stream.match(/^\s*(?:[,\[\]{}&*!|>'"%@`][^\s'":]|[^,\[\]{}#&*!|>'"%@`])[^#]*?(?=\s*:($|\s))/)) { + state.pair = true; + state.keyCol = stream.indentation(); + return "atom"; + } + if (state.pair && stream.match(/^:\s*/)) { state.pairStart = true; return 'meta'; } + + /* nothing found, continue */ + state.pairStart = false; + state.escaped = (ch == '\\'); + stream.next(); + return null; + }, + startState: function() { + return { + pair: false, + pairStart: false, + keyCol: 0, + inlinePairs: 0, + inlineList: 0, + literal: false, + escaped: false + }; + }, + lineComment: "#", + fold: "indent" + }; +}); + +CodeMirror.defineMIME("text/x-yaml", "yaml"); +CodeMirror.defineMIME("text/yaml", "yaml"); + +}); diff --git a/docs/js/node_modules/codemirror/mode/z80/z80.js b/docs/js/node_modules/codemirror/mode/z80/z80.js new file mode 100644 index 000000000..8cea4ff90 --- /dev/null +++ b/docs/js/node_modules/codemirror/mode/z80/z80.js @@ -0,0 +1,116 @@ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: https://codemirror.net/LICENSE + +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + +CodeMirror.defineMode('z80', function(_config, parserConfig) { + var ez80 = parserConfig.ez80; + var keywords1, keywords2; + if (ez80) { + keywords1 = /^(exx?|(ld|cp)([di]r?)?|[lp]ea|pop|push|ad[cd]|cpl|daa|dec|inc|neg|sbc|sub|and|bit|[cs]cf|x?or|res|set|r[lr]c?a?|r[lr]d|s[lr]a|srl|djnz|nop|[de]i|halt|im|in([di]mr?|ir?|irx|2r?)|ot(dmr?|[id]rx|imr?)|out(0?|[di]r?|[di]2r?)|tst(io)?|slp)(\.([sl]?i)?[sl])?\b/i; + keywords2 = /^(((call|j[pr]|rst|ret[in]?)(\.([sl]?i)?[sl])?)|(rs|st)mix)\b/i; + } else { + keywords1 = /^(exx?|(ld|cp|in)([di]r?)?|pop|push|ad[cd]|cpl|daa|dec|inc|neg|sbc|sub|and|bit|[cs]cf|x?or|res|set|r[lr]c?a?|r[lr]d|s[lr]a|srl|djnz|nop|rst|[de]i|halt|im|ot[di]r|out[di]?)\b/i; + keywords2 = /^(call|j[pr]|ret[in]?|b_?(call|jump))\b/i; + } + + var variables1 = /^(af?|bc?|c|de?|e|hl?|l|i[xy]?|r|sp)\b/i; + var variables2 = /^(n?[zc]|p[oe]?|m)\b/i; + var errors = /^([hl][xy]|i[xy][hl]|slia|sll)\b/i; + var numbers = /^([\da-f]+h|[0-7]+o|[01]+b|\d+d?)\b/i; + + return { + startState: function() { + return { + context: 0 + }; + }, + token: function(stream, state) { + if (!stream.column()) + state.context = 0; + + if (stream.eatSpace()) + return null; + + var w; + + if (stream.eatWhile(/\w/)) { + if (ez80 && stream.eat('.')) { + stream.eatWhile(/\w/); + } + w = stream.current(); + + if (stream.indentation()) { + if ((state.context == 1 || state.context == 4) && variables1.test(w)) { + state.context = 4; + return 'var2'; + } + + if (state.context == 2 && variables2.test(w)) { + state.context = 4; + return 'var3'; + } + + if (keywords1.test(w)) { + state.context = 1; + return 'keyword'; + } else if (keywords2.test(w)) { + state.context = 2; + return 'keyword'; + } else if (state.context == 4 && numbers.test(w)) { + return 'number'; + } + + if (errors.test(w)) + return 'error'; + } else if (stream.match(numbers)) { + return 'number'; + } else { + return null; + } + } else if (stream.eat(';')) { + stream.skipToEnd(); + return 'comment'; + } else if (stream.eat('"')) { + while (w = stream.next()) { + if (w == '"') + break; + + if (w == '\\') + stream.next(); + } + return 'string'; + } else if (stream.eat('\'')) { + if (stream.match(/\\?.'/)) + return 'number'; + } else if (stream.eat('.') || stream.sol() && stream.eat('#')) { + state.context = 5; + + if (stream.eatWhile(/\w/)) + return 'def'; + } else if (stream.eat('$')) { + if (stream.eatWhile(/[\da-f]/i)) + return 'number'; + } else if (stream.eat('%')) { + if (stream.eatWhile(/[01]/)) + return 'number'; + } else { + stream.next(); + } + return null; + } + }; +}); + +CodeMirror.defineMIME("text/x-z80", "z80"); +CodeMirror.defineMIME("text/x-ez80", { name: "z80", ez80: true }); + +}); diff --git a/docs/js/node_modules/codemirror/package.json b/docs/js/node_modules/codemirror/package.json new file mode 100644 index 000000000..0b32a20fc --- /dev/null +++ b/docs/js/node_modules/codemirror/package.json @@ -0,0 +1,2585 @@ +{ + "_from": "codemirror@^5.37.0", + "_id": "codemirror@5.49.2", + "_inBundle": false, + "_integrity": "sha512-dwJ2HRPHm8w51WB5YTF9J7m6Z5dtkqbU9ntMZ1dqXyFB9IpjoUFDj80ahRVEoVanfIp6pfASJbOlbWdEf8FOzQ==", + "_location": "/codemirror", + "_phantomChildren": {}, + "_requested": { + "type": "range", + "registry": true, + "raw": "codemirror@^5.37.0", + "name": "codemirror", + "escapedName": "codemirror", + "rawSpec": "^5.37.0", + "saveSpec": null, + "fetchSpec": "^5.37.0" + }, + "_requiredBy": [ + "/" + ], + "_resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.49.2.tgz", + "_shasum": "c84fdaf11b19803f828b0c67060c7bc6d154ccad", + "_spec": "codemirror@^5.37.0", + "_where": "/Users/hectorip/Education/Eloquent-JavaScript-ES", + "author": { + "name": "Marijn Haverbeke", + "email": "marijnh@gmail.com", + "url": "http://marijnhaverbeke.nl" + }, + "bugs": { + "url": "http://github.com/codemirror/CodeMirror/issues" + }, + "bundleDependencies": false, + "contributors": [ + { + "name": "List of CodeMirror contributors. Updated before every release." + }, + { + "name": "4oo4" + }, + { + "name": "4r2r" + }, + { + "name": "Aaron Brooks" + }, + { + "name": "Abdelouahab" + }, + { + "name": "Abdussalam Abdurrahman" + }, + { + "name": "Abe Fettig" + }, + { + "name": "Abhishek Gahlot" + }, + { + "name": "Adam Ahmed" + }, + { + "name": "Adam King" + }, + { + "name": "Adam Particka" + }, + { + "name": "adanlobato" + }, + { + "name": "Adán Lobato" + }, + { + "name": "Aditya Toshniwal" + }, + { + "name": "Adrian Aichner" + }, + { + "name": "Adrian Heine" + }, + { + "name": "Adrien Bertrand" + }, + { + "name": "aeroson" + }, + { + "name": "Ahmad Amireh" + }, + { + "name": "Ahmad M. Zawawi" + }, + { + "name": "ahoward" + }, + { + "name": "Akeksandr Motsjonov" + }, + { + "name": "Alasdair Smith" + }, + { + "name": "AlbertHilb" + }, + { + "name": "Alberto González Palomo" + }, + { + "name": "Alberto Pose" + }, + { + "name": "Albert Xing" + }, + { + "name": "Alexander Pavlov" + }, + { + "name": "Alexander Schepanovski" + }, + { + "name": "Alexander Shvets" + }, + { + "name": "Alexander Solovyov" + }, + { + "name": "Alexandre Bique" + }, + { + "name": "alexey-k" + }, + { + "name": "Alex Piggott" + }, + { + "name": "Aliaksei Chapyzhenka" + }, + { + "name": "Allen Sarkisyan" + }, + { + "name": "Ami Fischman" + }, + { + "name": "Amin Shali" + }, + { + "name": "Amin Ullah Khan" + }, + { + "name": "amshali@google.com" + }, + { + "name": "Amsul" + }, + { + "name": "amuntean" + }, + { + "name": "Amy" + }, + { + "name": "Ananya Sen" + }, + { + "name": "anaran" + }, + { + "name": "AndersMad" + }, + { + "name": "Anders Nawroth" + }, + { + "name": "Anderson Mesquita" + }, + { + "name": "Anders Wåglund" + }, + { + "name": "Andrea G" + }, + { + "name": "Andreas Reischuck" + }, + { + "name": "Andres Taylor" + }, + { + "name": "Andre von Houck" + }, + { + "name": "Andrew Cheng" + }, + { + "name": "Andrew Dassonville" + }, + { + "name": "Andrey Fedorov" + }, + { + "name": "Andrey Klyuchnikov" + }, + { + "name": "Andrey Lushnikov" + }, + { + "name": "Andrey Shchekin" + }, + { + "name": "Andy Joslin" + }, + { + "name": "Andy Kimball" + }, + { + "name": "Andy Li" + }, + { + "name": "Angelo" + }, + { + "name": "angelozerr" + }, + { + "name": "angelo.zerr@gmail.com" + }, + { + "name": "Ankit" + }, + { + "name": "Ankit Ahuja" + }, + { + "name": "Ansel Santosa" + }, + { + "name": "Anthony Dugois" + }, + { + "name": "anthonygego" + }, + { + "name": "Anthony Gégo" + }, + { + "name": "Anthony Grimes" + }, + { + "name": "Anton Kovalyov" + }, + { + "name": "Apollo Zhu" + }, + { + "name": "AQNOUCH Mohammed" + }, + { + "name": "Aram Shatakhtsyan" + }, + { + "name": "areos" + }, + { + "name": "Arnab Bose" + }, + { + "name": "Arnoud Buzing" + }, + { + "name": "Arsène von Wyss" + }, + { + "name": "Arthur Müller" + }, + { + "name": "Arun Narasani" + }, + { + "name": "as3boyan" + }, + { + "name": "asolove" + }, + { + "name": "atelierbram" + }, + { + "name": "AtomicPages LLC" + }, + { + "name": "Atul Bhouraskar" + }, + { + "name": "Aurelian Oancea" + }, + { + "name": "Axel Lewenhaupt" + }, + { + "name": "Baptiste Augrain" + }, + { + "name": "Barret Rennie" + }, + { + "name": "Bartosz Dziewoński" + }, + { + "name": "Basarat Ali Syed" + }, + { + "name": "Bastian Müller" + }, + { + "name": "belhaj" + }, + { + "name": "Bem Jones-Bey" + }, + { + "name": "benbro" + }, + { + "name": "Beni Cherniavsky-Paskin" + }, + { + "name": "Benjamin DeCoste" + }, + { + "name": "Benjamin Young" + }, + { + "name": "Ben Keen" + }, + { + "name": "Ben Miller" + }, + { + "name": "Ben Mosher" + }, + { + "name": "Bernhard Sirlinger" + }, + { + "name": "Bert Chang" + }, + { + "name": "Bharad" + }, + { + "name": "BigBlueHat" + }, + { + "name": "Billy Moon" + }, + { + "name": "binny" + }, + { + "name": "Bjorn Hansen" + }, + { + "name": "B Krishna Chaitanya" + }, + { + "name": "Blaine G" + }, + { + "name": "blukat29" + }, + { + "name": "Bo" + }, + { + "name": "boomyjee" + }, + { + "name": "Bo Peng" + }, + { + "name": "borawjm" + }, + { + "name": "Brad Metcalf" + }, + { + "name": "Brandon Frohs" + }, + { + "name": "Brandon Wamboldt" + }, + { + "name": "Bret Little" + }, + { + "name": "Brett Zamir" + }, + { + "name": "Brian Grinstead" + }, + { + "name": "Brian Sletten" + }, + { + "name": "brrd" + }, + { + "name": "Bruce Mitchener" + }, + { + "name": "Bruno Logerfo" + }, + { + "name": "Bryan Gin-ge Chen" + }, + { + "name": "Bryan Massoth" + }, + { + "name": "Caitlin Potter" + }, + { + "name": "Calin Barbat" + }, + { + "name": "callodacity" + }, + { + "name": "Camilo Roca" + }, + { + "name": "Casey Klebba" + }, + { + "name": "César González Íñiguez" + }, + { + "name": "Chad Jolly" + }, + { + "name": "Chandra Sekhar Pydi" + }, + { + "name": "Charles Skelton" + }, + { + "name": "Cheah Chu Yeow" + }, + { + "name": "Chhekur" + }, + { + "name": "Chris Colborne" + }, + { + "name": "Chris Coyier" + }, + { + "name": "Chris Ford" + }, + { + "name": "Chris Granger" + }, + { + "name": "Chris Houseknecht" + }, + { + "name": "Chris Lohfink" + }, + { + "name": "Chris Morgan" + }, + { + "name": "Chris Reeves" + }, + { + "name": "Chris Smith" + }, + { + "name": "Christian Gruen" + }, + { + "name": "Christian Oyarzun" + }, + { + "name": "Christian Petrov" + }, + { + "name": "christopherblaser" + }, + { + "name": "Christopher Brown" + }, + { + "name": "Christopher Kramer" + }, + { + "name": "Christopher Mitchell" + }, + { + "name": "Christopher Pfohl" + }, + { + "name": "Christopher Wallis" + }, + { + "name": "Chunliang Lyu" + }, + { + "name": "ciaranj" + }, + { + "name": "clso" + }, + { + "name": "CodeAnimal" + }, + { + "name": "CodeBitt" + }, + { + "name": "coderaiser" + }, + { + "name": "Cole R Lawrence" + }, + { + "name": "ComFreek" + }, + { + "name": "Cristian Prieto" + }, + { + "name": "Curran Kelleher" + }, + { + "name": "Curtis Gagliardi" + }, + { + "name": "dagsta" + }, + { + "name": "daines" + }, + { + "name": "Dale Jung" + }, + { + "name": "Dan Bentley" + }, + { + "name": "Dan Heberden" + }, + { + "name": "Daniel, Dao Quang Minh" + }, + { + "name": "Daniele Di Sarli" + }, + { + "name": "Daniel Faust" + }, + { + "name": "Daniel Hanggi" + }, + { + "name": "Daniel Huigens" + }, + { + "name": "Daniel Kesler" + }, + { + "name": "Daniel KJ" + }, + { + "name": "Daniel Neel" + }, + { + "name": "Daniel Parnell" + }, + { + "name": "Daniel Thwaites" + }, + { + "name": "Danila Malyutin" + }, + { + "name": "Danny Yoo" + }, + { + "name": "darealshinji" + }, + { + "name": "Darius Roberts" + }, + { + "name": "databricks-david-lewis" + }, + { + "name": "Dave Brondsema" + }, + { + "name": "Dave MacLachlan" + }, + { + "name": "Dave Myers" + }, + { + "name": "David Barnett" + }, + { + "name": "David H. Bronke" + }, + { + "name": "David Mignot" + }, + { + "name": "David Pathakjee" + }, + { + "name": "David Santana" + }, + { + "name": "David Vázquez" + }, + { + "name": "David Whittington" + }, + { + "name": "deebugger" + }, + { + "name": "Deep Thought" + }, + { + "name": "Denis Ovsienko" + }, + { + "name": "Devin Abbott" + }, + { + "name": "Devon Carew" + }, + { + "name": "Dick Choi" + }, + { + "name": "Diego Fernandez" + }, + { + "name": "dignifiedquire" + }, + { + "name": "Dimage Sapelkin" + }, + { + "name": "dmaclach" + }, + { + "name": "Dmitry Kiselyov" + }, + { + "name": "domagoj412" + }, + { + "name": "Dominator008" + }, + { + "name": "Domizio Demichelis" + }, + { + "name": "Doug Blank" + }, + { + "name": "Doug Wikle" + }, + { + "name": "Drew Bratcher" + }, + { + "name": "Drew Hintz" + }, + { + "name": "Drew Khoury" + }, + { + "name": "Drini Cami" + }, + { + "name": "Dror BG" + }, + { + "name": "Duncan Lilley" + }, + { + "name": "duralog" + }, + { + "name": "dwelle" + }, + { + "name": "eborden" + }, + { + "name": "edoroshenko" + }, + { + "name": "edsharp" + }, + { + "name": "ekhaled" + }, + { + "name": "Elisée" + }, + { + "name": "Emmanuel Schanzer" + }, + { + "name": "Enam Mijbah Noor" + }, + { + "name": "Eric Allam" + }, + { + "name": "Eric Bogard" + }, + { + "name": "Erik Demaine" + }, + { + "name": "Erik Welander" + }, + { + "name": "eustas" + }, + { + "name": "Evan Minsk" + }, + { + "name": "Fabien Dubosson" + }, + { + "name": "Fabien O'Carroll" + }, + { + "name": "Fabio Zendhi Nagao" + }, + { + "name": "Faiza Alsaied" + }, + { + "name": "Fauntleroy" + }, + { + "name": "fbuchinger" + }, + { + "name": "feizhang365" + }, + { + "name": "Felipe Lalanne" + }, + { + "name": "Felix Raab" + }, + { + "name": "ficristo" + }, + { + "name": "Filip Noetzel" + }, + { + "name": "Filip Stollár" + }, + { + "name": "Filype Pereira" + }, + { + "name": "finalfantasia" + }, + { + "name": "flack" + }, + { + "name": "Florian Felten" + }, + { + "name": "Forbes Lindesay" + }, + { + "name": "ForbesLindesay" + }, + { + "name": "Ford_Lawnmower" + }, + { + "name": "Forrest Oliphant" + }, + { + "name": "Franco Catena" + }, + { + "name": "Frank Seifferth" + }, + { + "name": "Frank Wiegand" + }, + { + "name": "fraxx001" + }, + { + "name": "Fredrik Borg" + }, + { + "name": "FUJI Goro", + "url": "gfx" + }, + { + "name": "Gabriel Gheorghian" + }, + { + "name": "Gabriel Horner" + }, + { + "name": "Gabriel Nahmias" + }, + { + "name": "galambalazs" + }, + { + "name": "Gary Sheng" + }, + { + "name": "Gautam Mehta" + }, + { + "name": "Gavin Douglas" + }, + { + "name": "gekkoe" + }, + { + "name": "Geordie Hall" + }, + { + "name": "George Stephanis" + }, + { + "name": "geowarin" + }, + { + "name": "Gerard Braad" + }, + { + "name": "Gergely Hegykozi" + }, + { + "name": "Germain Chazot" + }, + { + "name": "Giovanni Calò" + }, + { + "name": "Glebov Boris" + }, + { + "name": "Glenn Jorde" + }, + { + "name": "Glenn Ruehle" + }, + { + "name": "goldsmcb" + }, + { + "name": "Golevka" + }, + { + "name": "Google LLC" + }, + { + "name": "Gordon Smith" + }, + { + "name": "Grant Skinner" + }, + { + "name": "greengiant" + }, + { + "name": "Gregory Koberger" + }, + { + "name": "Grzegorz Mazur" + }, + { + "name": "Guang Li" + }, + { + "name": "Guan Gui" + }, + { + "name": "Guillaume Massé" + }, + { + "name": "Guillaume Massé" + }, + { + "name": "guraga" + }, + { + "name": "Gustavo Rodrigues" + }, + { + "name": "Hakan Tunc" + }, + { + "name": "Hans Engel" + }, + { + "name": "Hanzhao Deng" + }, + { + "name": "Harald Schilly" + }, + { + "name": "Hardest" + }, + { + "name": "Harshvardhan Gupta" + }, + { + "name": "Hasan Karahan" + }, + { + "name": "Heanes" + }, + { + "name": "Hector Oswaldo Caballero" + }, + { + "name": "Hélio" + }, + { + "name": "Hendrik Wallbaum" + }, + { + "name": "Henrik Haugbølle" + }, + { + "name": "Herculano Campos" + }, + { + "name": "hidaiy" + }, + { + "name": "Hiroyuki Makino" + }, + { + "name": "hitsthings" + }, + { + "name": "Hocdoc" + }, + { + "name": "Hugues Malphettes" + }, + { + "name": "Ian Beck" + }, + { + "name": "Ian Davies" + }, + { + "name": "Ian Dickinson" + }, + { + "name": "Ian Rose" + }, + { + "name": "Ian Wehrman" + }, + { + "name": "Ian Wetherbee" + }, + { + "name": "Ice White" + }, + { + "name": "ICHIKAWA, Yuji" + }, + { + "name": "idleberg" + }, + { + "name": "ilvalle" + }, + { + "name": "Ilya Kharlamov" + }, + { + "name": "Ingo Richter" + }, + { + "name": "Irakli Gozalishvili" + }, + { + "name": "Ivan Kurnosov" + }, + { + "name": "Ivoah" + }, + { + "name": "Jacob Lee" + }, + { + "name": "Jaimin" + }, + { + "name": "Jake Peyser" + }, + { + "name": "Jakob Miland" + }, + { + "name": "Jakub Vrana" + }, + { + "name": "Jakub Vrána" + }, + { + "name": "James Campos" + }, + { + "name": "James Howard" + }, + { + "name": "James Thorne" + }, + { + "name": "Jamie Hill" + }, + { + "name": "Jamie Morris" + }, + { + "name": "Janice Leung" + }, + { + "name": "Jan Jongboom" + }, + { + "name": "jankeromnes" + }, + { + "name": "Jan Keromnes" + }, + { + "name": "Jan Odvarko" + }, + { + "name": "Jan Schär" + }, + { + "name": "Jan T. Sott" + }, + { + "name": "Jared Dean" + }, + { + "name": "Jared Forsyth" + }, + { + "name": "Jared Jacobs" + }, + { + "name": "Jason" + }, + { + "name": "Jason Barnabe" + }, + { + "name": "Jason Grout" + }, + { + "name": "Jason Heeris" + }, + { + "name": "Jason Johnston" + }, + { + "name": "Jason San Jose" + }, + { + "name": "Jason Siefken" + }, + { + "name": "Jayaprabhakar" + }, + { + "name": "Jaydeep Solanki" + }, + { + "name": "Jean Boussier" + }, + { + "name": "Jeff Blaisdell" + }, + { + "name": "Jeff Hanke" + }, + { + "name": "Jeff Jenkins" + }, + { + "name": "jeffkenton" + }, + { + "name": "Jeff Pickhardt" + }, + { + "name": "jem", + "url": "graphite" + }, + { + "name": "Jeremy Parmenter" + }, + { + "name": "Jim" + }, + { + "name": "Jim Avery" + }, + { + "name": "jkaplon" + }, + { + "name": "JobJob" + }, + { + "name": "jochenberger" + }, + { + "name": "Jochen Berger" + }, + { + "name": "Joel Einbinder" + }, + { + "name": "joelpinheiro" + }, + { + "name": "joewalsh" + }, + { + "name": "Johan Ask" + }, + { + "name": "John Connor" + }, + { + "name": "John-David Dalton" + }, + { + "name": "John Engler" + }, + { + "name": "John Lees-Miller" + }, + { + "name": "John Ryan" + }, + { + "name": "John Snelson" + }, + { + "name": "John Van Der Loo" + }, + { + "name": "Jon Ander Peñalba" + }, + { + "name": "Jonas Döbertin" + }, + { + "name": "Jonas Helfer" + }, + { + "name": "Jonathan Dierksen" + }, + { + "name": "Jonathan Hart" + }, + { + "name": "Jonathan Malmaud" + }, + { + "name": "Jon Gacnik" + }, + { + "name": "jongalloway" + }, + { + "name": "Jon Malmaud" + }, + { + "name": "Jon Sangster" + }, + { + "name": "Joo" + }, + { + "name": "Joost-Wim Boekesteijn" + }, + { + "name": "Joseph Pecoraro" + }, + { + "name": "Josh Barnes" + }, + { + "name": "Josh Cohen" + }, + { + "name": "Josh Soref" + }, + { + "name": "Joshua Newman" + }, + { + "name": "Josh Watzman" + }, + { + "name": "jots" + }, + { + "name": "Joy Zhong" + }, + { + "name": "jsoojeon" + }, + { + "name": "ju1ius" + }, + { + "name": "Juan Benavides Romero" + }, + { + "name": "Jucovschi Constantin" + }, + { + "name": "Juho Vuori" + }, + { + "name": "Julien CROUZET" + }, + { + "name": "Julien Rebetez" + }, + { + "name": "Justin Andresen" + }, + { + "name": "Justin Hileman" + }, + { + "name": "jwallers@gmail.com" + }, + { + "name": "kaniga" + }, + { + "name": "karevn" + }, + { + "name": "Karol" + }, + { + "name": "Kayur Patel" + }, + { + "name": "Kazuhito Hokamura" + }, + { + "name": "kcwiakala" + }, + { + "name": "Kees de Kooter" + }, + { + "name": "Kenan Christian Dimas" + }, + { + "name": "Ken Newman" + }, + { + "name": "ken restivo" + }, + { + "name": "Ken Rockot" + }, + { + "name": "Kevin Earls" + }, + { + "name": "Kevin Kwok" + }, + { + "name": "Kevin Muret" + }, + { + "name": "Kevin Sawicki" + }, + { + "name": "Kevin Ushey" + }, + { + "name": "Kier Darby" + }, + { + "name": "Klaus Silveira" + }, + { + "name": "Koh Zi Han, Cliff" + }, + { + "name": "komakino" + }, + { + "name": "Konstantin Lopuhin" + }, + { + "name": "koops" + }, + { + "name": "Kris Ciccarello" + }, + { + "name": "ks-ifware" + }, + { + "name": "kubelsmieci" + }, + { + "name": "KwanEsq" + }, + { + "name": "Kyle Kelley" + }, + { + "name": "KyleMcNutt" + }, + { + "name": "LaKing" + }, + { + "name": "Lanfei" + }, + { + "name": "Lanny" + }, + { + "name": "laobubu" + }, + { + "name": "Laszlo Vidacs" + }, + { + "name": "leaf corcoran" + }, + { + "name": "Lemmon" + }, + { + "name": "Leo Baschy" + }, + { + "name": "Leonid Khachaturov" + }, + { + "name": "Leon Sorokin" + }, + { + "name": "Leonya Khachaturov" + }, + { + "name": "Liam Newman" + }, + { + "name": "Libo Cannici" + }, + { + "name": "Lior Goldberg" + }, + { + "name": "Lior Shub" + }, + { + "name": "LloydMilligan" + }, + { + "name": "LM" + }, + { + "name": "lochel" + }, + { + "name": "Lonnie Abelbeck" + }, + { + "name": "Lorenzo Simionato" + }, + { + "name": "Lorenzo Stoakes" + }, + { + "name": "Louis Mauchet" + }, + { + "name": "Luca Fabbri" + }, + { + "name": "Luciano Longo" + }, + { + "name": "Luciano Santana" + }, + { + "name": "Lu Fangjian" + }, + { + "name": "Luke Browning" + }, + { + "name": "Luke Granger-Brown" + }, + { + "name": "Luke Stagner" + }, + { + "name": "lynschinzer" + }, + { + "name": "M1cha" + }, + { + "name": "Madhura Jayaratne" + }, + { + "name": "Maksim Lin" + }, + { + "name": "Maksym Taran" + }, + { + "name": "Malay Majithia" + }, + { + "name": "Manideep" + }, + { + "name": "Manuel Rego Casasnovas" + }, + { + "name": "Marat Dreizin" + }, + { + "name": "Marcel Gerber" + }, + { + "name": "Marcelo Camargo" + }, + { + "name": "Marco Aurélio" + }, + { + "name": "Marco Munizaga" + }, + { + "name": "Marcus Bointon" + }, + { + "name": "Marek Rudnicki" + }, + { + "name": "Marijn Haverbeke" + }, + { + "name": "Mário Gonçalves" + }, + { + "name": "Mario Pietsch" + }, + { + "name": "Mark Anderson" + }, + { + "name": "Mark Dalgleish" + }, + { + "name": "Mark Hamstra" + }, + { + "name": "Mark Lentczner" + }, + { + "name": "Marko Bonaci" + }, + { + "name": "Mark Peace" + }, + { + "name": "Markus Bordihn" + }, + { + "name": "Markus Olsson" + }, + { + "name": "Martin Balek" + }, + { + "name": "Martín Gaitán" + }, + { + "name": "Martin Hasoň" + }, + { + "name": "Martin Hunt" + }, + { + "name": "Martin Laine" + }, + { + "name": "Martin Zagora" + }, + { + "name": "Mason Malone" + }, + { + "name": "Mateusz Paprocki" + }, + { + "name": "Mathias Bynens" + }, + { + "name": "mats cronqvist" + }, + { + "name": "Matt Gaide" + }, + { + "name": "Matthew Bauer" + }, + { + "name": "Matthew Beale" + }, + { + "name": "matthewhayes" + }, + { + "name": "Matthew Rathbone" + }, + { + "name": "Matthew Suozzo" + }, + { + "name": "Matthias Bussonnier" + }, + { + "name": "Matthias BUSSONNIER" + }, + { + "name": "Mattia Astorino" + }, + { + "name": "Matt MacPherson" + }, + { + "name": "Matt McDonald" + }, + { + "name": "Matt Pass" + }, + { + "name": "Matt Sacks" + }, + { + "name": "mauricio" + }, + { + "name": "Maximilian Hils" + }, + { + "name": "Maxim Kraev" + }, + { + "name": "Max Kirsch" + }, + { + "name": "Max Schaefer" + }, + { + "name": "Max Wu" + }, + { + "name": "Max Xiantu" + }, + { + "name": "mbarkhau" + }, + { + "name": "McBrainy" + }, + { + "name": "mce2" + }, + { + "name": "melpon" + }, + { + "name": "meshuamam" + }, + { + "name": "Metatheos" + }, + { + "name": "Micah Dubinko" + }, + { + "name": "Michael" + }, + { + "name": "Michael Goderbauer" + }, + { + "name": "Michael Grey" + }, + { + "name": "Michael Kaminsky" + }, + { + "name": "Michael Lehenbauer" + }, + { + "name": "Michael Wadman" + }, + { + "name": "Michael Walker" + }, + { + "name": "Michael Zhou" + }, + { + "name": "Michal Čihař" + }, + { + "name": "Michal Dorner" + }, + { + "name": "Michal Kapiczynski" + }, + { + "name": "Mighty Guava" + }, + { + "name": "Miguel Castillo" + }, + { + "name": "mihailik" + }, + { + "name": "Mika Andrianarijaona" + }, + { + "name": "Mike" + }, + { + "name": "Mike Bostock" + }, + { + "name": "Mike Brevoort" + }, + { + "name": "Mike Diaz" + }, + { + "name": "Mike Ivanov" + }, + { + "name": "Mike Kadin" + }, + { + "name": "Mike Kobit" + }, + { + "name": "Milan Szekely" + }, + { + "name": "MinRK" + }, + { + "name": "Miraculix87" + }, + { + "name": "misfo" + }, + { + "name": "mkaminsky11" + }, + { + "name": "mloginov" + }, + { + "name": "Moritz Schubotz", + "url": "physikerwelt" + }, + { + "name": "Moritz Schwörer" + }, + { + "name": "Moshe Wajnberg" + }, + { + "name": "mps" + }, + { + "name": "ms" + }, + { + "name": "mtaran-google" + }, + { + "name": "Mu-An ✌️ Chiou" + }, + { + "name": "Mu-An Chiou" + }, + { + "name": "mzabuawala" + }, + { + "name": "Narciso Jaramillo" + }, + { + "name": "Nathan Williams" + }, + { + "name": "ndr" + }, + { + "name": "Neil Anderson" + }, + { + "name": "neon-dev" + }, + { + "name": "nerbert" + }, + { + "name": "NetworkNode" + }, + { + "name": "nextrevision" + }, + { + "name": "ngn" + }, + { + "name": "nguillaumin" + }, + { + "name": "Ng Zhi An" + }, + { + "name": "Nicholas Bollweg" + }, + { + "name": "Nicholas Bollweg", + "url": "Nick" + }, + { + "name": "NickKolok" + }, + { + "name": "Nick Kreeger" + }, + { + "name": "Nick Small" + }, + { + "name": "Nicolas Chevobbe" + }, + { + "name": "Nicolas Kick" + }, + { + "name": "Nicolò Ribaudo" + }, + { + "name": "Niels van Groningen" + }, + { + "name": "nightwing" + }, + { + "name": "Nikita Beloglazov" + }, + { + "name": "Nikita Vasilyev" + }, + { + "name": "Nikolaj Kappler" + }, + { + "name": "Nikolay Kostov" + }, + { + "name": "nilp0inter" + }, + { + "name": "Nils Knappmeier" + }, + { + "name": "Nisarg Jhaveri" + }, + { + "name": "nlwillia" + }, + { + "name": "noragrossman" + }, + { + "name": "Norman Rzepka" + }, + { + "name": "Nouzbe" + }, + { + "name": "Oleksandr Yakovenko" + }, + { + "name": "opl-" + }, + { + "name": "Oreoluwa Onatemowo" + }, + { + "name": "oscar.lofwenhamn" + }, + { + "name": "Oskar Segersvärd" + }, + { + "name": "overdodactyl" + }, + { + "name": "pablo" + }, + { + "name": "pabloferz" + }, + { + "name": "Pablo Zubieta" + }, + { + "name": "paddya" + }, + { + "name": "Page" + }, + { + "name": "paladox" + }, + { + "name": "Panupong Pasupat" + }, + { + "name": "paris" + }, + { + "name": "Paris" + }, + { + "name": "Paris Kasidiaris" + }, + { + "name": "Patil Arpith" + }, + { + "name": "Patrick Stoica" + }, + { + "name": "Patrick Strawderman" + }, + { + "name": "Paul Garvin" + }, + { + "name": "Paul Ivanov" + }, + { + "name": "Paul Masson" + }, + { + "name": "Pavel" + }, + { + "name": "Pavel Feldman" + }, + { + "name": "Pavel Petržela" + }, + { + "name": "Pavel Strashkin" + }, + { + "name": "Paweł Bartkiewicz" + }, + { + "name": "peteguhl" + }, + { + "name": "peter" + }, + { + "name": "Peter Flynn" + }, + { + "name": "peterkroon" + }, + { + "name": "Peter Kroon" + }, + { + "name": "Philipp A" + }, + { + "name": "Philipp Markovics" + }, + { + "name": "Philip Stadermann" + }, + { + "name": "Pi Delport" + }, + { + "name": "Pierre Gerold" + }, + { + "name": "Pieter Ouwerkerk" + }, + { + "name": "Pontus Melke" + }, + { + "name": "prasanthj" + }, + { + "name": "Prasanth J" + }, + { + "name": "Prayag Verma" + }, + { + "name": "prendota" + }, + { + "name": "Prendota" + }, + { + "name": "Qiang Li" + }, + { + "name": "Radek Piórkowski" + }, + { + "name": "Rahul" + }, + { + "name": "Rahul Anand" + }, + { + "name": "ramwin1" + }, + { + "name": "Randall Mason" + }, + { + "name": "Randy Burden" + }, + { + "name": "Randy Edmunds" + }, + { + "name": "Randy Luecke" + }, + { + "name": "Raphael Amorim" + }, + { + "name": "Rasmus Erik Voel Jensen" + }, + { + "name": "Rasmus Schultz" + }, + { + "name": "raymondf" + }, + { + "name": "Raymond Hill" + }, + { + "name": "ray ratchup" + }, + { + "name": "Ray Ratchup" + }, + { + "name": "Remi Nyborg" + }, + { + "name": "Renaud Durlin" + }, + { + "name": "Reynold Xin" + }, + { + "name": "Richard Denton" + }, + { + "name": "Richard van der Meer" + }, + { + "name": "Richard Z.H. Wang" + }, + { + "name": "Rishi Goomar" + }, + { + "name": "Robert Brignull" + }, + { + "name": "Robert Crossfield" + }, + { + "name": "Robert Martin" + }, + { + "name": "Roberto Abdelkader Martínez Pérez" + }, + { + "name": "robertop23" + }, + { + "name": "Robert Plummer" + }, + { + "name": "Rrandom" + }, + { + "name": "Rrrandom" + }, + { + "name": "Ruslan Osmanov" + }, + { + "name": "Ryan Pangrle" + }, + { + "name": "Ryan Petrello" + }, + { + "name": "Ryan Prior" + }, + { + "name": "ryu-sato" + }, + { + "name": "sabaca" + }, + { + "name": "Sam Lee" + }, + { + "name": "Sam Rawlins" + }, + { + "name": "Samuel Ainsworth" + }, + { + "name": "Sam Wilson" + }, + { + "name": "sandeepshetty" + }, + { + "name": "Sander AKA Redsandro" + }, + { + "name": "Sander Verweij" + }, + { + "name": "santec" + }, + { + "name": "Sarah McAlear and Wenlin Zhang" + }, + { + "name": "Sascha Peilicke" + }, + { + "name": "Sasha Varlamov" + }, + { + "name": "satamas" + }, + { + "name": "satchmorun" + }, + { + "name": "sathyamoorthi" + }, + { + "name": "Saul Costa" + }, + { + "name": "S. Chris Colbert" + }, + { + "name": "SCLINIC\\jdecker" + }, + { + "name": "Scott Aikin" + }, + { + "name": "Scott Feeney" + }, + { + "name": "Scott Goodhew" + }, + { + "name": "Seb35" + }, + { + "name": "Sebastian Wilzbach" + }, + { + "name": "Sebastian Zaha" + }, + { + "name": "Seren D" + }, + { + "name": "Sergey Goder" + }, + { + "name": "Sergey Tselovalnikov" + }, + { + "name": "Se-Won Kim" + }, + { + "name": "Shane Liesegang" + }, + { + "name": "shaund" + }, + { + "name": "shaun gilchrist" + }, + { + "name": "Shawn A" + }, + { + "name": "Shea Bunge" + }, + { + "name": "sheopory" + }, + { + "name": "Shil S" + }, + { + "name": "Shiv Deepak" + }, + { + "name": "Shmuel Englard" + }, + { + "name": "Shubham Jain" + }, + { + "name": "Siamak Mokhtari" + }, + { + "name": "silverwind" + }, + { + "name": "Simon Edwards" + }, + { + "name": "sinkuu" + }, + { + "name": "snasa" + }, + { + "name": "soliton4" + }, + { + "name": "sonson" + }, + { + "name": "Sorab Bisht" + }, + { + "name": "spastorelli" + }, + { + "name": "srajanpaliwal" + }, + { + "name": "Stanislav Oaserele" + }, + { + "name": "stan-z" + }, + { + "name": "Stas Kobzar" + }, + { + "name": "Stefan Borsje" + }, + { + "name": "Steffen Beyer" + }, + { + "name": "Steffen Bruchmann" + }, + { + "name": "Steffen Kowalski" + }, + { + "name": "Stephane Moore" + }, + { + "name": "Stephen Lavelle" + }, + { + "name": "Steve Champagne" + }, + { + "name": "Steve Hoover" + }, + { + "name": "Steve O'Hara" + }, + { + "name": "stockiNail" + }, + { + "name": "stoskov" + }, + { + "name": "Stryder Crown" + }, + { + "name": "Stu Kennedy" + }, + { + "name": "Sungho Kim" + }, + { + "name": "sverweij" + }, + { + "name": "Taha Jahangir" + }, + { + "name": "takamori" + }, + { + "name": "Tako Schotanus" + }, + { + "name": "Takuji Shimokawa" + }, + { + "name": "Takuya Matsuyama" + }, + { + "name": "Tarmil" + }, + { + "name": "TDaglis" + }, + { + "name": "tel" + }, + { + "name": "Tentone" + }, + { + "name": "tfjgeorge" + }, + { + "name": "Thaddee Tyl" + }, + { + "name": "thanasis" + }, + { + "name": "TheHowl" + }, + { + "name": "themrmax" + }, + { + "name": "think" + }, + { + "name": "Thomas Brouard" + }, + { + "name": "Thomas Dvornik" + }, + { + "name": "Thomas Kluyver" + }, + { + "name": "thomasmaclean" + }, + { + "name": "Thomas Schmid" + }, + { + "name": "Tim Alby" + }, + { + "name": "Tim Baumann" + }, + { + "name": "Timothy Farrell" + }, + { + "name": "Timothy Gu" + }, + { + "name": "Timothy Hatcher" + }, + { + "name": "Tobias Bertelsen" + }, + { + "name": "TobiasBg" + }, + { + "name": "Todd Berman" + }, + { + "name": "Todd Kennedy" + }, + { + "name": "Tomas-A" + }, + { + "name": "Tomas Varaneckas" + }, + { + "name": "Tom Erik Støwer" + }, + { + "name": "Tom Klancer" + }, + { + "name": "Tom MacWright" + }, + { + "name": "Tom McLaughlin" + }, + { + "name": "Tony Jian" + }, + { + "name": "tophf" + }, + { + "name": "Torgeir Thoresen" + }, + { + "name": "totalamd" + }, + { + "name": "Travis Heppe" + }, + { + "name": "Triangle717" + }, + { + "name": "Tristan Tarrant" + }, + { + "name": "TSUYUSATO Kitsune" + }, + { + "name": "Tugrul Elmas" + }, + { + "name": "twifkak" + }, + { + "name": "Tyler Long" + }, + { + "name": "Tyler Makaro" + }, + { + "name": "Vadim Dyachenko" + }, + { + "name": "Vadzim Ramanenka" + }, + { + "name": "Vaibhav Sagar" + }, + { + "name": "VapidWorx" + }, + { + "name": "Vestimir Markov" + }, + { + "name": "vf" + }, + { + "name": "Victor Bocharsky" + }, + { + "name": "Vincent Woo" + }, + { + "name": "Volker Mische" + }, + { + "name": "vtripolitakis" + }, + { + "name": "wdouglashall" + }, + { + "name": "Weiyan Shao" + }, + { + "name": "wenli" + }, + { + "name": "Wes Cossick" + }, + { + "name": "Wesley Wiser" + }, + { + "name": "Weston Ruter" + }, + { + "name": "Will Binns-Smith" + }, + { + "name": "Will Dean" + }, + { + "name": "William Desportes" + }, + { + "name": "William Jamieson" + }, + { + "name": "William Stein" + }, + { + "name": "Willy" + }, + { + "name": "Wojtek Ptak" + }, + { + "name": "wonderboyjon" + }, + { + "name": "Wu Cheng-Han" + }, + { + "name": "Xavier Mendez" + }, + { + "name": "Yang Guo" + }, + { + "name": "Yassin N. Hassan" + }, + { + "name": "YNH Webdev" + }, + { + "name": "yoongu" + }, + { + "name": "Yunchi Luo" + }, + { + "name": "Yuvi Panda" + }, + { + "name": "Yvonnick Esnault" + }, + { + "name": "Zac Anger" + }, + { + "name": "Zachary Dremann" + }, + { + "name": "Zeno Rocha" + }, + { + "name": "Zhang Hao" + }, + { + "name": "Ziv" + }, + { + "name": "zoobestik" + }, + { + "name": "zziuni" + }, + { + "name": "魏鹏刚" + } + ], + "deprecated": false, + "description": "Full-featured in-browser code editor", + "devDependencies": { + "blint": "^1", + "node-static": "0.7.11", + "phantomjs-prebuilt": "^2.1.12", + "rollup": "^0.66.2", + "rollup-plugin-buble": "^0.19.2", + "rollup-watch": "^4.3.1" + }, + "directories": { + "lib": "./lib" + }, + "homepage": "https://codemirror.net", + "jspm": { + "directories": {}, + "dependencies": {}, + "devDependencies": {} + }, + "keywords": [ + "JavaScript", + "CodeMirror", + "Editor" + ], + "license": "MIT", + "main": "lib/codemirror.js", + "name": "codemirror", + "repository": { + "type": "git", + "url": "git+https://github.com/codemirror/CodeMirror.git" + }, + "scripts": { + "build": "rollup -c", + "lint": "bin/lint", + "prepare": "npm run-script build", + "test": "node ./test/run.js", + "watch": "rollup -w -c" + }, + "style": "lib/codemirror.css", + "version": "5.49.2" +} diff --git a/docs/js/node_modules/codemirror/rollup.config.js b/docs/js/node_modules/codemirror/rollup.config.js new file mode 100644 index 000000000..fbb435717 --- /dev/null +++ b/docs/js/node_modules/codemirror/rollup.config.js @@ -0,0 +1,20 @@ +import buble from 'rollup-plugin-buble'; + +export default { + input: "src/codemirror.js", + output: { + banner: `// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: https://codemirror.net/LICENSE + +// This is CodeMirror (https://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 . +`, + format: "umd", + file: "lib/codemirror.js", + name: "CodeMirror" + }, + plugins: [ buble({namedFunctionExpressions: false}) ] +}; diff --git a/docs/js/node_modules/codemirror/src/codemirror.js b/docs/js/node_modules/codemirror/src/codemirror.js new file mode 100644 index 000000000..2a2f54e4c --- /dev/null +++ b/docs/js/node_modules/codemirror/src/codemirror.js @@ -0,0 +1,3 @@ +import { CodeMirror } from "./edit/main.js" + +export default CodeMirror diff --git a/docs/js/node_modules/codemirror/src/display/Display.js b/docs/js/node_modules/codemirror/src/display/Display.js new file mode 100644 index 000000000..d57f00bdd --- /dev/null +++ b/docs/js/node_modules/codemirror/src/display/Display.js @@ -0,0 +1,110 @@ +import { gecko, ie, ie_version, mobile, webkit } from "../util/browser.js" +import { elt, eltP } from "../util/dom.js" +import { scrollerGap } from "../util/misc.js" +import { getGutters, renderGutters } from "./gutters.js" + +// The display handles the DOM integration, both for input reading +// and content drawing. It holds references to DOM nodes and +// display-related state. + +export function Display(place, doc, input, options) { + let 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 = eltP("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 = eltP("div", [d.measure, d.lineMeasure, d.selectionDiv, d.cursorDiv, d.lineDiv], + null, "position: relative; outline: none") + let lines = eltP("div", [d.lineSpace], "CodeMirror-lines") + // Moved around its parent to cover visible view. + d.mover = elt("div", [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 + + d.gutterSpecs = getGutters(options.gutters, options.lineNumbers) + renderGutters(d) + + input.init(d) +} diff --git a/docs/js/node_modules/codemirror/src/display/focus.js b/docs/js/node_modules/codemirror/src/display/focus.js new file mode 100644 index 000000000..aa731b435 --- /dev/null +++ b/docs/js/node_modules/codemirror/src/display/focus.js @@ -0,0 +1,47 @@ +import { restartBlink } from "./selection.js" +import { webkit } from "../util/browser.js" +import { addClass, rmClass } from "../util/dom.js" +import { signal } from "../util/event.js" + +export function ensureFocus(cm) { + if (!cm.state.focused) { cm.display.input.focus(); onFocus(cm) } +} + +export function delayBlurEvent(cm) { + cm.state.delayingBlurEvent = true + setTimeout(() => { if (cm.state.delayingBlurEvent) { + cm.state.delayingBlurEvent = false + onBlur(cm) + } }, 100) +} + +export function onFocus(cm, e) { + if (cm.state.delayingBlurEvent) cm.state.delayingBlurEvent = false + + if (cm.options.readOnly == "nocursor") return + if (!cm.state.focused) { + signal(cm, "focus", cm, e) + cm.state.focused = true + addClass(cm.display.wrapper, "CodeMirror-focused") + // This test prevents this from firing when a context + // menu is closed (since the input reset would kill the + // select-all detection hack) + if (!cm.curOp && cm.display.selForContextMenu != cm.doc.sel) { + cm.display.input.reset() + if (webkit) setTimeout(() => cm.display.input.reset(true), 20) // Issue #1730 + } + cm.display.input.receivedFocus() + } + restartBlink(cm) +} +export function onBlur(cm, e) { + if (cm.state.delayingBlurEvent) return + + if (cm.state.focused) { + signal(cm, "blur", cm, e) + cm.state.focused = false + rmClass(cm.display.wrapper, "CodeMirror-focused") + } + clearInterval(cm.display.blinker) + setTimeout(() => { if (!cm.state.focused) cm.display.shift = false }, 150) +} diff --git a/docs/js/node_modules/codemirror/src/display/gutters.js b/docs/js/node_modules/codemirror/src/display/gutters.js new file mode 100644 index 000000000..b27b6ce77 --- /dev/null +++ b/docs/js/node_modules/codemirror/src/display/gutters.js @@ -0,0 +1,44 @@ +import { elt, removeChildren } from "../util/dom.js" +import { regChange } from "./view_tracking.js" +import { alignHorizontally } from "./line_numbers.js" +import { updateGutterSpace } from "./update_display.js" + +export function getGutters(gutters, lineNumbers) { + let result = [], sawLineNumbers = false + for (let i = 0; i < gutters.length; i++) { + let name = gutters[i], style = null + if (typeof name != "string") { style = name.style; name = name.className } + if (name == "CodeMirror-linenumbers") { + if (!lineNumbers) continue + else sawLineNumbers = true + } + result.push({className: name, style}) + } + if (lineNumbers && !sawLineNumbers) result.push({className: "CodeMirror-linenumbers", style: null}) + return result +} + +// Rebuild the gutter elements, ensure the margin to the left of the +// code matches their width. +export function renderGutters(display) { + let gutters = display.gutters, specs = display.gutterSpecs + removeChildren(gutters) + display.lineGutter = null + for (let i = 0; i < specs.length; ++i) { + let {className, style} = specs[i] + let gElt = gutters.appendChild(elt("div", null, "CodeMirror-gutter " + className)) + if (style) gElt.style.cssText = style + if (className == "CodeMirror-linenumbers") { + display.lineGutter = gElt + gElt.style.width = (display.lineNumWidth || 1) + "px" + } + } + gutters.style.display = specs.length ? "" : "none" + updateGutterSpace(display) +} + +export function updateGutters(cm) { + renderGutters(cm.display) + regChange(cm) + alignHorizontally(cm) +} diff --git a/docs/js/node_modules/codemirror/src/display/highlight_worker.js b/docs/js/node_modules/codemirror/src/display/highlight_worker.js new file mode 100644 index 000000000..606981571 --- /dev/null +++ b/docs/js/node_modules/codemirror/src/display/highlight_worker.js @@ -0,0 +1,55 @@ +import { getContextBefore, highlightLine, processLine } from "../line/highlight.js" +import { copyState } from "../modes.js" +import { bind } from "../util/misc.js" + +import { runInOp } from "./operations.js" +import { regLineChange } from "./view_tracking.js" + +// HIGHLIGHT WORKER + +export function startWorker(cm, time) { + if (cm.doc.highlightFrontier < cm.display.viewTo) + cm.state.highlight.set(time, bind(highlightWorker, cm)) +} + +function highlightWorker(cm) { + let doc = cm.doc + if (doc.highlightFrontier >= cm.display.viewTo) return + let end = +new Date + cm.options.workTime + let context = getContextBefore(cm, doc.highlightFrontier) + let changedLines = [] + + doc.iter(context.line, Math.min(doc.first + doc.size, cm.display.viewTo + 500), line => { + if (context.line >= cm.display.viewFrom) { // Visible + let oldStyles = line.styles + let resetState = line.text.length > cm.options.maxHighlightLength ? copyState(doc.mode, context.state) : null + let highlighted = highlightLine(cm, line, context, true) + if (resetState) context.state = resetState + line.styles = highlighted.styles + let oldCls = line.styleClasses, newCls = highlighted.classes + if (newCls) line.styleClasses = newCls + else if (oldCls) line.styleClasses = null + let ischange = !oldStyles || oldStyles.length != line.styles.length || + oldCls != newCls && (!oldCls || !newCls || oldCls.bgClass != newCls.bgClass || oldCls.textClass != newCls.textClass) + for (let i = 0; !ischange && i < oldStyles.length; ++i) ischange = oldStyles[i] != line.styles[i] + if (ischange) changedLines.push(context.line) + line.stateAfter = context.save() + context.nextLine() + } else { + if (line.text.length <= cm.options.maxHighlightLength) + processLine(cm, line.text, context) + line.stateAfter = context.line % 5 == 0 ? context.save() : null + context.nextLine() + } + if (+new Date > end) { + startWorker(cm, cm.options.workDelay) + return true + } + }) + doc.highlightFrontier = context.line + doc.modeFrontier = Math.max(doc.modeFrontier, context.line) + if (changedLines.length) runInOp(cm, () => { + for (let i = 0; i < changedLines.length; i++) + regLineChange(cm, changedLines[i], "text") + }) +} diff --git a/docs/js/node_modules/codemirror/src/display/line_numbers.js b/docs/js/node_modules/codemirror/src/display/line_numbers.js new file mode 100644 index 000000000..073cbade0 --- /dev/null +++ b/docs/js/node_modules/codemirror/src/display/line_numbers.js @@ -0,0 +1,48 @@ +import { lineNumberFor } from "../line/utils_line.js" +import { compensateForHScroll } from "../measurement/position_measurement.js" +import { elt } from "../util/dom.js" + +import { updateGutterSpace } from "./update_display.js" + +// Re-align line numbers and gutter marks to compensate for +// horizontal scrolling. +export function alignHorizontally(cm) { + let display = cm.display, view = display.view + if (!display.alignWidgets && (!display.gutters.firstChild || !cm.options.fixedGutter)) return + let comp = compensateForHScroll(display) - display.scroller.scrollLeft + cm.doc.scrollLeft + let gutterW = display.gutters.offsetWidth, left = comp + "px" + for (let i = 0; i < view.length; i++) if (!view[i].hidden) { + if (cm.options.fixedGutter) { + if (view[i].gutter) + view[i].gutter.style.left = left + if (view[i].gutterBackground) + view[i].gutterBackground.style.left = left + } + let align = view[i].alignable + if (align) for (let 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. +export function maybeUpdateLineNumberWidth(cm) { + if (!cm.options.lineNumbers) return false + let doc = cm.doc, last = lineNumberFor(cm.options, doc.first + doc.size - 1), display = cm.display + if (last.length != display.lineNumChars) { + let test = display.measure.appendChild(elt("div", [elt("div", last)], + "CodeMirror-linenumber CodeMirror-gutter-elt")) + let 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.display) + return true + } + return false +} diff --git a/docs/js/node_modules/codemirror/src/display/mode_state.js b/docs/js/node_modules/codemirror/src/display/mode_state.js new file mode 100644 index 000000000..5d8ebf250 --- /dev/null +++ b/docs/js/node_modules/codemirror/src/display/mode_state.js @@ -0,0 +1,22 @@ +import { getMode } from "../modes.js" + +import { startWorker } from "./highlight_worker.js" +import { regChange } from "./view_tracking.js" + +// Used to get the editor into a consistent state again when options change. + +export function loadMode(cm) { + cm.doc.mode = getMode(cm.options, cm.doc.modeOption) + resetModeState(cm) +} + +export function resetModeState(cm) { + cm.doc.iter(line => { + if (line.stateAfter) line.stateAfter = null + if (line.styles) line.styles = null + }) + cm.doc.modeFrontier = cm.doc.highlightFrontier = cm.doc.first + startWorker(cm, 100) + cm.state.modeGen++ + if (cm.curOp) regChange(cm) +} diff --git a/docs/js/node_modules/codemirror/src/display/operations.js b/docs/js/node_modules/codemirror/src/display/operations.js new file mode 100644 index 000000000..6f3c9d086 --- /dev/null +++ b/docs/js/node_modules/codemirror/src/display/operations.js @@ -0,0 +1,205 @@ +import { clipPos } from "../line/pos.js" +import { findMaxLine } from "../line/spans.js" +import { displayWidth, measureChar, scrollGap } from "../measurement/position_measurement.js" +import { signal } from "../util/event.js" +import { activeElt } from "../util/dom.js" +import { finishOperation, pushOperation } from "../util/operation_group.js" + +import { ensureFocus } from "./focus.js" +import { measureForScrollbars, updateScrollbars } from "./scrollbars.js" +import { restartBlink } from "./selection.js" +import { maybeScrollWindow, scrollPosIntoView, setScrollLeft, setScrollTop } from "./scrolling.js" +import { DisplayUpdate, maybeClipScrollbars, postUpdateDisplay, setDocumentHeight, updateDisplayIfNeeded } from "./update_display.js" +import { updateHeightsInViewport } from "./update_lines.js" + +// 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. + +let nextOpId = 0 +// Start a new operation. +export 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: 0, // 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 + } + pushOperation(cm.curOp) +} + +// Finish an operation, updating the display and signalling delayed events +export function endOperation(cm) { + let op = cm.curOp + if (op) finishOperation(op, group => { + for (let 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) { + let ops = group.ops + for (let i = 0; i < ops.length; i++) // Read DOM + endOperation_R1(ops[i]) + for (let i = 0; i < ops.length; i++) // Write DOM (maybe) + endOperation_W1(ops[i]) + for (let i = 0; i < ops.length; i++) // Read DOM + endOperation_R2(ops[i]) + for (let i = 0; i < ops.length; i++) // Write DOM (maybe) + endOperation_W2(ops[i]) + for (let i = 0; i < ops.length; i++) // Read DOM + endOperation_finish(ops[i]) +} + +function endOperation_R1(op) { + let 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) { + let 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) { + let 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 + } + + let takeFocus = op.focus && op.focus == activeElt() + if (op.preparedSelection) + cm.display.input.showSelection(op.preparedSelection, takeFocus) + if (op.updatedDisplay || op.startHeight != cm.doc.height) + updateScrollbars(cm, op.barMeasure) + if (op.updatedDisplay) + setDocumentHeight(cm, op.barMeasure) + + if (op.selectionChanged) restartBlink(cm) + + if (cm.state.focused && op.updateInput) + cm.display.input.reset(op.typing) + if (takeFocus) ensureFocus(op.cm) +} + +function endOperation_finish(op) { + let 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) setScrollTop(cm, op.scrollTop, op.forceScroll) + + if (op.scrollLeft != null) setScrollLeft(cm, op.scrollLeft, true, true) + // If we need to scroll a specific position into view, do so. + if (op.scrollToPos) { + let rect = scrollPosIntoView(cm, clipPos(doc, op.scrollToPos.from), + clipPos(doc, op.scrollToPos.to), op.scrollToPos.margin) + maybeScrollWindow(cm, rect) + } + + // Fire events for markers that are hidden/unidden by editing or + // undoing + let hidden = op.maybeHiddenMarkers, unhidden = op.maybeUnhiddenMarkers + if (hidden) for (let i = 0; i < hidden.length; ++i) + if (!hidden[i].lines.length) signal(hidden[i], "hide") + if (unhidden) for (let 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 +export 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. +export 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. +export function methodOp(f) { + return function() { + if (this.curOp) return f.apply(this, arguments) + startOperation(this) + try { return f.apply(this, arguments) } + finally { endOperation(this) } + } +} +export function docMethodOp(f) { + return function() { + let cm = this.cm + if (!cm || cm.curOp) return f.apply(this, arguments) + startOperation(cm) + try { return f.apply(this, arguments) } + finally { endOperation(cm) } + } +} diff --git a/docs/js/node_modules/codemirror/src/display/scroll_events.js b/docs/js/node_modules/codemirror/src/display/scroll_events.js new file mode 100644 index 000000000..fbed42663 --- /dev/null +++ b/docs/js/node_modules/codemirror/src/display/scroll_events.js @@ -0,0 +1,115 @@ +import { chrome, gecko, ie, mac, presto, safari, webkit } from "../util/browser.js" +import { e_preventDefault } from "../util/event.js" + +import { updateDisplaySimple } from "./update_display.js" +import { setScrollLeft, updateScrollTop } from "./scrolling.js" + +// Since the delta values reported on mouse wheel events are +// unstandardized between browsers and even browser versions, and +// generally horribly unpredictable, this code starts by measuring +// the scroll effect that the first few mouse wheel events have, +// and, from that, detects the way it can convert deltas to pixel +// offsets afterwards. +// +// The reason we want to know the amount a wheel event will scroll +// is that it gives us a chance to update the display before the +// actual scrolling happens, reducing flickering. + +let wheelSamples = 0, wheelPixelsPerUnit = null +// Fill in a browser-detected starting value on browsers where we +// know one. These don't have to be accurate -- the result of them +// being wrong would just be a slight flicker on the first wheel +// scroll (if it is large enough). +if (ie) wheelPixelsPerUnit = -.53 +else if (gecko) wheelPixelsPerUnit = 15 +else if (chrome) wheelPixelsPerUnit = -.7 +else if (safari) wheelPixelsPerUnit = -1/3 + +function wheelEventDelta(e) { + let dx = e.wheelDeltaX, dy = e.wheelDeltaY + if (dx == null && e.detail && e.axis == e.HORIZONTAL_AXIS) dx = e.detail + if (dy == null && e.detail && e.axis == e.VERTICAL_AXIS) dy = e.detail + else if (dy == null) dy = e.wheelDelta + return {x: dx, y: dy} +} +export function wheelEventPixels(e) { + let delta = wheelEventDelta(e) + delta.x *= wheelPixelsPerUnit + delta.y *= wheelPixelsPerUnit + return delta +} + +export function onScrollWheel(cm, e) { + let delta = wheelEventDelta(e), dx = delta.x, dy = delta.y + + let display = cm.display, scroll = display.scroller + // Quit if there's nothing to scroll here + let canScrollX = scroll.scrollWidth > scroll.clientWidth + let canScrollY = scroll.scrollHeight > scroll.clientHeight + if (!(dx && canScrollX || dy && canScrollY)) return + + // Webkit browsers on OS X abort momentum scrolls when the target + // of the scroll event is removed from the scrollable element. + // This hack (see related code in patchDisplay) makes sure the + // element is kept around. + if (dy && mac && webkit) { + outer: for (let cur = e.target, view = display.view; cur != scroll; cur = cur.parentNode) { + for (let i = 0; i < view.length; i++) { + if (view[i].node == cur) { + cm.display.currentWheelTarget = cur + break outer + } + } + } + } + + // On some browsers, horizontal scrolling will cause redraws to + // happen before the gutter has been realigned, causing it to + // wriggle around in a most unseemly way. When we have an + // estimated pixels/delta value, we just handle horizontal + // scrolling entirely here. It'll be slightly off from native, but + // better than glitching out. + if (dx && !gecko && !presto && wheelPixelsPerUnit != null) { + if (dy && canScrollY) + updateScrollTop(cm, Math.max(0, scroll.scrollTop + dy * wheelPixelsPerUnit)) + setScrollLeft(cm, Math.max(0, scroll.scrollLeft + dx * wheelPixelsPerUnit)) + // Only prevent default scrolling if vertical scrolling is + // actually possible. Otherwise, it causes vertical scroll + // jitter on OSX trackpads when deltaX is small and deltaY + // is large (issue #3579) + if (!dy || (dy && canScrollY)) + e_preventDefault(e) + display.wheelStartX = null // Abort measurement, if in progress + return + } + + // 'Project' the visible viewport to cover the area that is being + // scrolled into view (if we know enough to estimate it). + if (dy && wheelPixelsPerUnit != null) { + let pixels = dy * wheelPixelsPerUnit + let top = cm.doc.scrollTop, bot = top + display.wrapper.clientHeight + if (pixels < 0) top = Math.max(0, top + pixels - 50) + else bot = Math.min(cm.doc.height, bot + pixels + 50) + updateDisplaySimple(cm, {top: top, bottom: bot}) + } + + if (wheelSamples < 20) { + if (display.wheelStartX == null) { + display.wheelStartX = scroll.scrollLeft; display.wheelStartY = scroll.scrollTop + display.wheelDX = dx; display.wheelDY = dy + setTimeout(() => { + if (display.wheelStartX == null) return + let movedX = scroll.scrollLeft - display.wheelStartX + let movedY = scroll.scrollTop - display.wheelStartY + let sample = (movedY && display.wheelDY && movedY / display.wheelDY) || + (movedX && display.wheelDX && movedX / display.wheelDX) + display.wheelStartX = display.wheelStartY = null + if (!sample) return + wheelPixelsPerUnit = (wheelPixelsPerUnit * wheelSamples + sample) / (wheelSamples + 1) + ++wheelSamples + }, 200) + } else { + display.wheelDX += dx; display.wheelDY += dy + } + } +} diff --git a/docs/js/node_modules/codemirror/src/display/scrollbars.js b/docs/js/node_modules/codemirror/src/display/scrollbars.js new file mode 100644 index 000000000..18ac121a9 --- /dev/null +++ b/docs/js/node_modules/codemirror/src/display/scrollbars.js @@ -0,0 +1,193 @@ +import { addClass, elt, rmClass } from "../util/dom.js" +import { on } from "../util/event.js" +import { scrollGap, paddingVert } from "../measurement/position_measurement.js" +import { ie, ie_version, mac, mac_geMountainLion } from "../util/browser.js" +import { updateHeightsInViewport } from "./update_lines.js" +import { Delayed } from "../util/misc.js" + +import { setScrollLeft, updateScrollTop } from "./scrolling.js" + +// SCROLLBARS + +// Prepare DOM reads needed to update the scrollbars. Done in one +// shot to minimize update/measure roundtrips. +export function measureForScrollbars(cm) { + let d = cm.display, gutterW = d.gutters.offsetWidth + let 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 + } +} + +class NativeScrollbars { + constructor(place, scroll, cm) { + this.cm = cm + let vert = this.vert = elt("div", [elt("div", null, null, "min-width: 1px")], "CodeMirror-vscrollbar") + let horiz = this.horiz = elt("div", [elt("div", null, null, "height: 100%; min-height: 1px")], "CodeMirror-hscrollbar") + vert.tabIndex = horiz.tabIndex = -1 + place(vert); place(horiz) + + on(vert, "scroll", () => { + if (vert.clientHeight) scroll(vert.scrollTop, "vertical") + }) + on(horiz, "scroll", () => { + if (horiz.clientWidth) scroll(horiz.scrollLeft, "horizontal") + }) + + this.checkedZeroWidth = 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" + } + + update(measure) { + let needsH = measure.scrollWidth > measure.clientWidth + 1 + let needsV = measure.scrollHeight > measure.clientHeight + 1 + let sWidth = measure.nativeBarWidth + + if (needsV) { + this.vert.style.display = "block" + this.vert.style.bottom = needsH ? sWidth + "px" : "0" + let 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" + let totalWidth = measure.viewWidth - measure.barLeft - (needsV ? sWidth : 0) + this.horiz.firstChild.style.width = + Math.max(0, measure.scrollWidth - measure.clientWidth + totalWidth) + "px" + } else { + this.horiz.style.display = "" + this.horiz.firstChild.style.width = "0" + } + + if (!this.checkedZeroWidth && measure.clientHeight > 0) { + if (sWidth == 0) this.zeroWidthHack() + this.checkedZeroWidth = true + } + + return {right: needsV ? sWidth : 0, bottom: needsH ? sWidth : 0} + } + + setScrollLeft(pos) { + if (this.horiz.scrollLeft != pos) this.horiz.scrollLeft = pos + if (this.disableHoriz) this.enableZeroWidthBar(this.horiz, this.disableHoriz, "horiz") + } + + setScrollTop(pos) { + if (this.vert.scrollTop != pos) this.vert.scrollTop = pos + if (this.disableVert) this.enableZeroWidthBar(this.vert, this.disableVert, "vert") + } + + zeroWidthHack() { + let w = mac && !mac_geMountainLion ? "12px" : "18px" + this.horiz.style.height = this.vert.style.width = w + this.horiz.style.pointerEvents = this.vert.style.pointerEvents = "none" + this.disableHoriz = new Delayed + this.disableVert = new Delayed + } + + enableZeroWidthBar(bar, delay, type) { + bar.style.pointerEvents = "auto" + function maybeDisable() { + // To find out whether the scrollbar is still visible, we + // check whether the element under the pixel in the bottom + // right corner of the scrollbar box is the scrollbar box + // itself (when the bar is still visible) or its filler child + // (when the bar is hidden). If it is still visible, we keep + // it enabled, if it's hidden, we disable pointer events. + let box = bar.getBoundingClientRect() + let elt = type == "vert" ? document.elementFromPoint(box.right - 1, (box.top + box.bottom) / 2) + : document.elementFromPoint((box.right + box.left) / 2, box.bottom - 1) + if (elt != bar) bar.style.pointerEvents = "none" + else delay.set(1000, maybeDisable) + } + delay.set(1000, maybeDisable) + } + + clear() { + let parent = this.horiz.parentNode + parent.removeChild(this.horiz) + parent.removeChild(this.vert) + } +} + +class NullScrollbars { + update() { return {bottom: 0, right: 0} } + setScrollLeft() {} + setScrollTop() {} + clear() {} +} + +export function updateScrollbars(cm, measure) { + if (!measure) measure = measureForScrollbars(cm) + let startWidth = cm.display.barWidth, startHeight = cm.display.barHeight + updateScrollbarsInner(cm, measure) + for (let 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) { + let d = cm.display + let sizes = d.scrollbars.update(measure) + + d.sizer.style.paddingRight = (d.barWidth = sizes.right) + "px" + d.sizer.style.paddingBottom = (d.barHeight = sizes.bottom) + "px" + d.heightForcer.style.borderBottom = sizes.bottom + "px solid transparent" + + 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 = "" +} + +export let scrollbarModel = {"native": NativeScrollbars, "null": NullScrollbars} + +export 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 scrollbarModel[cm.options.scrollbarStyle](node => { + cm.display.wrapper.insertBefore(node, cm.display.scrollbarFiller) + // Prevent clicks in the scrollbars from killing focus + on(node, "mousedown", () => { + if (cm.state.focused) setTimeout(() => cm.display.input.focus(), 0) + }) + node.setAttribute("cm-not-content", "true") + }, (pos, axis) => { + if (axis == "horizontal") setScrollLeft(cm, pos) + else updateScrollTop(cm, pos) + }, cm) + if (cm.display.scrollbars.addClass) + addClass(cm.display.wrapper, cm.display.scrollbars.addClass) +} diff --git a/docs/js/node_modules/codemirror/src/display/scrolling.js b/docs/js/node_modules/codemirror/src/display/scrolling.js new file mode 100644 index 000000000..26ec993b0 --- /dev/null +++ b/docs/js/node_modules/codemirror/src/display/scrolling.js @@ -0,0 +1,184 @@ +import { Pos } from "../line/pos.js" +import { cursorCoords, displayHeight, displayWidth, estimateCoords, paddingTop, paddingVert, scrollGap, textHeight } from "../measurement/position_measurement.js" +import { gecko, phantom } from "../util/browser.js" +import { elt } from "../util/dom.js" +import { signalDOMEvent } from "../util/event.js" + +import { startWorker } from "./highlight_worker.js" +import { alignHorizontally } from "./line_numbers.js" +import { updateDisplaySimple } from "./update_display.js" + +// SCROLLING THINGS INTO VIEW + +// If an editor sits on the top or bottom of the window, partially +// scrolled out of view, this ensures that the cursor is visible. +export function maybeScrollWindow(cm, rect) { + if (signalDOMEvent(cm, "scrollCursorIntoView")) return + + let display = cm.display, box = display.sizer.getBoundingClientRect(), doScroll = null + if (rect.top + box.top < 0) doScroll = true + else if (rect.bottom + box.top > (window.innerHeight || document.documentElement.clientHeight)) doScroll = false + if (doScroll != null && !phantom) { + let scrollNode = elt("div", "\u200b", null, `position: absolute; + top: ${rect.top - display.viewOffset - paddingTop(cm.display)}px; + height: ${rect.bottom - rect.top + scrollGap(cm) + display.barHeight}px; + left: ${rect.left}px; width: ${Math.max(2, rect.right - rect.left)}px;`) + cm.display.lineSpace.appendChild(scrollNode) + scrollNode.scrollIntoView(doScroll) + cm.display.lineSpace.removeChild(scrollNode) + } +} + +// Scroll a given position into view (immediately), verifying that +// it actually became visible (as line heights are accurately +// measured, the position of something may 'drift' during drawing). +export function scrollPosIntoView(cm, pos, end, margin) { + if (margin == null) margin = 0 + let rect + if (!cm.options.lineWrapping && pos == end) { + // Set pos and end to the cursor positions around the character pos sticks to + // If pos.sticky == "before", that is around pos.ch - 1, otherwise around pos.ch + // If pos == Pos(_, 0, "before"), pos and end are unchanged + pos = pos.ch ? Pos(pos.line, pos.sticky == "before" ? pos.ch - 1 : pos.ch, "after") : pos + end = pos.sticky == "before" ? Pos(pos.line, pos.ch + 1, "before") : pos + } + for (let limit = 0; limit < 5; limit++) { + let changed = false + let coords = cursorCoords(cm, pos) + let endCoords = !end || end == pos ? coords : cursorCoords(cm, end) + rect = {left: Math.min(coords.left, endCoords.left), + top: Math.min(coords.top, endCoords.top) - margin, + right: Math.max(coords.left, endCoords.left), + bottom: Math.max(coords.bottom, endCoords.bottom) + margin} + let scrollPos = calculateScrollPos(cm, rect) + let startTop = cm.doc.scrollTop, startLeft = cm.doc.scrollLeft + if (scrollPos.scrollTop != null) { + updateScrollTop(cm, scrollPos.scrollTop) + if (Math.abs(cm.doc.scrollTop - startTop) > 1) changed = true + } + if (scrollPos.scrollLeft != null) { + setScrollLeft(cm, scrollPos.scrollLeft) + if (Math.abs(cm.doc.scrollLeft - startLeft) > 1) changed = true + } + if (!changed) break + } + return rect +} + +// Scroll a given set of coordinates into view (immediately). +export function scrollIntoView(cm, rect) { + let scrollPos = calculateScrollPos(cm, rect) + if (scrollPos.scrollTop != null) updateScrollTop(cm, scrollPos.scrollTop) + if (scrollPos.scrollLeft != null) setScrollLeft(cm, scrollPos.scrollLeft) +} + +// Calculate a new scroll position needed to scroll the given +// rectangle into view. Returns an object with scrollTop and +// scrollLeft properties. When these are undefined, the +// vertical/horizontal position does not need to be adjusted. +function calculateScrollPos(cm, rect) { + let display = cm.display, snapMargin = textHeight(cm.display) + if (rect.top < 0) rect.top = 0 + let screentop = cm.curOp && cm.curOp.scrollTop != null ? cm.curOp.scrollTop : display.scroller.scrollTop + let screen = displayHeight(cm), result = {} + if (rect.bottom - rect.top > screen) rect.bottom = rect.top + screen + let docBottom = cm.doc.height + paddingVert(display) + let atTop = rect.top < snapMargin, atBottom = rect.bottom > docBottom - snapMargin + if (rect.top < screentop) { + result.scrollTop = atTop ? 0 : rect.top + } else if (rect.bottom > screentop + screen) { + let newTop = Math.min(rect.top, (atBottom ? docBottom : rect.bottom) - screen) + if (newTop != screentop) result.scrollTop = newTop + } + + let screenleft = cm.curOp && cm.curOp.scrollLeft != null ? cm.curOp.scrollLeft : display.scroller.scrollLeft + let screenw = displayWidth(cm) - (cm.options.fixedGutter ? display.gutters.offsetWidth : 0) + let tooWide = rect.right - rect.left > screenw + if (tooWide) rect.right = rect.left + screenw + if (rect.left < 10) + result.scrollLeft = 0 + else if (rect.left < screenleft) + result.scrollLeft = Math.max(0, rect.left - (tooWide ? 0 : 10)) + else if (rect.right > screenw + screenleft - 3) + result.scrollLeft = rect.right + (tooWide ? 0 : 10) - screenw + return result +} + +// Store a relative adjustment to the scroll position in the current +// operation (to be applied when the operation finishes). +export function addToScrollTop(cm, top) { + if (top == null) return + resolveScrollToPos(cm) + cm.curOp.scrollTop = (cm.curOp.scrollTop == null ? cm.doc.scrollTop : cm.curOp.scrollTop) + top +} + +// Make sure that at the end of the operation the current cursor is +// shown. +export function ensureCursorVisible(cm) { + resolveScrollToPos(cm) + let cur = cm.getCursor() + cm.curOp.scrollToPos = {from: cur, to: cur, margin: cm.options.cursorScrollMargin} +} + +export function scrollToCoords(cm, x, y) { + if (x != null || y != null) resolveScrollToPos(cm) + if (x != null) cm.curOp.scrollLeft = x + if (y != null) cm.curOp.scrollTop = y +} + +export function scrollToRange(cm, range) { + resolveScrollToPos(cm) + cm.curOp.scrollToPos = range +} + +// When an operation has its scrollToPos property set, and another +// scroll action is applied before the end of the operation, this +// 'simulates' scrolling that position into view in a cheap way, so +// that the effect of intermediate scroll commands is not ignored. +function resolveScrollToPos(cm) { + let range = cm.curOp.scrollToPos + if (range) { + cm.curOp.scrollToPos = null + let from = estimateCoords(cm, range.from), to = estimateCoords(cm, range.to) + scrollToCoordsRange(cm, from, to, range.margin) + } +} + +export function scrollToCoordsRange(cm, from, to, margin) { + let sPos = calculateScrollPos(cm, { + left: Math.min(from.left, to.left), + top: Math.min(from.top, to.top) - margin, + right: Math.max(from.right, to.right), + bottom: Math.max(from.bottom, to.bottom) + margin + }) + scrollToCoords(cm, sPos.scrollLeft, sPos.scrollTop) +} + +// Sync the scrollable area and scrollbars, ensure the viewport +// covers the visible area. +export function updateScrollTop(cm, val) { + if (Math.abs(cm.doc.scrollTop - val) < 2) return + if (!gecko) updateDisplaySimple(cm, {top: val}) + setScrollTop(cm, val, true) + if (gecko) updateDisplaySimple(cm) + startWorker(cm, 100) +} + +export function setScrollTop(cm, val, forceScroll) { + val = Math.min(cm.display.scroller.scrollHeight - cm.display.scroller.clientHeight, val) + if (cm.display.scroller.scrollTop == val && !forceScroll) return + cm.doc.scrollTop = val + cm.display.scrollbars.setScrollTop(val) + if (cm.display.scroller.scrollTop != val) cm.display.scroller.scrollTop = val +} + +// Sync scroller and scrollbar, ensure the gutter elements are +// aligned. +export function setScrollLeft(cm, val, isScroller, forceScroll) { + val = Math.min(val, cm.display.scroller.scrollWidth - cm.display.scroller.clientWidth) + if ((isScroller ? val == cm.doc.scrollLeft : Math.abs(cm.doc.scrollLeft - val) < 2) && !forceScroll) return + cm.doc.scrollLeft = val + alignHorizontally(cm) + if (cm.display.scroller.scrollLeft != val) cm.display.scroller.scrollLeft = val + cm.display.scrollbars.setScrollLeft(val) +} diff --git a/docs/js/node_modules/codemirror/src/display/selection.js b/docs/js/node_modules/codemirror/src/display/selection.js new file mode 100644 index 000000000..c658c0a27 --- /dev/null +++ b/docs/js/node_modules/codemirror/src/display/selection.js @@ -0,0 +1,158 @@ +import { Pos } from "../line/pos.js" +import { visualLine } from "../line/spans.js" +import { getLine } from "../line/utils_line.js" +import { charCoords, cursorCoords, displayWidth, paddingH, wrappedLineExtentChar } from "../measurement/position_measurement.js" +import { getOrder, iterateBidiSections } from "../util/bidi.js" +import { elt } from "../util/dom.js" + +export function updateSelection(cm) { + cm.display.input.showSelection(cm.display.input.prepareSelection()) +} + +export function prepareSelection(cm, primary = true) { + let doc = cm.doc, result = {} + let curFragment = result.cursors = document.createDocumentFragment() + let selFragment = result.selection = document.createDocumentFragment() + + for (let i = 0; i < doc.sel.ranges.length; i++) { + if (!primary && i == doc.sel.primIndex) continue + let range = doc.sel.ranges[i] + if (range.from().line >= cm.display.viewTo || range.to().line < cm.display.viewFrom) continue + let collapsed = range.empty() + if (collapsed || cm.options.showCursorWhenSelecting) + drawSelectionCursor(cm, range.head, curFragment) + if (!collapsed) + drawSelectionRange(cm, range, selFragment) + } + return result +} + +// Draws a cursor for the given range +export function drawSelectionCursor(cm, head, output) { + let pos = cursorCoords(cm, head, "div", null, null, !cm.options.singleCursorHeightPerLine) + + let 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 + let 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" + } +} + +function cmpCoords(a, b) { return a.top - b.top || a.left - b.left } + +// Draws the given range as a highlighted selection +function drawSelectionRange(cm, range, output) { + let display = cm.display, doc = cm.doc + let fragment = document.createDocumentFragment() + let padding = paddingH(cm.display), leftSide = padding.left + let rightSide = Math.max(display.sizerWidth, displayWidth(cm) - display.sizer.offsetLeft) - padding.right + let docLTR = doc.direction == "ltr" + + 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) { + let lineObj = getLine(doc, line) + let lineLen = lineObj.text.length + let start, end + function coords(ch, bias) { + return charCoords(cm, Pos(line, ch), "div", lineObj, bias) + } + + function wrapX(pos, dir, side) { + let extent = wrappedLineExtentChar(cm, lineObj, null, pos) + let prop = (dir == "ltr") == (side == "after") ? "left" : "right" + let ch = side == "after" ? extent.begin : extent.end - (/\s/.test(lineObj.text.charAt(extent.end - 1)) ? 2 : 1) + return coords(ch, prop)[prop] + } + + let order = getOrder(lineObj, doc.direction) + iterateBidiSections(order, fromArg || 0, toArg == null ? lineLen : toArg, (from, to, dir, i) => { + let ltr = dir == "ltr" + let fromPos = coords(from, ltr ? "left" : "right") + let toPos = coords(to - 1, ltr ? "right" : "left") + + let openStart = fromArg == null && from == 0, openEnd = toArg == null && to == lineLen + let first = i == 0, last = !order || i == order.length - 1 + if (toPos.top - fromPos.top <= 3) { // Single line + let openLeft = (docLTR ? openStart : openEnd) && first + let openRight = (docLTR ? openEnd : openStart) && last + let left = openLeft ? leftSide : (ltr ? fromPos : toPos).left + let right = openRight ? rightSide : (ltr ? toPos : fromPos).right + add(left, fromPos.top, right - left, fromPos.bottom) + } else { // Multiple lines + let topLeft, topRight, botLeft, botRight + if (ltr) { + topLeft = docLTR && openStart && first ? leftSide : fromPos.left + topRight = docLTR ? rightSide : wrapX(from, dir, "before") + botLeft = docLTR ? leftSide : wrapX(to, dir, "after") + botRight = docLTR && openEnd && last ? rightSide : toPos.right + } else { + topLeft = !docLTR ? leftSide : wrapX(from, dir, "before") + topRight = !docLTR && openStart && first ? rightSide : fromPos.right + botLeft = !docLTR && openEnd && last ? leftSide : toPos.left + botRight = !docLTR ? rightSide : wrapX(to, dir, "after") + } + add(topLeft, fromPos.top, topRight - topLeft, fromPos.bottom) + if (fromPos.bottom < toPos.top) add(leftSide, fromPos.bottom, null, toPos.top) + add(botLeft, toPos.top, botRight - botLeft, toPos.bottom) + } + + if (!start || cmpCoords(fromPos, start) < 0) start = fromPos + if (cmpCoords(toPos, start) < 0) start = toPos + if (!end || cmpCoords(fromPos, end) < 0) end = fromPos + if (cmpCoords(toPos, end) < 0) end = toPos + }) + return {start: start, end: end} + } + + let sFrom = range.from(), sTo = range.to() + if (sFrom.line == sTo.line) { + drawForLine(sFrom.line, sFrom.ch, sTo.ch) + } else { + let fromLine = getLine(doc, sFrom.line), toLine = getLine(doc, sTo.line) + let singleVLine = visualLine(fromLine) == visualLine(toLine) + let leftEnd = drawForLine(sFrom.line, sFrom.ch, singleVLine ? fromLine.text.length + 1 : null).end + let 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 +export function restartBlink(cm) { + if (!cm.state.focused) return + let display = cm.display + clearInterval(display.blinker) + let on = true + display.cursorDiv.style.visibility = "" + if (cm.options.cursorBlinkRate > 0) + display.blinker = setInterval(() => display.cursorDiv.style.visibility = (on = !on) ? "" : "hidden", + cm.options.cursorBlinkRate) + else if (cm.options.cursorBlinkRate < 0) + display.cursorDiv.style.visibility = "hidden" +} diff --git a/docs/js/node_modules/codemirror/src/display/update_display.js b/docs/js/node_modules/codemirror/src/display/update_display.js new file mode 100644 index 000000000..20798f590 --- /dev/null +++ b/docs/js/node_modules/codemirror/src/display/update_display.js @@ -0,0 +1,260 @@ +import { sawCollapsedSpans } from "../line/saw_special_spans.js" +import { heightAtLine, visualLineEndNo, visualLineNo } from "../line/spans.js" +import { getLine, lineNumberFor } from "../line/utils_line.js" +import { displayHeight, displayWidth, getDimensions, paddingVert, scrollGap } from "../measurement/position_measurement.js" +import { mac, webkit } from "../util/browser.js" +import { activeElt, removeChildren, contains } from "../util/dom.js" +import { hasHandler, signal } from "../util/event.js" +import { indexOf } from "../util/misc.js" + +import { buildLineElement, updateLineForChanges } from "./update_line.js" +import { startWorker } from "./highlight_worker.js" +import { maybeUpdateLineNumberWidth } from "./line_numbers.js" +import { measureForScrollbars, updateScrollbars } from "./scrollbars.js" +import { updateSelection } from "./selection.js" +import { updateHeightsInViewport, visibleLines } from "./update_lines.js" +import { adjustView, countDirtyView, resetView } from "./view_tracking.js" + +// DISPLAY DRAWING + +export class DisplayUpdate { + constructor(cm, viewport, force) { + let 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 = [] + } + + signal(emitter, type) { + if (hasHandler(emitter, type)) + this.events.push(arguments) + } + finish() { + for (let i = 0; i < this.events.length; i++) + signal.apply(null, this.events[i]) + } +} + +export function maybeClipScrollbars(cm) { + let 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 + } +} + +function selectionSnapshot(cm) { + if (cm.hasFocus()) return null + let active = activeElt() + if (!active || !contains(cm.display.lineDiv, active)) return null + let result = {activeElt: active} + if (window.getSelection) { + let sel = window.getSelection() + if (sel.anchorNode && sel.extend && contains(cm.display.lineDiv, sel.anchorNode)) { + result.anchorNode = sel.anchorNode + result.anchorOffset = sel.anchorOffset + result.focusNode = sel.focusNode + result.focusOffset = sel.focusOffset + } + } + return result +} + +function restoreSelection(snapshot) { + if (!snapshot || !snapshot.activeElt || snapshot.activeElt == activeElt()) return + snapshot.activeElt.focus() + if (snapshot.anchorNode && contains(document.body, snapshot.anchorNode) && contains(document.body, snapshot.focusNode)) { + let sel = window.getSelection(), range = document.createRange() + range.setEnd(snapshot.anchorNode, snapshot.anchorOffset) + range.collapse(false) + sel.removeAllRanges() + sel.addRange(range) + sel.extend(snapshot.focusNode, snapshot.focusOffset) + } +} + +// Does the actual updating of the line display. Bails out +// (returning false) when there is nothing to be done and forced is +// false. +export function updateDisplayIfNeeded(cm, update) { + let 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) + let end = doc.first + doc.size + let from = Math.max(update.visible.from - cm.options.viewportMargin, doc.first) + let 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) + } + + let 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" + + let 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. + let selSnapshot = selectionSnapshot(cm) + 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. + restoreSelection(selSnapshot) + + // Prevent selection and cursors from interfering with the scroll + // width and height. + removeChildren(display.cursorDiv) + removeChildren(display.selectionDiv) + display.gutters.style.height = display.sizer.style.minHeight = 0 + + if (different) { + display.lastWrapHeight = update.wrapperHeight + display.lastWrapWidth = update.wrapperWidth + startWorker(cm, 400) + } + + display.updateLineNumbers = null + + return true +} + +export function postUpdateDisplay(cm, update) { + let viewport = update.viewport + + for (let 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) + let barMeasure = measureForScrollbars(cm) + updateSelection(cm) + updateScrollbars(cm, barMeasure) + setDocumentHeight(cm, barMeasure) + update.force = false + } + + 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 + } +} + +export function updateDisplaySimple(cm, viewport) { + let update = new DisplayUpdate(cm, viewport) + if (updateDisplayIfNeeded(cm, update)) { + updateHeightsInViewport(cm) + postUpdateDisplay(cm, update) + let barMeasure = measureForScrollbars(cm) + updateSelection(cm) + updateScrollbars(cm, barMeasure) + setDocumentHeight(cm, barMeasure) + update.finish() + } +} + +// 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) { + let display = cm.display, lineNumbers = cm.options.lineNumbers + let container = display.lineDiv, cur = container.firstChild + + function rm(node) { + let 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 + } + + let 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 (let i = 0; i < view.length; i++) { + let lineView = view[i] + if (lineView.hidden) { + } else if (!lineView.node || lineView.node.parentNode != container) { // Not drawn yet + let node = buildLineElement(cm, lineView, lineN, dims) + container.insertBefore(node, cur) + } else { // Already drawn + while (cur != lineView.node) cur = rm(cur) + let 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) +} + +export function updateGutterSpace(display) { + let width = display.gutters.offsetWidth + display.sizer.style.marginLeft = width + "px" +} + +export function setDocumentHeight(cm, measure) { + cm.display.sizer.style.minHeight = measure.docHeight + "px" + cm.display.heightForcer.style.top = measure.docHeight + "px" + cm.display.gutters.style.height = (measure.docHeight + cm.display.barHeight + scrollGap(cm)) + "px" +} diff --git a/docs/js/node_modules/codemirror/src/display/update_line.js b/docs/js/node_modules/codemirror/src/display/update_line.js new file mode 100644 index 000000000..a436973ea --- /dev/null +++ b/docs/js/node_modules/codemirror/src/display/update_line.js @@ -0,0 +1,188 @@ +import { buildLineContent } from "../line/line_data.js" +import { lineNumberFor } from "../line/utils_line.js" +import { ie, ie_version } from "../util/browser.js" +import { elt } from "../util/dom.js" +import { signalLater } from "../util/operation_group.js" + +// 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. +export function updateLineForChanges(cm, lineView, lineN, dims) { + for (let j = 0; j < lineView.changes.length; j++) { + let type = lineView.changes[j] + if (type == "text") updateLineText(cm, lineView) + else if (type == "gutter") updateLineGutter(cm, lineView, lineN, dims) + else if (type == "class") updateLineClasses(cm, 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(cm, lineView) { + let 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) { + let wrap = ensureLineWrapped(lineView) + lineView.background = wrap.insertBefore(elt("div", null, cls), wrap.firstChild) + cm.display.input.setUneditable(lineView.background) + } +} + +// Wrapper around buildLineContent which will reuse the structure +// in display.externalMeasured when possible. +function getLineContent(cm, lineView) { + let 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) { + let cls = lineView.text.className + let 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(cm, lineView) + } else if (cls) { + lineView.text.className = cls + } +} + +function updateLineClasses(cm, lineView) { + updateLineBackground(cm, lineView) + if (lineView.line.wrapClass) + ensureLineWrapped(lineView).className = lineView.line.wrapClass + else if (lineView.node != lineView.text) + lineView.node.className = "" + let 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 + } + if (lineView.gutterBackground) { + lineView.node.removeChild(lineView.gutterBackground) + lineView.gutterBackground = null + } + if (lineView.line.gutterClass) { + let wrap = ensureLineWrapped(lineView) + lineView.gutterBackground = elt("div", null, "CodeMirror-gutter-background " + lineView.line.gutterClass, + `left: ${cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth}px; width: ${dims.gutterTotalWidth}px`) + cm.display.input.setUneditable(lineView.gutterBackground) + wrap.insertBefore(lineView.gutterBackground, lineView.text) + } + let markers = lineView.line.gutterMarkers + if (cm.options.lineNumbers || markers) { + let wrap = ensureLineWrapped(lineView) + let gutterWrap = lineView.gutter = elt("div", null, "CodeMirror-gutter-wrapper", `left: ${cm.options.fixedGutter ? dims.fixedPos : -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 (let k = 0; k < cm.display.gutterSpecs.length; ++k) { + let id = cm.display.gutterSpecs[k].className, 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 (let node = lineView.node.firstChild, next; node; node = next) { + next = node.nextSibling + if (node.className == "CodeMirror-linewidget") + lineView.node.removeChild(node) + } + insertLineWidgets(cm, lineView, dims) +} + +// Build a line's DOM representation from scratch +export function buildLineElement(cm, lineView, lineN, dims) { + let 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(cm, 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 (let 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 + let wrap = ensureLineWrapped(lineView) + for (let i = 0, ws = line.widgets; i < ws.length; ++i) { + let 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) + let 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" + } +} diff --git a/docs/js/node_modules/codemirror/src/display/update_lines.js b/docs/js/node_modules/codemirror/src/display/update_lines.js new file mode 100644 index 000000000..60c367e4d --- /dev/null +++ b/docs/js/node_modules/codemirror/src/display/update_lines.js @@ -0,0 +1,76 @@ +import { heightAtLine } from "../line/spans.js" +import { getLine, lineAtHeight, updateLineHeight } from "../line/utils_line.js" +import { paddingTop, charWidth } from "../measurement/position_measurement.js" +import { ie, ie_version } from "../util/browser.js" + +// Read the actual heights of the rendered lines, and update their +// stored heights to match. +export function updateHeightsInViewport(cm) { + let display = cm.display + let prevBottom = display.lineDiv.offsetTop + for (let i = 0; i < display.view.length; i++) { + let cur = display.view[i], wrapping = cm.options.lineWrapping + let height, width = 0 + if (cur.hidden) continue + if (ie && ie_version < 8) { + let bot = cur.node.offsetTop + cur.node.offsetHeight + height = bot - prevBottom + prevBottom = bot + } else { + let box = cur.node.getBoundingClientRect() + height = box.bottom - box.top + // Check that lines don't extend past the right of the current + // editor width + if (!wrapping && cur.text.firstChild) + width = cur.text.firstChild.getBoundingClientRect().right - box.left - 1 + } + let diff = cur.line.height - height + if (diff > .005 || diff < -.005) { + updateLineHeight(cur.line, height) + updateWidgetHeight(cur.line) + if (cur.rest) for (let j = 0; j < cur.rest.length; j++) + updateWidgetHeight(cur.rest[j]) + } + if (width > cm.display.sizerWidth) { + let chWidth = Math.ceil(width / charWidth(cm.display)) + if (chWidth > cm.display.maxLineLength) { + cm.display.maxLineLength = chWidth + cm.display.maxLine = cur.line + cm.display.maxLineChanged = true + } + } + } +} + +// Read and store the height of line widgets associated with the +// given line. +function updateWidgetHeight(line) { + if (line.widgets) for (let i = 0; i < line.widgets.length; ++i) { + let w = line.widgets[i], parent = w.node.parentNode + if (parent) w.height = parent.offsetHeight + } +} + +// 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. +export function visibleLines(display, doc, viewport) { + let top = viewport && viewport.top != null ? Math.max(0, viewport.top) : display.scroller.scrollTop + top = Math.floor(top - paddingTop(display)) + let bottom = viewport && viewport.bottom != null ? viewport.bottom : top + display.wrapper.clientHeight + + let 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) { + let 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)} +} diff --git a/docs/js/node_modules/codemirror/src/display/view_tracking.js b/docs/js/node_modules/codemirror/src/display/view_tracking.js new file mode 100644 index 000000000..41464f235 --- /dev/null +++ b/docs/js/node_modules/codemirror/src/display/view_tracking.js @@ -0,0 +1,153 @@ +import { buildViewArray } from "../line/line_data.js" +import { sawCollapsedSpans } from "../line/saw_special_spans.js" +import { visualLineEndNo, visualLineNo } from "../line/spans.js" +import { findViewIndex } from "../measurement/position_measurement.js" +import { indexOf } from "../util/misc.js" + +// 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. +export 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 + + let 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 + let 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 + let 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 + let cutTop = viewCuttingPoint(cm, from, from, -1) + let 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) + } + } + + let 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" +export function regLineChange(cm, line, type) { + cm.curOp.viewChanged = true + let 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 + let lineView = display.view[findViewIndex(cm, line)] + if (lineView.node == null) return + let arr = lineView.changes || (lineView.changes = []) + if (indexOf(arr, type) == -1) arr.push(type) +} + +// Clear the view. +export function resetView(cm) { + cm.display.viewFrom = cm.display.viewTo = cm.doc.first + cm.display.view = [] + cm.display.viewOffset = 0 +} + +function viewCuttingPoint(cm, oldN, newN, dir) { + let index = findViewIndex(cm, oldN), diff, view = cm.display.view + if (!sawCollapsedSpans || newN == cm.doc.first + cm.doc.size) + return {index: index, lineN: newN} + let n = cm.display.viewFrom + for (let i = 0; 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. +export function adjustView(cm, from, to) { + let 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). +export function countDirtyView(cm) { + let view = cm.display.view, dirty = 0 + for (let i = 0; i < view.length; i++) { + let lineView = view[i] + if (!lineView.hidden && (!lineView.node || lineView.changes)) ++dirty + } + return dirty +} diff --git a/docs/js/node_modules/codemirror/src/edit/CodeMirror.js b/docs/js/node_modules/codemirror/src/edit/CodeMirror.js new file mode 100644 index 000000000..9188e1b25 --- /dev/null +++ b/docs/js/node_modules/codemirror/src/edit/CodeMirror.js @@ -0,0 +1,212 @@ +import { Display } from "../display/Display.js" +import { onFocus, onBlur } from "../display/focus.js" +import { maybeUpdateLineNumberWidth } from "../display/line_numbers.js" +import { endOperation, operation, startOperation } from "../display/operations.js" +import { initScrollbars } from "../display/scrollbars.js" +import { onScrollWheel } from "../display/scroll_events.js" +import { setScrollLeft, updateScrollTop } from "../display/scrolling.js" +import { clipPos, Pos } from "../line/pos.js" +import { posFromMouse } from "../measurement/position_measurement.js" +import { eventInWidget } from "../measurement/widgets.js" +import Doc from "../model/Doc.js" +import { attachDoc } from "../model/document_data.js" +import { Range } from "../model/selection.js" +import { extendSelection } from "../model/selection_updates.js" +import { ie, ie_version, mobile, webkit } from "../util/browser.js" +import { e_preventDefault, e_stop, on, signal, signalDOMEvent } from "../util/event.js" +import { bind, copyObj, Delayed } from "../util/misc.js" + +import { clearDragCursor, onDragOver, onDragStart, onDrop } from "./drop_events.js" +import { ensureGlobalHandlers } from "./global_events.js" +import { onKeyDown, onKeyPress, onKeyUp } from "./key_events.js" +import { clickInGutter, onContextMenu, onMouseDown } from "./mouse_events.js" +import { themeChanged } from "./utils.js" +import { defaults, optionHandlers, Init } from "./options.js" + +// A CodeMirror instance represents an editor. This is the object +// that user code is usually dealing with. + +export 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) + + let doc = options.value + if (typeof doc == "string") doc = new Doc(doc, options.mode, null, options.lineSeparator, options.direction) + else if (options.mode) doc.modeOption = options.mode + this.doc = doc + + let input = new CodeMirror.inputStyles[options.inputStyle](this) + let display = this.display = new Display(place, doc, input, options) + display.wrapper.CodeMirror = this + themeChanged(this) + if (options.lineWrapping) + this.display.wrapper.className += " CodeMirror-wrap" + 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: -1, cutIncoming: -1, // help recognize paste/cut edits in input.poll + selectingText: false, + draggingText: false, + highlight: new Delayed(), // stores highlight worker timeout + keySeq: null, // Unfinished key sequence + specialChars: null + } + + if (options.autofocus && !mobile) display.input.focus() + + // Override magic textarea content restore that IE sometimes does + // on our hidden textarea on reload + if (ie && ie_version < 11) setTimeout(() => this.display.input.reset(true), 20) + + registerEventHandlers(this) + ensureGlobalHandlers() + + startOperation(this) + this.curOp.forceUpdate = true + attachDoc(this, doc) + + if ((options.autofocus && !mobile) || this.hasFocus()) + setTimeout(bind(onFocus, this), 20) + else + onBlur(this) + + for (let opt in optionHandlers) if (optionHandlers.hasOwnProperty(opt)) + optionHandlers[opt](this, options[opt], Init) + maybeUpdateLineNumberWidth(this) + if (options.finishInit) options.finishInit(this) + for (let 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" +} + +// The default configuration options. +CodeMirror.defaults = defaults +// Functions to run when options are changed. +CodeMirror.optionHandlers = optionHandlers + +export default CodeMirror + +// Attach the necessary event handlers when initializing the editor +function registerEventHandlers(cm) { + let 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, e => { + if (signalDOMEvent(cm, e)) return + let pos = posFromMouse(cm, e) + if (!pos || clickInGutter(cm, e) || eventInWidget(cm.display, e)) return + e_preventDefault(e) + let word = cm.findWordAt(pos) + extendSelection(cm.doc, word.anchor, word.head) + })) + else + on(d.scroller, "dblclick", 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. + on(d.scroller, "contextmenu", e => onContextMenu(cm, e)) + + // Used to suppress mouse event handling when a touch happens + let touchFinished, prevTouch = {end: 0} + function finishTouch() { + if (d.activeTouch) { + touchFinished = setTimeout(() => d.activeTouch = null, 1000) + prevTouch = d.activeTouch + prevTouch.end = +new Date + } + } + function isMouseLikeTouchEvent(e) { + if (e.touches.length != 1) return false + let touch = e.touches[0] + return touch.radiusX <= 1 && touch.radiusY <= 1 + } + function farAway(touch, other) { + if (other.left == null) return true + let dx = other.left - touch.left, dy = other.top - touch.top + return dx * dx + dy * dy > 20 * 20 + } + on(d.scroller, "touchstart", e => { + if (!signalDOMEvent(cm, e) && !isMouseLikeTouchEvent(e) && !clickInGutter(cm, e)) { + d.input.ensurePolled() + clearTimeout(touchFinished) + let 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", () => { + if (d.activeTouch) d.activeTouch.moved = true + }) + on(d.scroller, "touchend", e => { + let touch = d.activeTouch + if (touch && !eventInWidget(d, e) && touch.left != null && + !touch.moved && new Date - touch.start < 300) { + let 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", () => { + if (d.scroller.clientHeight) { + updateScrollTop(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", e => onScrollWheel(cm, e)) + on(d.scroller, "DOMMouseScroll", e => onScrollWheel(cm, e)) + + // Prevent wrapper from ever scrolling + on(d.wrapper, "scroll", () => d.wrapper.scrollTop = d.wrapper.scrollLeft = 0) + + d.dragFunctions = { + enter: e => {if (!signalDOMEvent(cm, e)) e_stop(e)}, + over: e => {if (!signalDOMEvent(cm, e)) { onDragOver(cm, e); e_stop(e) }}, + start: e => onDragStart(cm, e), + drop: operation(cm, onDrop), + leave: e => {if (!signalDOMEvent(cm, e)) { clearDragCursor(cm) }} + } + + let inp = d.input.getField() + on(inp, "keyup", e => onKeyUp.call(cm, e)) + on(inp, "keydown", operation(cm, onKeyDown)) + on(inp, "keypress", operation(cm, onKeyPress)) + on(inp, "focus", e => onFocus(cm, e)) + on(inp, "blur", e => onBlur(cm, e)) +} + +let initHooks = [] +CodeMirror.defineInitHook = f => initHooks.push(f) diff --git a/docs/js/node_modules/codemirror/src/edit/commands.js b/docs/js/node_modules/codemirror/src/edit/commands.js new file mode 100644 index 000000000..3916b129f --- /dev/null +++ b/docs/js/node_modules/codemirror/src/edit/commands.js @@ -0,0 +1,178 @@ +import { deleteNearSelection } from "./deleteNearSelection.js" +import { runInOp } from "../display/operations.js" +import { ensureCursorVisible } from "../display/scrolling.js" +import { endOfLine } from "../input/movement.js" +import { clipPos, Pos } from "../line/pos.js" +import { visualLine, visualLineEnd } from "../line/spans.js" +import { getLine, lineNo } from "../line/utils_line.js" +import { Range } from "../model/selection.js" +import { selectAll } from "../model/selection_updates.js" +import { countColumn, sel_dontScroll, sel_move, spaceStr } from "../util/misc.js" +import { getOrder } from "../util/bidi.js" + +// Commands are parameter-less actions that can be performed on an +// editor, mostly used for keybindings. +export let commands = { + selectAll: selectAll, + singleSelection: cm => cm.setSelection(cm.getCursor("anchor"), cm.getCursor("head"), sel_dontScroll), + killLine: cm => deleteNearSelection(cm, range => { + if (range.empty()) { + let len = getLine(cm.doc, range.head.line).text.length + if (range.head.ch == len && range.head.line < cm.lastLine()) + return {from: range.head, to: Pos(range.head.line + 1, 0)} + else + return {from: range.head, to: Pos(range.head.line, len)} + } else { + return {from: range.from(), to: range.to()} + } + }), + deleteLine: cm => deleteNearSelection(cm, range => ({ + from: Pos(range.from().line, 0), + to: clipPos(cm.doc, Pos(range.to().line + 1, 0)) + })), + delLineLeft: cm => deleteNearSelection(cm, range => ({ + from: Pos(range.from().line, 0), to: range.from() + })), + delWrappedLineLeft: cm => deleteNearSelection(cm, range => { + let top = cm.charCoords(range.head, "div").top + 5 + let leftPos = cm.coordsChar({left: 0, top: top}, "div") + return {from: leftPos, to: range.from()} + }), + delWrappedLineRight: cm => deleteNearSelection(cm, range => { + let top = cm.charCoords(range.head, "div").top + 5 + let rightPos = cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div") + return {from: range.from(), to: rightPos } + }), + undo: cm => cm.undo(), + redo: cm => cm.redo(), + undoSelection: cm => cm.undoSelection(), + redoSelection: cm => cm.redoSelection(), + goDocStart: cm => cm.extendSelection(Pos(cm.firstLine(), 0)), + goDocEnd: cm => cm.extendSelection(Pos(cm.lastLine())), + goLineStart: cm => cm.extendSelectionsBy(range => lineStart(cm, range.head.line), + {origin: "+move", bias: 1} + ), + goLineStartSmart: cm => cm.extendSelectionsBy(range => lineStartSmart(cm, range.head), + {origin: "+move", bias: 1} + ), + goLineEnd: cm => cm.extendSelectionsBy(range => lineEnd(cm, range.head.line), + {origin: "+move", bias: -1} + ), + goLineRight: cm => cm.extendSelectionsBy(range => { + let top = cm.cursorCoords(range.head, "div").top + 5 + return cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div") + }, sel_move), + goLineLeft: cm => cm.extendSelectionsBy(range => { + let top = cm.cursorCoords(range.head, "div").top + 5 + return cm.coordsChar({left: 0, top: top}, "div") + }, sel_move), + goLineLeftSmart: cm => cm.extendSelectionsBy(range => { + let top = cm.cursorCoords(range.head, "div").top + 5 + let pos = cm.coordsChar({left: 0, top: top}, "div") + if (pos.ch < cm.getLine(pos.line).search(/\S/)) return lineStartSmart(cm, range.head) + return pos + }, sel_move), + goLineUp: cm => cm.moveV(-1, "line"), + goLineDown: cm => cm.moveV(1, "line"), + goPageUp: cm => cm.moveV(-1, "page"), + goPageDown: cm => cm.moveV(1, "page"), + goCharLeft: cm => cm.moveH(-1, "char"), + goCharRight: cm => cm.moveH(1, "char"), + goColumnLeft: cm => cm.moveH(-1, "column"), + goColumnRight: cm => cm.moveH(1, "column"), + goWordLeft: cm => cm.moveH(-1, "word"), + goGroupRight: cm => cm.moveH(1, "group"), + goGroupLeft: cm => cm.moveH(-1, "group"), + goWordRight: cm => cm.moveH(1, "word"), + delCharBefore: cm => cm.deleteH(-1, "char"), + delCharAfter: cm => cm.deleteH(1, "char"), + delWordBefore: cm => cm.deleteH(-1, "word"), + delWordAfter: cm => cm.deleteH(1, "word"), + delGroupBefore: cm => cm.deleteH(-1, "group"), + delGroupAfter: cm => cm.deleteH(1, "group"), + indentAuto: cm => cm.indentSelection("smart"), + indentMore: cm => cm.indentSelection("add"), + indentLess: cm => cm.indentSelection("subtract"), + insertTab: cm => cm.replaceSelection("\t"), + insertSoftTab: cm => { + let spaces = [], ranges = cm.listSelections(), tabSize = cm.options.tabSize + for (let i = 0; i < ranges.length; i++) { + let pos = ranges[i].from() + let col = countColumn(cm.getLine(pos.line), pos.ch, tabSize) + spaces.push(spaceStr(tabSize - col % tabSize)) + } + cm.replaceSelections(spaces) + }, + defaultTab: cm => { + if (cm.somethingSelected()) cm.indentSelection("add") + else cm.execCommand("insertTab") + }, + // Swap the two chars left and right of each selection's head. + // Move cursor behind the two swapped characters afterwards. + // + // Doesn't consider line feeds a character. + // Doesn't scan more than one line above to find a character. + // Doesn't do anything on an empty line. + // Doesn't do anything with non-empty selections. + transposeChars: cm => runInOp(cm, () => { + let ranges = cm.listSelections(), newSel = [] + for (let i = 0; i < ranges.length; i++) { + if (!ranges[i].empty()) continue + let cur = ranges[i].head, line = getLine(cm.doc, cur.line).text + if (line) { + if (cur.ch == line.length) cur = new Pos(cur.line, cur.ch - 1) + if (cur.ch > 0) { + cur = new Pos(cur.line, cur.ch + 1) + cm.replaceRange(line.charAt(cur.ch - 1) + line.charAt(cur.ch - 2), + Pos(cur.line, cur.ch - 2), cur, "+transpose") + } else if (cur.line > cm.doc.first) { + let prev = getLine(cm.doc, cur.line - 1).text + if (prev) { + cur = new Pos(cur.line, 1) + cm.replaceRange(line.charAt(0) + cm.doc.lineSeparator() + + prev.charAt(prev.length - 1), + Pos(cur.line - 1, prev.length - 1), cur, "+transpose") + } + } + } + newSel.push(new Range(cur, cur)) + } + cm.setSelections(newSel) + }), + newlineAndIndent: cm => runInOp(cm, () => { + let sels = cm.listSelections() + for (let i = sels.length - 1; i >= 0; i--) + cm.replaceRange(cm.doc.lineSeparator(), sels[i].anchor, sels[i].head, "+input") + sels = cm.listSelections() + for (let i = 0; i < sels.length; i++) + cm.indentLine(sels[i].from().line, null, true) + ensureCursorVisible(cm) + }), + openLine: cm => cm.replaceSelection("\n", "start"), + toggleOverwrite: cm => cm.toggleOverwrite() +} + + +function lineStart(cm, lineN) { + let line = getLine(cm.doc, lineN) + let visual = visualLine(line) + if (visual != line) lineN = lineNo(visual) + return endOfLine(true, cm, visual, lineN, 1) +} +function lineEnd(cm, lineN) { + let line = getLine(cm.doc, lineN) + let visual = visualLineEnd(line) + if (visual != line) lineN = lineNo(visual) + return endOfLine(true, cm, line, lineN, -1) +} +function lineStartSmart(cm, pos) { + let start = lineStart(cm, pos.line) + let line = getLine(cm.doc, start.line) + let order = getOrder(line, cm.doc.direction) + if (!order || order[0].level == 0) { + let firstNonWS = Math.max(0, line.text.search(/\S/)) + let inWS = pos.line == start.line && pos.ch <= firstNonWS && pos.ch + return Pos(start.line, inWS ? 0 : firstNonWS, start.sticky) + } + return start +} diff --git a/docs/js/node_modules/codemirror/src/edit/deleteNearSelection.js b/docs/js/node_modules/codemirror/src/edit/deleteNearSelection.js new file mode 100644 index 000000000..82e331a5f --- /dev/null +++ b/docs/js/node_modules/codemirror/src/edit/deleteNearSelection.js @@ -0,0 +1,30 @@ +import { runInOp } from "../display/operations.js" +import { ensureCursorVisible } from "../display/scrolling.js" +import { cmp } from "../line/pos.js" +import { replaceRange } from "../model/changes.js" +import { lst } from "../util/misc.js" + +// Helper for deleting text near the selection(s), used to implement +// backspace, delete, and similar functionality. +export function deleteNearSelection(cm, compute) { + let ranges = cm.doc.sel.ranges, kill = [] + // Build up a set of ranges to kill first, merging overlapping + // ranges. + for (let i = 0; i < ranges.length; i++) { + let toKill = compute(ranges[i]) + while (kill.length && cmp(toKill.from, lst(kill).to) <= 0) { + let replaced = kill.pop() + if (cmp(replaced.from, toKill.from) < 0) { + toKill.from = replaced.from + break + } + } + kill.push(toKill) + } + // Next, remove those actual ranges. + runInOp(cm, () => { + for (let i = kill.length - 1; i >= 0; i--) + replaceRange(cm.doc, "", kill[i].from, kill[i].to, "+delete") + ensureCursorVisible(cm) + }) +} diff --git a/docs/js/node_modules/codemirror/src/edit/drop_events.js b/docs/js/node_modules/codemirror/src/edit/drop_events.js new file mode 100644 index 000000000..12c760f0d --- /dev/null +++ b/docs/js/node_modules/codemirror/src/edit/drop_events.js @@ -0,0 +1,119 @@ +import { drawSelectionCursor } from "../display/selection.js" +import { operation } from "../display/operations.js" +import { clipPos } from "../line/pos.js" +import { posFromMouse } from "../measurement/position_measurement.js" +import { eventInWidget } from "../measurement/widgets.js" +import { makeChange, replaceRange } from "../model/changes.js" +import { changeEnd } from "../model/change_measurement.js" +import { simpleSelection } from "../model/selection.js" +import { setSelectionNoUndo, setSelectionReplaceHistory } from "../model/selection_updates.js" +import { ie, presto, safari } from "../util/browser.js" +import { elt, removeChildrenAndAdd } from "../util/dom.js" +import { e_preventDefault, e_stop, signalDOMEvent } from "../util/event.js" +import { indexOf } from "../util/misc.js" + +// Kludge to work around strange IE behavior where it'll sometimes +// re-fire a series of drag-related events right after the drop (#1551) +let lastDrop = 0 + +export function onDrop(e) { + let cm = this + clearDragCursor(cm) + if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e)) + return + e_preventDefault(e) + if (ie) lastDrop = +new Date + let pos = posFromMouse(cm, e, true), files = e.dataTransfer.files + if (!pos || cm.isReadOnly()) return + // Might be a file drop, in which case we simply extract the text + // and insert it. + if (files && files.length && window.FileReader && window.File) { + let n = files.length, text = Array(n), read = 0 + let loadFile = (file, i) => { + if (cm.options.allowDropFileTypes && + indexOf(cm.options.allowDropFileTypes, file.type) == -1) + return + + let reader = new FileReader + reader.onload = operation(cm, () => { + let content = reader.result + if (/[\x00-\x08\x0e-\x1f]{2}/.test(content)) content = "" + text[i] = content + if (++read == n) { + pos = clipPos(cm.doc, pos) + let change = {from: pos, to: pos, + text: cm.doc.splitLines(text.join(cm.doc.lineSeparator())), + origin: "paste"} + makeChange(cm.doc, change) + setSelectionReplaceHistory(cm.doc, simpleSelection(pos, changeEnd(change))) + } + }) + reader.readAsText(file) + } + for (let i = 0; i < n; ++i) loadFile(files[i], i) + } else { // Normal drop + // Don't do a replace if the drop happened inside of the selected text. + if (cm.state.draggingText && cm.doc.sel.contains(pos) > -1) { + cm.state.draggingText(e) + // Ensure the editor is re-focused + setTimeout(() => cm.display.input.focus(), 20) + return + } + try { + let text = e.dataTransfer.getData("Text") + if (text) { + let selected + if (cm.state.draggingText && !cm.state.draggingText.copy) + selected = cm.listSelections() + setSelectionNoUndo(cm.doc, simpleSelection(pos, pos)) + if (selected) for (let i = 0; i < selected.length; ++i) + replaceRange(cm.doc, "", selected[i].anchor, selected[i].head, "drag") + cm.replaceSelection(text, "around", "paste") + cm.display.input.focus() + } + } + catch(e){} + } +} + +export function onDragStart(cm, e) { + if (ie && (!cm.state.draggingText || +new Date - lastDrop < 100)) { e_stop(e); return } + if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e)) return + + e.dataTransfer.setData("Text", cm.getSelection()) + e.dataTransfer.effectAllowed = "copyMove" + + // Use dummy image instead of default browsers image. + // Recent Safari (~6.0.2) have a tendency to segfault when this happens, so we don't do it there. + if (e.dataTransfer.setDragImage && !safari) { + let img = elt("img", null, null, "position: fixed; left: 0; top: 0;") + img.src = "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" + if (presto) { + img.width = img.height = 1 + cm.display.wrapper.appendChild(img) + // Force a relayout, or Opera won't use our image for some obscure reason + img._top = img.offsetTop + } + e.dataTransfer.setDragImage(img, 0, 0) + if (presto) img.parentNode.removeChild(img) + } +} + +export function onDragOver(cm, e) { + let pos = posFromMouse(cm, e) + if (!pos) return + let frag = document.createDocumentFragment() + drawSelectionCursor(cm, pos, frag) + if (!cm.display.dragCursor) { + cm.display.dragCursor = elt("div", null, "CodeMirror-cursors CodeMirror-dragcursors") + cm.display.lineSpace.insertBefore(cm.display.dragCursor, cm.display.cursorDiv) + } + removeChildrenAndAdd(cm.display.dragCursor, frag) +} + +export function clearDragCursor(cm) { + if (cm.display.dragCursor) { + cm.display.lineSpace.removeChild(cm.display.dragCursor) + cm.display.dragCursor = null + } +} diff --git a/docs/js/node_modules/codemirror/src/edit/fromTextArea.js b/docs/js/node_modules/codemirror/src/edit/fromTextArea.js new file mode 100644 index 000000000..35024c5e2 --- /dev/null +++ b/docs/js/node_modules/codemirror/src/edit/fromTextArea.js @@ -0,0 +1,61 @@ +import { CodeMirror } from "./CodeMirror.js" +import { activeElt } from "../util/dom.js" +import { off, on } from "../util/event.js" +import { copyObj } from "../util/misc.js" + +export function fromTextArea(textarea, options) { + options = options ? copyObj(options) : {} + options.value = textarea.value + if (!options.tabindex && textarea.tabIndex) + options.tabindex = textarea.tabIndex + if (!options.placeholder && textarea.placeholder) + options.placeholder = textarea.placeholder + // Set autofocus to true if this textarea is focused, or if it has + // autofocus and no other element is focused. + if (options.autofocus == null) { + let hasFocus = activeElt() + options.autofocus = hasFocus == textarea || + textarea.getAttribute("autofocus") != null && hasFocus == document.body + } + + function save() {textarea.value = cm.getValue()} + + let realSubmit + if (textarea.form) { + on(textarea.form, "submit", save) + // Deplorable hack to make the submit method do the right thing. + if (!options.leaveSubmitMethodAlone) { + let form = textarea.form + realSubmit = form.submit + try { + let wrappedSubmit = form.submit = () => { + save() + form.submit = realSubmit + form.submit() + form.submit = wrappedSubmit + } + } catch(e) {} + } + } + + options.finishInit = cm => { + cm.save = save + cm.getTextArea = () => textarea + cm.toTextArea = () => { + cm.toTextArea = isNaN // Prevent this from being ran twice + save() + textarea.parentNode.removeChild(cm.getWrapperElement()) + textarea.style.display = "" + if (textarea.form) { + off(textarea.form, "submit", save) + if (!options.leaveSubmitMethodAlone && typeof textarea.form.submit == "function") + textarea.form.submit = realSubmit + } + } + } + + textarea.style.display = "none" + let cm = CodeMirror(node => textarea.parentNode.insertBefore(node, textarea.nextSibling), + options) + return cm +} diff --git a/docs/js/node_modules/codemirror/src/edit/global_events.js b/docs/js/node_modules/codemirror/src/edit/global_events.js new file mode 100644 index 000000000..d03da2d08 --- /dev/null +++ b/docs/js/node_modules/codemirror/src/edit/global_events.js @@ -0,0 +1,45 @@ +import { onBlur } from "../display/focus.js" +import { on } from "../util/event.js" + +// These must be handled carefully, because naively registering a +// handler for each editor will cause the editors to never be +// garbage collected. + +function forEachCodeMirror(f) { + if (!document.getElementsByClassName) return + let byClass = document.getElementsByClassName("CodeMirror"), editors = [] + for (let i = 0; i < byClass.length; i++) { + let cm = byClass[i].CodeMirror + if (cm) editors.push(cm) + } + if (editors.length) editors[0].operation(() => { + for (let i = 0; i < editors.length; i++) f(editors[i]) + }) +} + +let globalsRegistered = false +export function ensureGlobalHandlers() { + if (globalsRegistered) return + registerGlobalHandlers() + globalsRegistered = true +} +function registerGlobalHandlers() { + // When the window resizes, we need to refresh active editors. + let resizeTimer + on(window, "resize", () => { + if (resizeTimer == null) resizeTimer = setTimeout(() => { + resizeTimer = null + forEachCodeMirror(onResize) + }, 100) + }) + // When the window loses focus, we want to show the editor as blurred + on(window, "blur", () => forEachCodeMirror(onBlur)) +} +// Called when the window resizes +function onResize(cm) { + let d = cm.display + // Might be a text scaling operation, clear size caches. + d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null + d.scrollbarsClipped = false + cm.setSize() +} diff --git a/docs/js/node_modules/codemirror/src/edit/key_events.js b/docs/js/node_modules/codemirror/src/edit/key_events.js new file mode 100644 index 000000000..f0521d070 --- /dev/null +++ b/docs/js/node_modules/codemirror/src/edit/key_events.js @@ -0,0 +1,159 @@ +import { signalLater } from "../util/operation_group.js" +import { restartBlink } from "../display/selection.js" +import { isModifierKey, keyName, lookupKey } from "../input/keymap.js" +import { eventInWidget } from "../measurement/widgets.js" +import { ie, ie_version, mac, presto } from "../util/browser.js" +import { activeElt, addClass, rmClass } from "../util/dom.js" +import { e_preventDefault, off, on, signalDOMEvent } from "../util/event.js" +import { hasCopyEvent } from "../util/feature_detection.js" +import { Delayed, Pass } from "../util/misc.js" + +import { commands } from "./commands.js" + +// Run a handler that was bound to a key. +function doHandleBinding(cm, bound, dropShift) { + if (typeof bound == "string") { + bound = commands[bound] + if (!bound) return false + } + // Ensure previous input has been read, so that the handler sees a + // consistent view of the document + cm.display.input.ensurePolled() + let prevShift = cm.display.shift, done = false + try { + if (cm.isReadOnly()) cm.state.suppressEdits = true + if (dropShift) cm.display.shift = false + done = bound(cm) != Pass + } finally { + cm.display.shift = prevShift + cm.state.suppressEdits = false + } + return done +} + +function lookupKeyForEditor(cm, name, handle) { + for (let i = 0; i < cm.state.keyMaps.length; i++) { + let result = lookupKey(name, cm.state.keyMaps[i], handle, cm) + if (result) return result + } + return (cm.options.extraKeys && lookupKey(name, cm.options.extraKeys, handle, cm)) + || lookupKey(name, cm.options.keyMap, handle, cm) +} + +// Note that, despite the name, this function is also used to check +// for bound mouse clicks. + +let stopSeq = new Delayed + +export function dispatchKey(cm, name, e, handle) { + let seq = cm.state.keySeq + if (seq) { + if (isModifierKey(name)) return "handled" + if (/\'$/.test(name)) + cm.state.keySeq = null + else + stopSeq.set(50, () => { + if (cm.state.keySeq == seq) { + cm.state.keySeq = null + cm.display.input.reset() + } + }) + if (dispatchKeyInner(cm, seq + " " + name, e, handle)) return true + } + return dispatchKeyInner(cm, name, e, handle) +} + +function dispatchKeyInner(cm, name, e, handle) { + let result = lookupKeyForEditor(cm, name, handle) + + if (result == "multi") + cm.state.keySeq = name + if (result == "handled") + signalLater(cm, "keyHandled", cm, name, e) + + if (result == "handled" || result == "multi") { + e_preventDefault(e) + restartBlink(cm) + } + + return !!result +} + +// Handle a key from the keydown event. +function handleKeyBinding(cm, e) { + let name = keyName(e, true) + if (!name) return false + + if (e.shiftKey && !cm.state.keySeq) { + // First try to resolve full name (including 'Shift-'). Failing + // that, see if there is a cursor-motion command (starting with + // 'go') bound to the keyname without 'Shift-'. + return dispatchKey(cm, "Shift-" + name, e, b => doHandleBinding(cm, b, true)) + || dispatchKey(cm, name, e, b => { + if (typeof b == "string" ? /^go[A-Z]/.test(b) : b.motion) + return doHandleBinding(cm, b) + }) + } else { + return dispatchKey(cm, name, e, b => doHandleBinding(cm, b)) + } +} + +// Handle a key from the keypress event +function handleCharBinding(cm, e, ch) { + return dispatchKey(cm, "'" + ch + "'", e, b => doHandleBinding(cm, b, true)) +} + +let lastStoppedKey = null +export function onKeyDown(e) { + let cm = this + cm.curOp.focus = activeElt() + if (signalDOMEvent(cm, e)) return + // IE does strange things with escape. + if (ie && ie_version < 11 && e.keyCode == 27) e.returnValue = false + let code = e.keyCode + cm.display.shift = code == 16 || e.shiftKey + let handled = handleKeyBinding(cm, e) + if (presto) { + lastStoppedKey = handled ? code : null + // Opera has no cut event... we try to at least catch the key combo + if (!handled && code == 88 && !hasCopyEvent && (mac ? e.metaKey : e.ctrlKey)) + cm.replaceSelection("", null, "cut") + } + + // Turn mouse into crosshair when Alt is held on Mac. + if (code == 18 && !/\bCodeMirror-crosshair\b/.test(cm.display.lineDiv.className)) + showCrossHair(cm) +} + +function showCrossHair(cm) { + let lineDiv = cm.display.lineDiv + addClass(lineDiv, "CodeMirror-crosshair") + + function up(e) { + if (e.keyCode == 18 || !e.altKey) { + rmClass(lineDiv, "CodeMirror-crosshair") + off(document, "keyup", up) + off(document, "mouseover", up) + } + } + on(document, "keyup", up) + on(document, "mouseover", up) +} + +export function onKeyUp(e) { + if (e.keyCode == 16) this.doc.sel.shift = false + signalDOMEvent(this, e) +} + +export function onKeyPress(e) { + let cm = this + if (eventInWidget(cm.display, e) || signalDOMEvent(cm, e) || e.ctrlKey && !e.altKey || mac && e.metaKey) return + let keyCode = e.keyCode, charCode = e.charCode + if (presto && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return} + if ((presto && (!e.which || e.which < 10)) && handleKeyBinding(cm, e)) return + let ch = String.fromCharCode(charCode == null ? keyCode : charCode) + // Some browsers fire keypress events for backspace + if (ch == "\x08") return + if (handleCharBinding(cm, e, ch)) return + cm.display.input.onKeyPress(e) +} diff --git a/docs/js/node_modules/codemirror/src/edit/legacy.js b/docs/js/node_modules/codemirror/src/edit/legacy.js new file mode 100644 index 000000000..889badbe5 --- /dev/null +++ b/docs/js/node_modules/codemirror/src/edit/legacy.js @@ -0,0 +1,62 @@ +import { scrollbarModel } from "../display/scrollbars.js" +import { wheelEventPixels } from "../display/scroll_events.js" +import { keyMap, keyName, isModifierKey, lookupKey, normalizeKeyMap } from "../input/keymap.js" +import { keyNames } from "../input/keynames.js" +import { Line } from "../line/line_data.js" +import { cmp, Pos } from "../line/pos.js" +import { changeEnd } from "../model/change_measurement.js" +import Doc from "../model/Doc.js" +import { LineWidget } from "../model/line_widget.js" +import { SharedTextMarker, TextMarker } from "../model/mark_text.js" +import { copyState, extendMode, getMode, innerMode, mimeModes, modeExtensions, modes, resolveMode, startState } from "../modes.js" +import { addClass, contains, rmClass } from "../util/dom.js" +import { e_preventDefault, e_stop, e_stopPropagation, off, on, signal } from "../util/event.js" +import { splitLinesAuto } from "../util/feature_detection.js" +import { countColumn, findColumn, isWordCharBasic, Pass } from "../util/misc.js" +import StringStream from "../util/StringStream.js" + +import { commands } from "./commands.js" + +export function addLegacyProps(CodeMirror) { + CodeMirror.off = off + CodeMirror.on = on + CodeMirror.wheelEventPixels = wheelEventPixels + CodeMirror.Doc = Doc + CodeMirror.splitLines = splitLinesAuto + CodeMirror.countColumn = countColumn + CodeMirror.findColumn = findColumn + CodeMirror.isWordChar = isWordCharBasic + CodeMirror.Pass = Pass + CodeMirror.signal = signal + CodeMirror.Line = Line + CodeMirror.changeEnd = changeEnd + CodeMirror.scrollbarModel = scrollbarModel + CodeMirror.Pos = Pos + CodeMirror.cmpPos = cmp + CodeMirror.modes = modes + CodeMirror.mimeModes = mimeModes + CodeMirror.resolveMode = resolveMode + CodeMirror.getMode = getMode + CodeMirror.modeExtensions = modeExtensions + CodeMirror.extendMode = extendMode + CodeMirror.copyState = copyState + CodeMirror.startState = startState + CodeMirror.innerMode = innerMode + CodeMirror.commands = commands + CodeMirror.keyMap = keyMap + CodeMirror.keyName = keyName + CodeMirror.isModifierKey = isModifierKey + CodeMirror.lookupKey = lookupKey + CodeMirror.normalizeKeyMap = normalizeKeyMap + CodeMirror.StringStream = StringStream + CodeMirror.SharedTextMarker = SharedTextMarker + CodeMirror.TextMarker = TextMarker + CodeMirror.LineWidget = LineWidget + CodeMirror.e_preventDefault = e_preventDefault + CodeMirror.e_stopPropagation = e_stopPropagation + CodeMirror.e_stop = e_stop + CodeMirror.addClass = addClass + CodeMirror.contains = contains + CodeMirror.rmClass = rmClass + CodeMirror.keyNames = keyNames +} diff --git a/docs/js/node_modules/codemirror/src/edit/main.js b/docs/js/node_modules/codemirror/src/edit/main.js new file mode 100644 index 000000000..9b491152c --- /dev/null +++ b/docs/js/node_modules/codemirror/src/edit/main.js @@ -0,0 +1,69 @@ +// EDITOR CONSTRUCTOR + +import { CodeMirror } from "./CodeMirror.js" +export { CodeMirror } from "./CodeMirror.js" + +import { eventMixin } from "../util/event.js" +import { indexOf } from "../util/misc.js" + +import { defineOptions } from "./options.js" + +defineOptions(CodeMirror) + +import addEditorMethods from "./methods.js" + +addEditorMethods(CodeMirror) + +import Doc from "../model/Doc.js" + +// Set up methods on CodeMirror's prototype to redirect to the editor's document. +let dontDelegate = "iter insert remove copy getEditor constructor".split(" ") +for (let prop in Doc.prototype) if (Doc.prototype.hasOwnProperty(prop) && indexOf(dontDelegate, prop) < 0) + CodeMirror.prototype[prop] = (function(method) { + return function() {return method.apply(this.doc, arguments)} + })(Doc.prototype[prop]) + +eventMixin(Doc) + +// INPUT HANDLING + +import ContentEditableInput from "../input/ContentEditableInput.js" +import TextareaInput from "../input/TextareaInput.js" +CodeMirror.inputStyles = {"textarea": TextareaInput, "contenteditable": ContentEditableInput} + +// MODE DEFINITION AND QUERYING + +import { defineMIME, defineMode } from "../modes.js" + +// Extra arguments are stored as the mode's dependencies, which is +// used by (legacy) mechanisms like loadmode.js to automatically +// load a mode. (Preferred mechanism is the require/define calls.) +CodeMirror.defineMode = function(name/*, mode, …*/) { + if (!CodeMirror.defaults.mode && name != "null") CodeMirror.defaults.mode = name + defineMode.apply(this, arguments) +} + +CodeMirror.defineMIME = defineMIME + +// Minimal default mode. +CodeMirror.defineMode("null", () => ({token: stream => stream.skipToEnd()})) +CodeMirror.defineMIME("text/plain", "null") + +// EXTENSIONS + +CodeMirror.defineExtension = (name, func) => { + CodeMirror.prototype[name] = func +} +CodeMirror.defineDocExtension = (name, func) => { + Doc.prototype[name] = func +} + +import { fromTextArea } from "./fromTextArea.js" + +CodeMirror.fromTextArea = fromTextArea + +import { addLegacyProps } from "./legacy.js" + +addLegacyProps(CodeMirror) + +CodeMirror.version = "5.49.2" diff --git a/docs/js/node_modules/codemirror/src/edit/methods.js b/docs/js/node_modules/codemirror/src/edit/methods.js new file mode 100644 index 000000000..e40012e75 --- /dev/null +++ b/docs/js/node_modules/codemirror/src/edit/methods.js @@ -0,0 +1,546 @@ +import { deleteNearSelection } from "./deleteNearSelection.js" +import { commands } from "./commands.js" +import { attachDoc } from "../model/document_data.js" +import { activeElt, addClass, rmClass } from "../util/dom.js" +import { eventMixin, signal } from "../util/event.js" +import { getLineStyles, getContextBefore, takeToken } from "../line/highlight.js" +import { indentLine } from "../input/indent.js" +import { triggerElectric } from "../input/input.js" +import { onKeyDown, onKeyPress, onKeyUp } from "./key_events.js" +import { onMouseDown } from "./mouse_events.js" +import { getKeyMap } from "../input/keymap.js" +import { endOfLine, moveLogically, moveVisually } from "../input/movement.js" +import { endOperation, methodOp, operation, runInOp, startOperation } from "../display/operations.js" +import { clipLine, clipPos, equalCursorPos, Pos } from "../line/pos.js" +import { charCoords, charWidth, clearCaches, clearLineMeasurementCache, coordsChar, cursorCoords, displayHeight, displayWidth, estimateLineHeights, fromCoordSystem, intoCoordSystem, scrollGap, textHeight } from "../measurement/position_measurement.js" +import { Range } from "../model/selection.js" +import { replaceOneSelection, skipAtomic } from "../model/selection_updates.js" +import { addToScrollTop, ensureCursorVisible, scrollIntoView, scrollToCoords, scrollToCoordsRange, scrollToRange } from "../display/scrolling.js" +import { heightAtLine } from "../line/spans.js" +import { updateGutterSpace } from "../display/update_display.js" +import { indexOf, insertSorted, isWordChar, sel_dontScroll, sel_move } from "../util/misc.js" +import { signalLater } from "../util/operation_group.js" +import { getLine, isLine, lineAtHeight } from "../line/utils_line.js" +import { regChange, regLineChange } from "../display/view_tracking.js" + +// The publicly visible API. Note that methodOp(f) means +// 'wrap f in an operation, performed on its `this` parameter'. + +// This is not the complete set of editor methods. Most of the +// methods defined on the Doc type are also injected into +// CodeMirror.prototype, for backwards compatibility and +// convenience. + +export default function(CodeMirror) { + let optionHandlers = CodeMirror.optionHandlers + + let helpers = CodeMirror.helpers = {} + + CodeMirror.prototype = { + constructor: CodeMirror, + focus: function(){window.focus(); this.display.input.focus()}, + + setOption: function(option, value) { + let options = this.options, old = options[option] + if (options[option] == value && option != "mode") return + options[option] = value + if (optionHandlers.hasOwnProperty(option)) + operation(this, optionHandlers[option])(this, value, old) + signal(this, "optionChange", this, option) + }, + + getOption: function(option) {return this.options[option]}, + getDoc: function() {return this.doc}, + + addKeyMap: function(map, bottom) { + this.state.keyMaps[bottom ? "push" : "unshift"](getKeyMap(map)) + }, + removeKeyMap: function(map) { + let maps = this.state.keyMaps + for (let i = 0; i < maps.length; ++i) + if (maps[i] == map || maps[i].name == map) { + maps.splice(i, 1) + return true + } + }, + + addOverlay: methodOp(function(spec, options) { + let mode = spec.token ? spec : CodeMirror.getMode(this.options, spec) + if (mode.startState) throw new Error("Overlays may not be stateful.") + insertSorted(this.state.overlays, + {mode: mode, modeSpec: spec, opaque: options && options.opaque, + priority: (options && options.priority) || 0}, + overlay => overlay.priority) + this.state.modeGen++ + regChange(this) + }), + removeOverlay: methodOp(function(spec) { + let overlays = this.state.overlays + for (let i = 0; i < overlays.length; ++i) { + let cur = overlays[i].modeSpec + if (cur == spec || typeof spec == "string" && cur.name == spec) { + overlays.splice(i, 1) + this.state.modeGen++ + regChange(this) + return + } + } + }), + + indentLine: methodOp(function(n, dir, aggressive) { + if (typeof dir != "string" && typeof dir != "number") { + if (dir == null) dir = this.options.smartIndent ? "smart" : "prev" + else dir = dir ? "add" : "subtract" + } + if (isLine(this.doc, n)) indentLine(this, n, dir, aggressive) + }), + indentSelection: methodOp(function(how) { + let ranges = this.doc.sel.ranges, end = -1 + for (let i = 0; i < ranges.length; i++) { + let range = ranges[i] + if (!range.empty()) { + let from = range.from(), to = range.to() + let start = Math.max(end, from.line) + end = Math.min(this.lastLine(), to.line - (to.ch ? 0 : 1)) + 1 + for (let j = start; j < end; ++j) + indentLine(this, j, how) + let newRanges = this.doc.sel.ranges + if (from.ch == 0 && ranges.length == newRanges.length && newRanges[i].from().ch > 0) + replaceOneSelection(this.doc, i, new Range(from, newRanges[i].to()), sel_dontScroll) + } else if (range.head.line > end) { + indentLine(this, range.head.line, how, true) + end = range.head.line + if (i == this.doc.sel.primIndex) ensureCursorVisible(this) + } + } + }), + + // Fetch the parser token for a given character. Useful for hacks + // that want to inspect the mode state (say, for completion). + getTokenAt: function(pos, precise) { + return takeToken(this, pos, precise) + }, + + getLineTokens: function(line, precise) { + return takeToken(this, Pos(line), precise, true) + }, + + getTokenTypeAt: function(pos) { + pos = clipPos(this.doc, pos) + let styles = getLineStyles(this, getLine(this.doc, pos.line)) + let before = 0, after = (styles.length - 1) / 2, ch = pos.ch + let type + if (ch == 0) type = styles[2] + else for (;;) { + let mid = (before + after) >> 1 + if ((mid ? styles[mid * 2 - 1] : 0) >= ch) after = mid + else if (styles[mid * 2 + 1] < ch) before = mid + 1 + else { type = styles[mid * 2 + 2]; break } + } + let cut = type ? type.indexOf("overlay ") : -1 + return cut < 0 ? type : cut == 0 ? null : type.slice(0, cut - 1) + }, + + getModeAt: function(pos) { + let mode = this.doc.mode + if (!mode.innerMode) return mode + return CodeMirror.innerMode(mode, this.getTokenAt(pos).state).mode + }, + + getHelper: function(pos, type) { + return this.getHelpers(pos, type)[0] + }, + + getHelpers: function(pos, type) { + let found = [] + if (!helpers.hasOwnProperty(type)) return found + let help = helpers[type], mode = this.getModeAt(pos) + if (typeof mode[type] == "string") { + if (help[mode[type]]) found.push(help[mode[type]]) + } else if (mode[type]) { + for (let i = 0; i < mode[type].length; i++) { + let val = help[mode[type][i]] + if (val) found.push(val) + } + } else if (mode.helperType && help[mode.helperType]) { + found.push(help[mode.helperType]) + } else if (help[mode.name]) { + found.push(help[mode.name]) + } + for (let i = 0; i < help._global.length; i++) { + let cur = help._global[i] + if (cur.pred(mode, this) && indexOf(found, cur.val) == -1) + found.push(cur.val) + } + return found + }, + + getStateAfter: function(line, precise) { + let doc = this.doc + line = clipLine(doc, line == null ? doc.first + doc.size - 1: line) + return getContextBefore(this, line + 1, precise).state + }, + + cursorCoords: function(start, mode) { + let pos, range = this.doc.sel.primary() + if (start == null) pos = range.head + else if (typeof start == "object") pos = clipPos(this.doc, start) + else pos = start ? range.from() : range.to() + return cursorCoords(this, pos, mode || "page") + }, + + charCoords: function(pos, mode) { + return charCoords(this, clipPos(this.doc, pos), mode || "page") + }, + + coordsChar: function(coords, mode) { + coords = fromCoordSystem(this, coords, mode || "page") + return coordsChar(this, coords.left, coords.top) + }, + + lineAtHeight: function(height, mode) { + height = fromCoordSystem(this, {top: height, left: 0}, mode || "page").top + return lineAtHeight(this.doc, height + this.display.viewOffset) + }, + heightAtLine: function(line, mode, includeWidgets) { + let end = false, lineObj + if (typeof line == "number") { + let last = this.doc.first + this.doc.size - 1 + if (line < this.doc.first) line = this.doc.first + else if (line > last) { line = last; end = true } + lineObj = getLine(this.doc, line) + } else { + lineObj = line + } + return intoCoordSystem(this, lineObj, {top: 0, left: 0}, mode || "page", includeWidgets || end).top + + (end ? this.doc.height - heightAtLine(lineObj) : 0) + }, + + defaultTextHeight: function() { return textHeight(this.display) }, + defaultCharWidth: function() { return charWidth(this.display) }, + + getViewport: function() { return {from: this.display.viewFrom, to: this.display.viewTo}}, + + addWidget: function(pos, node, scroll, vert, horiz) { + let display = this.display + pos = cursorCoords(this, clipPos(this.doc, pos)) + let top = pos.bottom, left = pos.left + node.style.position = "absolute" + node.setAttribute("cm-ignore-events", "true") + this.display.input.setUneditable(node) + display.sizer.appendChild(node) + if (vert == "over") { + top = pos.top + } else if (vert == "above" || vert == "near") { + let vspace = Math.max(display.wrapper.clientHeight, this.doc.height), + hspace = Math.max(display.sizer.clientWidth, display.lineSpace.clientWidth) + // Default to positioning above (if specified and possible); otherwise default to positioning below + if ((vert == 'above' || pos.bottom + node.offsetHeight > vspace) && pos.top > node.offsetHeight) + top = pos.top - node.offsetHeight + else if (pos.bottom + node.offsetHeight <= vspace) + top = pos.bottom + if (left + node.offsetWidth > hspace) + left = hspace - node.offsetWidth + } + node.style.top = top + "px" + node.style.left = node.style.right = "" + if (horiz == "right") { + left = display.sizer.clientWidth - node.offsetWidth + node.style.right = "0px" + } else { + if (horiz == "left") left = 0 + else if (horiz == "middle") left = (display.sizer.clientWidth - node.offsetWidth) / 2 + node.style.left = left + "px" + } + if (scroll) + scrollIntoView(this, {left, top, right: left + node.offsetWidth, bottom: top + node.offsetHeight}) + }, + + triggerOnKeyDown: methodOp(onKeyDown), + triggerOnKeyPress: methodOp(onKeyPress), + triggerOnKeyUp: onKeyUp, + triggerOnMouseDown: methodOp(onMouseDown), + + execCommand: function(cmd) { + if (commands.hasOwnProperty(cmd)) + return commands[cmd].call(null, this) + }, + + triggerElectric: methodOp(function(text) { triggerElectric(this, text) }), + + findPosH: function(from, amount, unit, visually) { + let dir = 1 + if (amount < 0) { dir = -1; amount = -amount } + let cur = clipPos(this.doc, from) + for (let i = 0; i < amount; ++i) { + cur = findPosH(this.doc, cur, dir, unit, visually) + if (cur.hitSide) break + } + return cur + }, + + moveH: methodOp(function(dir, unit) { + this.extendSelectionsBy(range => { + if (this.display.shift || this.doc.extend || range.empty()) + return findPosH(this.doc, range.head, dir, unit, this.options.rtlMoveVisually) + else + return dir < 0 ? range.from() : range.to() + }, sel_move) + }), + + deleteH: methodOp(function(dir, unit) { + let sel = this.doc.sel, doc = this.doc + if (sel.somethingSelected()) + doc.replaceSelection("", null, "+delete") + else + deleteNearSelection(this, range => { + let other = findPosH(doc, range.head, dir, unit, false) + return dir < 0 ? {from: other, to: range.head} : {from: range.head, to: other} + }) + }), + + findPosV: function(from, amount, unit, goalColumn) { + let dir = 1, x = goalColumn + if (amount < 0) { dir = -1; amount = -amount } + let cur = clipPos(this.doc, from) + for (let i = 0; i < amount; ++i) { + let coords = cursorCoords(this, cur, "div") + if (x == null) x = coords.left + else coords.left = x + cur = findPosV(this, coords, dir, unit) + if (cur.hitSide) break + } + return cur + }, + + moveV: methodOp(function(dir, unit) { + let doc = this.doc, goals = [] + let collapse = !this.display.shift && !doc.extend && doc.sel.somethingSelected() + doc.extendSelectionsBy(range => { + if (collapse) + return dir < 0 ? range.from() : range.to() + let headPos = cursorCoords(this, range.head, "div") + if (range.goalColumn != null) headPos.left = range.goalColumn + goals.push(headPos.left) + let pos = findPosV(this, headPos, dir, unit) + if (unit == "page" && range == doc.sel.primary()) + addToScrollTop(this, charCoords(this, pos, "div").top - headPos.top) + return pos + }, sel_move) + if (goals.length) for (let i = 0; i < doc.sel.ranges.length; i++) + doc.sel.ranges[i].goalColumn = goals[i] + }), + + // Find the word at the given position (as returned by coordsChar). + findWordAt: function(pos) { + let doc = this.doc, line = getLine(doc, pos.line).text + let start = pos.ch, end = pos.ch + if (line) { + let helper = this.getHelper(pos, "wordChars") + if ((pos.sticky == "before" || end == line.length) && start) --start; else ++end + let startChar = line.charAt(start) + let check = isWordChar(startChar, helper) + ? ch => isWordChar(ch, helper) + : /\s/.test(startChar) ? ch => /\s/.test(ch) + : ch => (!/\s/.test(ch) && !isWordChar(ch)) + while (start > 0 && check(line.charAt(start - 1))) --start + while (end < line.length && check(line.charAt(end))) ++end + } + return new Range(Pos(pos.line, start), Pos(pos.line, end)) + }, + + toggleOverwrite: function(value) { + if (value != null && value == this.state.overwrite) return + if (this.state.overwrite = !this.state.overwrite) + addClass(this.display.cursorDiv, "CodeMirror-overwrite") + else + rmClass(this.display.cursorDiv, "CodeMirror-overwrite") + + signal(this, "overwriteToggle", this, this.state.overwrite) + }, + hasFocus: function() { return this.display.input.getField() == activeElt() }, + isReadOnly: function() { return !!(this.options.readOnly || this.doc.cantEdit) }, + + scrollTo: methodOp(function (x, y) { scrollToCoords(this, x, y) }), + getScrollInfo: function() { + let scroller = this.display.scroller + return {left: scroller.scrollLeft, top: scroller.scrollTop, + height: scroller.scrollHeight - scrollGap(this) - this.display.barHeight, + width: scroller.scrollWidth - scrollGap(this) - this.display.barWidth, + clientHeight: displayHeight(this), clientWidth: displayWidth(this)} + }, + + scrollIntoView: methodOp(function(range, margin) { + if (range == null) { + range = {from: this.doc.sel.primary().head, to: null} + if (margin == null) margin = this.options.cursorScrollMargin + } else if (typeof range == "number") { + range = {from: Pos(range, 0), to: null} + } else if (range.from == null) { + range = {from: range, to: null} + } + if (!range.to) range.to = range.from + range.margin = margin || 0 + + if (range.from.line != null) { + scrollToRange(this, range) + } else { + scrollToCoordsRange(this, range.from, range.to, range.margin) + } + }), + + setSize: methodOp(function(width, height) { + let interpret = val => typeof val == "number" || /^\d+$/.test(String(val)) ? val + "px" : val + if (width != null) this.display.wrapper.style.width = interpret(width) + if (height != null) this.display.wrapper.style.height = interpret(height) + if (this.options.lineWrapping) clearLineMeasurementCache(this) + let lineNo = this.display.viewFrom + this.doc.iter(lineNo, this.display.viewTo, line => { + if (line.widgets) for (let i = 0; i < line.widgets.length; i++) + if (line.widgets[i].noHScroll) { regLineChange(this, lineNo, "widget"); break } + ++lineNo + }) + this.curOp.forceUpdate = true + signal(this, "refresh", this) + }), + + operation: function(f){return runInOp(this, f)}, + startOperation: function(){return startOperation(this)}, + endOperation: function(){return endOperation(this)}, + + refresh: methodOp(function() { + let oldHeight = this.display.cachedTextHeight + regChange(this) + this.curOp.forceUpdate = true + clearCaches(this) + scrollToCoords(this, this.doc.scrollLeft, this.doc.scrollTop) + updateGutterSpace(this.display) + if (oldHeight == null || Math.abs(oldHeight - textHeight(this.display)) > .5) + estimateLineHeights(this) + signal(this, "refresh", this) + }), + + swapDoc: methodOp(function(doc) { + let old = this.doc + old.cm = null + // Cancel the current text selection if any (#5821) + if (this.state.selectingText) this.state.selectingText() + attachDoc(this, doc) + clearCaches(this) + this.display.input.reset() + scrollToCoords(this, doc.scrollLeft, doc.scrollTop) + this.curOp.forceScroll = true + signalLater(this, "swapDoc", this, old) + return old + }), + + phrase: function(phraseText) { + let phrases = this.options.phrases + return phrases && Object.prototype.hasOwnProperty.call(phrases, phraseText) ? phrases[phraseText] : phraseText + }, + + getInputField: function(){return this.display.input.getField()}, + getWrapperElement: function(){return this.display.wrapper}, + getScrollerElement: function(){return this.display.scroller}, + getGutterElement: function(){return this.display.gutters} + } + eventMixin(CodeMirror) + + CodeMirror.registerHelper = function(type, name, value) { + if (!helpers.hasOwnProperty(type)) helpers[type] = CodeMirror[type] = {_global: []} + helpers[type][name] = value + } + CodeMirror.registerGlobalHelper = function(type, name, predicate, value) { + CodeMirror.registerHelper(type, name, value) + helpers[type]._global.push({pred: predicate, val: value}) + } +} + +// Used for horizontal relative motion. Dir is -1 or 1 (left or +// right), unit can be "char", "column" (like char, but doesn't +// cross line boundaries), "word" (across next word), or "group" (to +// the start of next group of word or non-word-non-whitespace +// chars). The visually param controls whether, in right-to-left +// text, direction 1 means to move towards the next index in the +// string, or towards the character to the right of the current +// position. The resulting position will have a hitSide=true +// property if it reached the end of the document. +function findPosH(doc, pos, dir, unit, visually) { + let oldPos = pos + let origDir = dir + let lineObj = getLine(doc, pos.line) + function findNextLine() { + let l = pos.line + dir + if (l < doc.first || l >= doc.first + doc.size) return false + pos = new Pos(l, pos.ch, pos.sticky) + return lineObj = getLine(doc, l) + } + function moveOnce(boundToLine) { + let next + if (visually) { + next = moveVisually(doc.cm, lineObj, pos, dir) + } else { + next = moveLogically(lineObj, pos, dir) + } + if (next == null) { + if (!boundToLine && findNextLine()) + pos = endOfLine(visually, doc.cm, lineObj, pos.line, dir) + else + return false + } else { + pos = next + } + return true + } + + if (unit == "char") { + moveOnce() + } else if (unit == "column") { + moveOnce(true) + } else if (unit == "word" || unit == "group") { + let sawType = null, group = unit == "group" + let helper = doc.cm && doc.cm.getHelper(pos, "wordChars") + for (let first = true;; first = false) { + if (dir < 0 && !moveOnce(!first)) break + let cur = lineObj.text.charAt(pos.ch) || "\n" + let type = isWordChar(cur, helper) ? "w" + : group && cur == "\n" ? "n" + : !group || /\s/.test(cur) ? null + : "p" + if (group && !first && !type) type = "s" + if (sawType && sawType != type) { + if (dir < 0) {dir = 1; moveOnce(); pos.sticky = "after"} + break + } + + if (type) sawType = type + if (dir > 0 && !moveOnce(!first)) break + } + } + let result = skipAtomic(doc, pos, oldPos, origDir, true) + if (equalCursorPos(oldPos, result)) result.hitSide = true + return result +} + +// For relative vertical movement. Dir may be -1 or 1. Unit can be +// "page" or "line". The resulting position will have a hitSide=true +// property if it reached the end of the document. +function findPosV(cm, pos, dir, unit) { + let doc = cm.doc, x = pos.left, y + if (unit == "page") { + let pageSize = Math.min(cm.display.wrapper.clientHeight, window.innerHeight || document.documentElement.clientHeight) + let moveAmount = Math.max(pageSize - .5 * textHeight(cm.display), 3) + y = (dir > 0 ? pos.bottom : pos.top) + dir * moveAmount + + } else if (unit == "line") { + y = dir > 0 ? pos.bottom + 3 : pos.top - 3 + } + let target + for (;;) { + target = coordsChar(cm, x, y) + if (!target.outside) break + if (dir < 0 ? y <= 0 : y >= doc.height) { target.hitSide = true; break } + y += dir * 5 + } + return target +} diff --git a/docs/js/node_modules/codemirror/src/edit/mouse_events.js b/docs/js/node_modules/codemirror/src/edit/mouse_events.js new file mode 100644 index 000000000..d0bbfba1d --- /dev/null +++ b/docs/js/node_modules/codemirror/src/edit/mouse_events.js @@ -0,0 +1,412 @@ +import { delayBlurEvent, ensureFocus } from "../display/focus.js" +import { operation } from "../display/operations.js" +import { visibleLines } from "../display/update_lines.js" +import { clipPos, cmp, maxPos, minPos, Pos } from "../line/pos.js" +import { getLine, lineAtHeight } from "../line/utils_line.js" +import { posFromMouse } from "../measurement/position_measurement.js" +import { eventInWidget } from "../measurement/widgets.js" +import { normalizeSelection, Range, Selection } from "../model/selection.js" +import { extendRange, extendSelection, replaceOneSelection, setSelection } from "../model/selection_updates.js" +import { captureRightClick, chromeOS, ie, ie_version, mac, webkit } from "../util/browser.js" +import { getOrder, getBidiPartAt } from "../util/bidi.js" +import { activeElt } from "../util/dom.js" +import { e_button, e_defaultPrevented, e_preventDefault, e_target, hasHandler, off, on, signal, signalDOMEvent } from "../util/event.js" +import { dragAndDrop } from "../util/feature_detection.js" +import { bind, countColumn, findColumn, sel_mouse } from "../util/misc.js" +import { addModifierNames } from "../input/keymap.js" +import { Pass } from "../util/misc.js" + +import { dispatchKey } from "./key_events.js" +import { commands } from "./commands.js" + +const DOUBLECLICK_DELAY = 400 + +class PastClick { + constructor(time, pos, button) { + this.time = time + this.pos = pos + this.button = button + } + + compare(time, pos, button) { + return this.time + DOUBLECLICK_DELAY > time && + cmp(pos, this.pos) == 0 && button == this.button + } +} + +let lastClick, lastDoubleClick +function clickRepeat(pos, button) { + let now = +new Date + if (lastDoubleClick && lastDoubleClick.compare(now, pos, button)) { + lastClick = lastDoubleClick = null + return "triple" + } else if (lastClick && lastClick.compare(now, pos, button)) { + lastDoubleClick = new PastClick(now, pos, button) + lastClick = null + return "double" + } else { + lastClick = new PastClick(now, pos, button) + lastDoubleClick = null + return "single" + } +} + +// 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. +export function onMouseDown(e) { + let cm = this, display = cm.display + if (signalDOMEvent(cm, e) || display.activeTouch && display.input.supportsTouch()) return + display.input.ensurePolled() + 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(() => display.scroller.draggable = true, 100) + } + return + } + if (clickInGutter(cm, e)) return + let pos = posFromMouse(cm, e), button = e_button(e), repeat = pos ? clickRepeat(pos, button) : "single" + window.focus() + + // #3261: make sure, that we're not starting a second selection + if (button == 1 && cm.state.selectingText) + cm.state.selectingText(e) + + if (pos && handleMappedButton(cm, button, pos, repeat, e)) return + + if (button == 1) { + if (pos) leftButtonDown(cm, pos, repeat, e) + else if (e_target(e) == display.scroller) e_preventDefault(e) + } else if (button == 2) { + if (pos) extendSelection(cm.doc, pos) + setTimeout(() => display.input.focus(), 20) + } else if (button == 3) { + if (captureRightClick) cm.display.input.onContextMenu(e) + else delayBlurEvent(cm) + } +} + +function handleMappedButton(cm, button, pos, repeat, event) { + let name = "Click" + if (repeat == "double") name = "Double" + name + else if (repeat == "triple") name = "Triple" + name + name = (button == 1 ? "Left" : button == 2 ? "Middle" : "Right") + name + + return dispatchKey(cm, addModifierNames(name, event), event, bound => { + if (typeof bound == "string") bound = commands[bound] + if (!bound) return false + let done = false + try { + if (cm.isReadOnly()) cm.state.suppressEdits = true + done = bound(cm, pos) != Pass + } finally { + cm.state.suppressEdits = false + } + return done + }) +} + +function configureMouse(cm, repeat, event) { + let option = cm.getOption("configureMouse") + let value = option ? option(cm, repeat, event) : {} + if (value.unit == null) { + let rect = chromeOS ? event.shiftKey && event.metaKey : event.altKey + value.unit = rect ? "rectangle" : repeat == "single" ? "char" : repeat == "double" ? "word" : "line" + } + if (value.extend == null || cm.doc.extend) value.extend = cm.doc.extend || event.shiftKey + if (value.addNew == null) value.addNew = mac ? event.metaKey : event.ctrlKey + if (value.moveOnDrag == null) value.moveOnDrag = !(mac ? event.altKey : event.ctrlKey) + return value +} + +function leftButtonDown(cm, pos, repeat, event) { + if (ie) setTimeout(bind(ensureFocus, cm), 0) + else cm.curOp.focus = activeElt() + + let behavior = configureMouse(cm, repeat, event) + + let sel = cm.doc.sel, contained + if (cm.options.dragDrop && dragAndDrop && !cm.isReadOnly() && + repeat == "single" && (contained = sel.contains(pos)) > -1 && + (cmp((contained = sel.ranges[contained]).from(), pos) < 0 || pos.xRel > 0) && + (cmp(contained.to(), pos) > 0 || pos.xRel < 0)) + leftButtonStartDrag(cm, event, pos, behavior) + else + leftButtonSelect(cm, event, pos, behavior) +} + +// Start a text drag. When it ends, see if any dragging actually +// happen, and treat as a click if it didn't. +function leftButtonStartDrag(cm, event, pos, behavior) { + let display = cm.display, moved = false + let dragEnd = operation(cm, e => { + if (webkit) display.scroller.draggable = false + cm.state.draggingText = false + off(display.wrapper.ownerDocument, "mouseup", dragEnd) + off(display.wrapper.ownerDocument, "mousemove", mouseMove) + off(display.scroller, "dragstart", dragStart) + off(display.scroller, "drop", dragEnd) + if (!moved) { + e_preventDefault(e) + if (!behavior.addNew) + extendSelection(cm.doc, pos, null, null, behavior.extend) + // Work around unexplainable focus problem in IE9 (#2127) and Chrome (#3081) + if (webkit || ie && ie_version == 9) + setTimeout(() => {display.wrapper.ownerDocument.body.focus(); display.input.focus()}, 20) + else + display.input.focus() + } + }) + let mouseMove = function(e2) { + moved = moved || Math.abs(event.clientX - e2.clientX) + Math.abs(event.clientY - e2.clientY) >= 10 + } + let dragStart = () => moved = true + // Let the drag handler handle this. + if (webkit) display.scroller.draggable = true + cm.state.draggingText = dragEnd + dragEnd.copy = !behavior.moveOnDrag + // IE's approach to draggable + if (display.scroller.dragDrop) display.scroller.dragDrop() + on(display.wrapper.ownerDocument, "mouseup", dragEnd) + on(display.wrapper.ownerDocument, "mousemove", mouseMove) + on(display.scroller, "dragstart", dragStart) + on(display.scroller, "drop", dragEnd) + + delayBlurEvent(cm) + setTimeout(() => display.input.focus(), 20) +} + +function rangeForUnit(cm, pos, unit) { + if (unit == "char") return new Range(pos, pos) + if (unit == "word") return cm.findWordAt(pos) + if (unit == "line") return new Range(Pos(pos.line, 0), clipPos(cm.doc, Pos(pos.line + 1, 0))) + let result = unit(cm, pos) + return new Range(result.from, result.to) +} + +// Normal selection, as opposed to text dragging. +function leftButtonSelect(cm, event, start, behavior) { + let display = cm.display, doc = cm.doc + e_preventDefault(event) + + let ourRange, ourIndex, startSel = doc.sel, ranges = startSel.ranges + if (behavior.addNew && !behavior.extend) { + ourIndex = doc.sel.contains(start) + if (ourIndex > -1) + ourRange = ranges[ourIndex] + else + ourRange = new Range(start, start) + } else { + ourRange = doc.sel.primary() + ourIndex = doc.sel.primIndex + } + + if (behavior.unit == "rectangle") { + if (!behavior.addNew) ourRange = new Range(start, start) + start = posFromMouse(cm, event, true, true) + ourIndex = -1 + } else { + let range = rangeForUnit(cm, start, behavior.unit) + if (behavior.extend) + ourRange = extendRange(ourRange, range.anchor, range.head, behavior.extend) + else + ourRange = range + } + + if (!behavior.addNew) { + ourIndex = 0 + setSelection(doc, new Selection([ourRange], 0), sel_mouse) + startSel = doc.sel + } else if (ourIndex == -1) { + ourIndex = ranges.length + setSelection(doc, normalizeSelection(cm, ranges.concat([ourRange]), ourIndex), + {scroll: false, origin: "*mouse"}) + } else if (ranges.length > 1 && ranges[ourIndex].empty() && behavior.unit == "char" && !behavior.extend) { + setSelection(doc, normalizeSelection(cm, ranges.slice(0, ourIndex).concat(ranges.slice(ourIndex + 1)), 0), + {scroll: false, origin: "*mouse"}) + startSel = doc.sel + } else { + replaceOneSelection(doc, ourIndex, ourRange, sel_mouse) + } + + let lastPos = start + function extendTo(pos) { + if (cmp(lastPos, pos) == 0) return + lastPos = pos + + if (behavior.unit == "rectangle") { + let ranges = [], tabSize = cm.options.tabSize + let startCol = countColumn(getLine(doc, start.line).text, start.ch, tabSize) + let posCol = countColumn(getLine(doc, pos.line).text, pos.ch, tabSize) + let left = Math.min(startCol, posCol), right = Math.max(startCol, posCol) + for (let line = Math.min(start.line, pos.line), end = Math.min(cm.lastLine(), Math.max(start.line, pos.line)); + line <= end; line++) { + let text = getLine(doc, line).text, leftPos = findColumn(text, left, tabSize) + if (left == right) + ranges.push(new Range(Pos(line, leftPos), Pos(line, leftPos))) + else if (text.length > leftPos) + ranges.push(new Range(Pos(line, leftPos), Pos(line, findColumn(text, right, tabSize)))) + } + if (!ranges.length) ranges.push(new Range(start, start)) + setSelection(doc, normalizeSelection(cm, startSel.ranges.slice(0, ourIndex).concat(ranges), ourIndex), + {origin: "*mouse", scroll: false}) + cm.scrollIntoView(pos) + } else { + let oldRange = ourRange + let range = rangeForUnit(cm, pos, behavior.unit) + let anchor = oldRange.anchor, head + if (cmp(range.anchor, anchor) > 0) { + head = range.head + anchor = minPos(oldRange.from(), range.anchor) + } else { + head = range.anchor + anchor = maxPos(oldRange.to(), range.head) + } + let ranges = startSel.ranges.slice(0) + ranges[ourIndex] = bidiSimplify(cm, new Range(clipPos(doc, anchor), head)) + setSelection(doc, normalizeSelection(cm, ranges, ourIndex), sel_mouse) + } + } + + let editorSize = display.wrapper.getBoundingClientRect() + // Used to ensure timeout re-tries don't fire when another extend + // happened in the meantime (clearTimeout isn't reliable -- at + // least on Chrome, the timeouts still happen even when cleared, + // if the clear happens after their scheduled firing time). + let counter = 0 + + function extend(e) { + let curCount = ++counter + let cur = posFromMouse(cm, e, true, behavior.unit == "rectangle") + if (!cur) return + if (cmp(cur, lastPos) != 0) { + cm.curOp.focus = activeElt() + extendTo(cur) + let visible = visibleLines(display, doc) + if (cur.line >= visible.to || cur.line < visible.from) + setTimeout(operation(cm, () => {if (counter == curCount) extend(e)}), 150) + } else { + let outside = e.clientY < editorSize.top ? -20 : e.clientY > editorSize.bottom ? 20 : 0 + if (outside) setTimeout(operation(cm, () => { + if (counter != curCount) return + display.scroller.scrollTop += outside + extend(e) + }), 50) + } + } + + function done(e) { + cm.state.selectingText = false + counter = Infinity + // If e is null or undefined we interpret this as someone trying + // to explicitly cancel the selection rather than the user + // letting go of the mouse button. + if (e) { + e_preventDefault(e) + display.input.focus() + } + off(display.wrapper.ownerDocument, "mousemove", move) + off(display.wrapper.ownerDocument, "mouseup", up) + doc.history.lastSelOrigin = null + } + + let move = operation(cm, e => { + if (e.buttons === 0 || !e_button(e)) done(e) + else extend(e) + }) + let up = operation(cm, done) + cm.state.selectingText = up + on(display.wrapper.ownerDocument, "mousemove", move) + on(display.wrapper.ownerDocument, "mouseup", up) +} + +// Used when mouse-selecting to adjust the anchor to the proper side +// of a bidi jump depending on the visual position of the head. +function bidiSimplify(cm, range) { + let {anchor, head} = range, anchorLine = getLine(cm.doc, anchor.line) + if (cmp(anchor, head) == 0 && anchor.sticky == head.sticky) return range + let order = getOrder(anchorLine) + if (!order) return range + let index = getBidiPartAt(order, anchor.ch, anchor.sticky), part = order[index] + if (part.from != anchor.ch && part.to != anchor.ch) return range + let boundary = index + ((part.from == anchor.ch) == (part.level != 1) ? 0 : 1) + if (boundary == 0 || boundary == order.length) return range + + // Compute the relative visual position of the head compared to the + // anchor (<0 is to the left, >0 to the right) + let leftSide + if (head.line != anchor.line) { + leftSide = (head.line - anchor.line) * (cm.doc.direction == "ltr" ? 1 : -1) > 0 + } else { + let headIndex = getBidiPartAt(order, head.ch, head.sticky) + let dir = headIndex - index || (head.ch - anchor.ch) * (part.level == 1 ? -1 : 1) + if (headIndex == boundary - 1 || headIndex == boundary) + leftSide = dir < 0 + else + leftSide = dir > 0 + } + + let usePart = order[boundary + (leftSide ? -1 : 0)] + let from = leftSide == (usePart.level == 1) + let ch = from ? usePart.from : usePart.to, sticky = from ? "after" : "before" + return anchor.ch == ch && anchor.sticky == sticky ? range : new Range(new Pos(anchor.line, ch, sticky), head) +} + + +// Determines whether an event happened in the gutter, and fires the +// handlers for the corresponding event. +function gutterEvent(cm, e, type, prevent) { + let mX, mY + if (e.touches) { + mX = e.touches[0].clientX + mY = e.touches[0].clientY + } else { + try { mX = e.clientX; mY = e.clientY } + catch(e) { return false } + } + if (mX >= Math.floor(cm.display.gutters.getBoundingClientRect().right)) return false + if (prevent) e_preventDefault(e) + + let display = cm.display + let lineBox = display.lineDiv.getBoundingClientRect() + + if (mY > lineBox.bottom || !hasHandler(cm, type)) return e_defaultPrevented(e) + mY -= lineBox.top - display.viewOffset + + for (let i = 0; i < cm.display.gutterSpecs.length; ++i) { + let g = display.gutters.childNodes[i] + if (g && g.getBoundingClientRect().right >= mX) { + let line = lineAtHeight(cm.doc, mY) + let gutter = cm.display.gutterSpecs[i] + signal(cm, type, cm, line, gutter.className, e) + return e_defaultPrevented(e) + } + } +} + +export function clickInGutter(cm, e) { + return gutterEvent(cm, e, "gutterClick", true) +} + +// CONTEXT MENU HANDLING + +// To make the context menu work, we need to briefly unhide the +// textarea (making it as unobtrusive as possible) to let the +// right-click take effect on it. +export function onContextMenu(cm, e) { + if (eventInWidget(cm.display, e) || contextMenuInGutter(cm, e)) return + if (signalDOMEvent(cm, e, "contextmenu")) return + if (!captureRightClick) cm.display.input.onContextMenu(e) +} + +function contextMenuInGutter(cm, e) { + if (!hasHandler(cm, "gutterContextMenu")) return false + return gutterEvent(cm, e, "gutterContextMenu", false) +} diff --git a/docs/js/node_modules/codemirror/src/edit/options.js b/docs/js/node_modules/codemirror/src/edit/options.js new file mode 100644 index 000000000..3abd3c3c1 --- /dev/null +++ b/docs/js/node_modules/codemirror/src/edit/options.js @@ -0,0 +1,188 @@ +import { onBlur } from "../display/focus.js" +import { getGutters, updateGutters } from "../display/gutters.js" +import { loadMode, resetModeState } from "../display/mode_state.js" +import { initScrollbars, updateScrollbars } from "../display/scrollbars.js" +import { updateSelection } from "../display/selection.js" +import { regChange } from "../display/view_tracking.js" +import { getKeyMap } from "../input/keymap.js" +import { defaultSpecialCharPlaceholder } from "../line/line_data.js" +import { Pos } from "../line/pos.js" +import { findMaxLine } from "../line/spans.js" +import { clearCaches, compensateForHScroll, estimateLineHeights } from "../measurement/position_measurement.js" +import { replaceRange } from "../model/changes.js" +import { mobile, windows } from "../util/browser.js" +import { addClass, rmClass } from "../util/dom.js" +import { off, on } from "../util/event.js" + +import { themeChanged } from "./utils.js" + +export let Init = {toString: function(){return "CodeMirror.Init"}} + +export let defaults = {} +export let optionHandlers = {} + +export function defineOptions(CodeMirror) { + let optionHandlers = CodeMirror.optionHandlers + + function option(name, deflt, handle, notOnInit) { + CodeMirror.defaults[name] = deflt + if (handle) optionHandlers[name] = + notOnInit ? (cm, val, old) => {if (old != Init) handle(cm, val, old)} : handle + } + + CodeMirror.defineOption = option + + // Passed to option handlers when there is no old value. + CodeMirror.Init = Init + + // These two are, on init, called from the constructor because they + // have to be initialized before the editor can start at all. + option("value", "", (cm, val) => cm.setValue(val), true) + option("mode", null, (cm, val) => { + cm.doc.modeOption = val + loadMode(cm) + }, true) + + option("indentUnit", 2, loadMode, true) + option("indentWithTabs", false) + option("smartIndent", true) + option("tabSize", 4, cm => { + resetModeState(cm) + clearCaches(cm) + regChange(cm) + }, true) + + option("lineSeparator", null, (cm, val) => { + cm.doc.lineSep = val + if (!val) return + let newBreaks = [], lineNo = cm.doc.first + cm.doc.iter(line => { + for (let pos = 0;;) { + let found = line.text.indexOf(val, pos) + if (found == -1) break + pos = found + val.length + newBreaks.push(Pos(lineNo, found)) + } + lineNo++ + }) + for (let i = newBreaks.length - 1; i >= 0; i--) + replaceRange(cm.doc, val, newBreaks[i], Pos(newBreaks[i].line, newBreaks[i].ch + val.length)) + }) + option("specialChars", /[\u0000-\u001f\u007f-\u009f\u00ad\u061c\u200b-\u200f\u2028\u2029\ufeff\ufff9-\ufffc]/g, (cm, val, old) => { + cm.state.specialChars = new RegExp(val.source + (val.test("\t") ? "" : "|\t"), "g") + if (old != Init) cm.refresh() + }) + option("specialCharPlaceholder", defaultSpecialCharPlaceholder, cm => cm.refresh(), true) + option("electricChars", true) + option("inputStyle", mobile ? "contenteditable" : "textarea", () => { + throw new Error("inputStyle can not (yet) be changed in a running editor") // FIXME + }, true) + option("spellcheck", false, (cm, val) => cm.getInputField().spellcheck = val, true) + option("autocorrect", false, (cm, val) => cm.getInputField().autocorrect = val, true) + option("autocapitalize", false, (cm, val) => cm.getInputField().autocapitalize = val, true) + option("rtlMoveVisually", !windows) + option("wholeLineUpdateBefore", true) + + option("theme", "default", cm => { + themeChanged(cm) + updateGutters(cm) + }, true) + option("keyMap", "default", (cm, val, old) => { + let next = getKeyMap(val) + let prev = old != Init && getKeyMap(old) + if (prev && prev.detach) prev.detach(cm, next) + if (next.attach) next.attach(cm, prev || null) + }) + option("extraKeys", null) + option("configureMouse", null) + + option("lineWrapping", false, wrappingChanged, true) + option("gutters", [], (cm, val) => { + cm.display.gutterSpecs = getGutters(val, cm.options.lineNumbers) + updateGutters(cm) + }, true) + option("fixedGutter", true, (cm, val) => { + cm.display.gutters.style.left = val ? compensateForHScroll(cm.display) + "px" : "0" + cm.refresh() + }, true) + option("coverGutterNextToScrollbar", false, cm => updateScrollbars(cm), true) + option("scrollbarStyle", "native", cm => { + initScrollbars(cm) + updateScrollbars(cm) + cm.display.scrollbars.setScrollTop(cm.doc.scrollTop) + cm.display.scrollbars.setScrollLeft(cm.doc.scrollLeft) + }, true) + option("lineNumbers", false, (cm, val) => { + cm.display.gutterSpecs = getGutters(cm.options.gutters, val) + updateGutters(cm) + }, true) + option("firstLineNumber", 1, updateGutters, true) + option("lineNumberFormatter", integer => integer, updateGutters, true) + option("showCursorWhenSelecting", false, updateSelection, true) + + option("resetSelectionOnContextMenu", true) + option("lineWiseCopyCut", true) + option("pasteLinesPerSelection", true) + option("selectionsMayTouch", false) + + option("readOnly", false, (cm, val) => { + if (val == "nocursor") { + onBlur(cm) + cm.display.input.blur() + } + cm.display.input.readOnlyChanged(val) + }) + option("disableInput", false, (cm, val) => {if (!val) cm.display.input.reset()}, true) + option("dragDrop", true, dragDropChanged) + option("allowDropFileTypes", null) + + option("cursorBlinkRate", 530) + option("cursorScrollMargin", 0) + option("cursorHeight", 1, updateSelection, true) + option("singleCursorHeightPerLine", true, updateSelection, true) + option("workTime", 100) + option("workDelay", 100) + option("flattenSpans", true, resetModeState, true) + option("addModeClass", false, resetModeState, true) + option("pollInterval", 100) + option("undoDepth", 200, (cm, val) => cm.doc.history.undoDepth = val) + option("historyEventDelay", 1250) + option("viewportMargin", 10, cm => cm.refresh(), true) + option("maxHighlightLength", 10000, resetModeState, true) + option("moveInputWithCursor", true, (cm, val) => { + if (!val) cm.display.input.resetPosition() + }) + + option("tabindex", null, (cm, val) => cm.display.input.getField().tabIndex = val || "") + option("autofocus", null) + option("direction", "ltr", (cm, val) => cm.doc.setDirection(val), true) + option("phrases", null) +} + +function dragDropChanged(cm, value, old) { + let wasOn = old && old != Init + if (!value != !wasOn) { + let funcs = cm.display.dragFunctions + let toggle = value ? on : off + toggle(cm.display.scroller, "dragstart", funcs.start) + toggle(cm.display.scroller, "dragenter", funcs.enter) + toggle(cm.display.scroller, "dragover", funcs.over) + toggle(cm.display.scroller, "dragleave", funcs.leave) + toggle(cm.display.scroller, "drop", funcs.drop) + } +} + +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(() => updateScrollbars(cm), 100) +} diff --git a/docs/js/node_modules/codemirror/src/edit/utils.js b/docs/js/node_modules/codemirror/src/edit/utils.js new file mode 100644 index 000000000..fda0be741 --- /dev/null +++ b/docs/js/node_modules/codemirror/src/edit/utils.js @@ -0,0 +1,7 @@ +import { clearCaches } from "../measurement/position_measurement.js" + +export 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) +} diff --git a/docs/js/node_modules/codemirror/src/input/ContentEditableInput.js b/docs/js/node_modules/codemirror/src/input/ContentEditableInput.js new file mode 100644 index 000000000..b77c7d49d --- /dev/null +++ b/docs/js/node_modules/codemirror/src/input/ContentEditableInput.js @@ -0,0 +1,527 @@ +import { operation, runInOp } from "../display/operations.js" +import { prepareSelection } from "../display/selection.js" +import { regChange } from "../display/view_tracking.js" +import { applyTextInput, copyableRanges, disableBrowserMagic, handlePaste, hiddenTextarea, lastCopied, setLastCopied } from "./input.js" +import { cmp, maxPos, minPos, Pos } from "../line/pos.js" +import { getBetween, getLine, lineNo } from "../line/utils_line.js" +import { findViewForLine, findViewIndex, mapFromLineView, nodeAndOffsetInLineMap } from "../measurement/position_measurement.js" +import { replaceRange } from "../model/changes.js" +import { simpleSelection } from "../model/selection.js" +import { setSelection } from "../model/selection_updates.js" +import { getBidiPartAt, getOrder } from "../util/bidi.js" +import { android, chrome, gecko, ie_version } from "../util/browser.js" +import { contains, range, removeChildrenAndAdd, selectInput } from "../util/dom.js" +import { on, signalDOMEvent } from "../util/event.js" +import { Delayed, lst, sel_dontScroll } from "../util/misc.js" + +// CONTENTEDITABLE INPUT STYLE + +export default class ContentEditableInput { + constructor(cm) { + this.cm = cm + this.lastAnchorNode = this.lastAnchorOffset = this.lastFocusNode = this.lastFocusOffset = null + this.polling = new Delayed() + this.composing = null + this.gracePeriod = false + this.readDOMTimeout = null + } + + init(display) { + let input = this, cm = input.cm + let div = input.div = display.lineDiv + disableBrowserMagic(div, cm.options.spellcheck, cm.options.autocorrect, cm.options.autocapitalize) + + on(div, "paste", e => { + if (signalDOMEvent(cm, e) || handlePaste(e, cm)) return + // IE doesn't fire input events, so we schedule a read for the pasted content in this way + if (ie_version <= 11) setTimeout(operation(cm, () => this.updateFromDOM()), 20) + }) + + on(div, "compositionstart", e => { + this.composing = {data: e.data, done: false} + }) + on(div, "compositionupdate", e => { + if (!this.composing) this.composing = {data: e.data, done: false} + }) + on(div, "compositionend", e => { + if (this.composing) { + if (e.data != this.composing.data) this.readFromDOMSoon() + this.composing.done = true + } + }) + + on(div, "touchstart", () => input.forceCompositionEnd()) + + on(div, "input", () => { + if (!this.composing) this.readFromDOMSoon() + }) + + function onCopyCut(e) { + if (signalDOMEvent(cm, e)) return + if (cm.somethingSelected()) { + setLastCopied({lineWise: false, text: cm.getSelections()}) + if (e.type == "cut") cm.replaceSelection("", null, "cut") + } else if (!cm.options.lineWiseCopyCut) { + return + } else { + let ranges = copyableRanges(cm) + setLastCopied({lineWise: true, text: ranges.text}) + if (e.type == "cut") { + cm.operation(() => { + cm.setSelections(ranges.ranges, 0, sel_dontScroll) + cm.replaceSelection("", null, "cut") + }) + } + } + if (e.clipboardData) { + e.clipboardData.clearData() + let content = lastCopied.text.join("\n") + // iOS exposes the clipboard API, but seems to discard content inserted into it + e.clipboardData.setData("Text", content) + if (e.clipboardData.getData("Text") == content) { + e.preventDefault() + return + } + } + // Old-fashioned briefly-focus-a-textarea hack + let kludge = hiddenTextarea(), te = kludge.firstChild + cm.display.lineSpace.insertBefore(kludge, cm.display.lineSpace.firstChild) + te.value = lastCopied.text.join("\n") + let hadFocus = document.activeElement + selectInput(te) + setTimeout(() => { + cm.display.lineSpace.removeChild(kludge) + hadFocus.focus() + if (hadFocus == div) input.showPrimarySelection() + }, 50) + } + on(div, "copy", onCopyCut) + on(div, "cut", onCopyCut) + } + + prepareSelection() { + let result = prepareSelection(this.cm, false) + result.focus = this.cm.state.focused + return result + } + + showSelection(info, takeFocus) { + if (!info || !this.cm.display.view.length) return + if (info.focus || takeFocus) this.showPrimarySelection() + this.showMultipleSelections(info) + } + + getSelection() { + return this.cm.display.wrapper.ownerDocument.getSelection() + } + + showPrimarySelection() { + let sel = this.getSelection(), cm = this.cm, prim = cm.doc.sel.primary() + let from = prim.from(), to = prim.to() + + if (cm.display.viewTo == cm.display.viewFrom || from.line >= cm.display.viewTo || to.line < cm.display.viewFrom) { + sel.removeAllRanges() + return + } + + let curAnchor = domToPos(cm, sel.anchorNode, sel.anchorOffset) + let curFocus = domToPos(cm, sel.focusNode, sel.focusOffset) + if (curAnchor && !curAnchor.bad && curFocus && !curFocus.bad && + cmp(minPos(curAnchor, curFocus), from) == 0 && + cmp(maxPos(curAnchor, curFocus), to) == 0) + return + + let view = cm.display.view + let start = (from.line >= cm.display.viewFrom && posToDOM(cm, from)) || + {node: view[0].measure.map[2], offset: 0} + let end = to.line < cm.display.viewTo && posToDOM(cm, to) + if (!end) { + let measure = view[view.length - 1].measure + let 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]} + } + + if (!start || !end) { + sel.removeAllRanges() + return + } + + let old = sel.rangeCount && sel.getRangeAt(0), rng + try { 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) { + if (!gecko && cm.state.focused) { + sel.collapse(start.node, start.offset) + if (!rng.collapsed) { + sel.removeAllRanges() + sel.addRange(rng) + } + } else { + sel.removeAllRanges() + sel.addRange(rng) + } + if (old && sel.anchorNode == null) sel.addRange(old) + else if (gecko) this.startGracePeriod() + } + this.rememberSelection() + } + + startGracePeriod() { + clearTimeout(this.gracePeriod) + this.gracePeriod = setTimeout(() => { + this.gracePeriod = false + if (this.selectionChanged()) + this.cm.operation(() => this.cm.curOp.selectionChanged = true) + }, 20) + } + + showMultipleSelections(info) { + removeChildrenAndAdd(this.cm.display.cursorDiv, info.cursors) + removeChildrenAndAdd(this.cm.display.selectionDiv, info.selection) + } + + rememberSelection() { + let sel = this.getSelection() + this.lastAnchorNode = sel.anchorNode; this.lastAnchorOffset = sel.anchorOffset + this.lastFocusNode = sel.focusNode; this.lastFocusOffset = sel.focusOffset + } + + selectionInEditor() { + let sel = this.getSelection() + if (!sel.rangeCount) return false + let node = sel.getRangeAt(0).commonAncestorContainer + return contains(this.div, node) + } + + focus() { + if (this.cm.options.readOnly != "nocursor") { + if (!this.selectionInEditor()) + this.showSelection(this.prepareSelection(), true) + this.div.focus() + } + } + blur() { this.div.blur() } + getField() { return this.div } + + supportsTouch() { return true } + + receivedFocus() { + let input = this + if (this.selectionInEditor()) + this.pollSelection() + else + runInOp(this.cm, () => 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() { + let sel = this.getSelection() + return sel.anchorNode != this.lastAnchorNode || sel.anchorOffset != this.lastAnchorOffset || + sel.focusNode != this.lastFocusNode || sel.focusOffset != this.lastFocusOffset + } + + pollSelection() { + if (this.readDOMTimeout != null || this.gracePeriod || !this.selectionChanged()) return + let sel = this.getSelection(), cm = this.cm + // On Android Chrome (version 56, at least), backspacing into an + // uneditable block element will put the cursor in that element, + // and then, because it's not editable, hide the virtual keyboard. + // Because Android doesn't allow us to actually detect backspace + // presses in a sane way, this code checks for when that happens + // and simulates a backspace press in this case. + if (android && chrome && this.cm.display.gutterSpecs.length && isInGutter(sel.anchorNode)) { + this.cm.triggerOnKeyDown({type: "keydown", keyCode: 8, preventDefault: Math.abs}) + this.blur() + this.focus() + return + } + if (this.composing) return + this.rememberSelection() + let anchor = domToPos(cm, sel.anchorNode, sel.anchorOffset) + let head = domToPos(cm, sel.focusNode, sel.focusOffset) + if (anchor && head) runInOp(cm, () => { + setSelection(cm.doc, simpleSelection(anchor, head), sel_dontScroll) + if (anchor.bad || head.bad) cm.curOp.selectionChanged = true + }) + } + + pollContent() { + if (this.readDOMTimeout != null) { + clearTimeout(this.readDOMTimeout) + this.readDOMTimeout = null + } + + let cm = this.cm, display = cm.display, sel = cm.doc.sel.primary() + let from = sel.from(), to = sel.to() + if (from.ch == 0 && from.line > cm.firstLine()) + from = Pos(from.line - 1, getLine(cm.doc, from.line - 1).length) + if (to.ch == getLine(cm.doc, to.line).text.length && to.line < cm.lastLine()) + to = Pos(to.line + 1, 0) + if (from.line < display.viewFrom || to.line > display.viewTo - 1) return false + + let fromIndex, fromLine, fromNode + if (from.line == display.viewFrom || (fromIndex = findViewIndex(cm, from.line)) == 0) { + fromLine = lineNo(display.view[0].line) + fromNode = display.view[0].node + } else { + fromLine = lineNo(display.view[fromIndex].line) + fromNode = display.view[fromIndex - 1].node.nextSibling + } + let toIndex = findViewIndex(cm, to.line) + let toLine, toNode + if (toIndex == display.view.length - 1) { + toLine = display.viewTo - 1 + toNode = display.lineDiv.lastChild + } else { + toLine = lineNo(display.view[toIndex + 1].line) - 1 + toNode = display.view[toIndex + 1].node.previousSibling + } + + if (!fromNode) return false + let newText = cm.doc.splitLines(domTextBetween(cm, fromNode, toNode, fromLine, toLine)) + let 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 + } + + let cutFront = 0, cutEnd = 0 + let newTop = newText[0], oldTop = oldText[0], maxCutFront = Math.min(newTop.length, oldTop.length) + while (cutFront < maxCutFront && newTop.charCodeAt(cutFront) == oldTop.charCodeAt(cutFront)) + ++cutFront + let newBot = lst(newText), oldBot = lst(oldText) + let 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 + // Try to move start of change to start of selection if ambiguous + if (newText.length == 1 && oldText.length == 1 && fromLine == from.line) { + while (cutFront && cutFront > from.ch && + newBot.charCodeAt(newBot.length - cutEnd - 1) == oldBot.charCodeAt(oldBot.length - cutEnd - 1)) { + cutFront-- + cutEnd++ + } + } + + newText[newText.length - 1] = newBot.slice(0, newBot.length - cutEnd).replace(/^\u200b+/, "") + newText[0] = newText[0].slice(cutFront).replace(/\u200b+$/, "") + + let chFrom = Pos(fromLine, cutFront) + let 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() { + this.forceCompositionEnd() + } + reset() { + this.forceCompositionEnd() + } + forceCompositionEnd() { + if (!this.composing) return + clearTimeout(this.readDOMTimeout) + this.composing = null + this.updateFromDOM() + this.div.blur() + this.div.focus() + } + readFromDOMSoon() { + if (this.readDOMTimeout != null) return + this.readDOMTimeout = setTimeout(() => { + this.readDOMTimeout = null + if (this.composing) { + if (this.composing.done) this.composing = null + else return + } + this.updateFromDOM() + }, 80) + } + + updateFromDOM() { + if (this.cm.isReadOnly() || !this.pollContent()) + runInOp(this.cm, () => regChange(this.cm)) + } + + setUneditable(node) { + node.contentEditable = "false" + } + + onKeyPress(e) { + if (e.charCode == 0 || this.composing) return + e.preventDefault() + if (!this.cm.isReadOnly()) + operation(this.cm, applyTextInput)(this.cm, String.fromCharCode(e.charCode == null ? e.keyCode : e.charCode), 0) + } + + readOnlyChanged(val) { + this.div.contentEditable = String(val != "nocursor") + } + + onContextMenu() {} + resetPosition() {} +} + +ContentEditableInput.prototype.needsContentAttribute = true + +function posToDOM(cm, pos) { + let view = findViewForLine(cm, pos.line) + if (!view || view.hidden) return null + let line = getLine(cm.doc, pos.line) + let info = mapFromLineView(view, line, pos.line) + + let order = getOrder(line, cm.doc.direction), side = "left" + if (order) { + let partPos = getBidiPartAt(order, pos.ch) + side = partPos % 2 ? "right" : "left" + } + let result = nodeAndOffsetInLineMap(info.map, pos.ch, side) + result.offset = result.collapse == "right" ? result.end : result.start + return result +} + +function isInGutter(node) { + for (let scan = node; scan; scan = scan.parentNode) + if (/CodeMirror-gutter-wrapper/.test(scan.className)) return true + return false +} + +function badPos(pos, bad) { if (bad) pos.bad = true; return pos } + +function domTextBetween(cm, from, to, fromLine, toLine) { + let text = "", closing = false, lineSep = cm.doc.lineSeparator(), extraLinebreak = false + function recognizeMarker(id) { return marker => marker.id == id } + function close() { + if (closing) { + text += lineSep + if (extraLinebreak) text += lineSep + closing = extraLinebreak = false + } + } + function addText(str) { + if (str) { + close() + text += str + } + } + function walk(node) { + if (node.nodeType == 1) { + let cmText = node.getAttribute("cm-text") + if (cmText) { + addText(cmText) + return + } + let markerID = node.getAttribute("cm-marker"), range + if (markerID) { + let found = cm.findMarks(Pos(fromLine, 0), Pos(toLine + 1, 0), recognizeMarker(+markerID)) + if (found.length && (range = found[0].find(0))) + addText(getBetween(cm.doc, range.from, range.to).join(lineSep)) + return + } + if (node.getAttribute("contenteditable") == "false") return + let isBlock = /^(pre|div|p|li|table|br)$/i.test(node.nodeName) + if (!/^br$/i.test(node.nodeName) && node.textContent.length == 0) return + + if (isBlock) close() + for (let i = 0; i < node.childNodes.length; i++) + walk(node.childNodes[i]) + + if (/^(pre|p)$/i.test(node.nodeName)) extraLinebreak = true + if (isBlock) closing = true + } else if (node.nodeType == 3) { + addText(node.nodeValue.replace(/\u200b/g, "").replace(/\u00a0/g, " ")) + } + } + for (;;) { + walk(from) + if (from == to) break + from = from.nextSibling + extraLinebreak = false + } + return text +} + +function domToPos(cm, node, offset) { + let 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 (let i = 0; i < cm.display.view.length; i++) { + let lineView = cm.display.view[i] + if (lineView.node == lineNode) + return locateNodeInLineView(lineView, node, offset) + } +} + +function locateNodeInLineView(lineView, node, offset) { + let 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) { + let line = lineView.rest ? lst(lineView.rest) : lineView.line + return badPos(Pos(lineNo(line), line.text.length), bad) + } + } + + let 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 + let measure = lineView.measure, maps = measure.maps + + function find(textNode, topNode, offset) { + for (let i = -1; i < (maps ? maps.length : 0); i++) { + let map = i < 0 ? measure.map : maps[i] + for (let j = 0; j < map.length; j += 3) { + let curNode = map[j + 2] + if (curNode == textNode || curNode == topNode) { + let line = lineNo(i < 0 ? lineView.line : lineView.rest[i]) + let ch = map[j] + offset + if (offset < 0 || curNode != textNode) ch = map[j + (offset ? 1 : 0)] + return Pos(line, ch) + } + } + } + } + let 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 (let 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 (let 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 += before.textContent.length + } +} diff --git a/docs/js/node_modules/codemirror/src/input/TextareaInput.js b/docs/js/node_modules/codemirror/src/input/TextareaInput.js new file mode 100644 index 000000000..ab02230f9 --- /dev/null +++ b/docs/js/node_modules/codemirror/src/input/TextareaInput.js @@ -0,0 +1,365 @@ +import { operation, runInOp } from "../display/operations.js" +import { prepareSelection } from "../display/selection.js" +import { applyTextInput, copyableRanges, handlePaste, hiddenTextarea, setLastCopied } from "./input.js" +import { cursorCoords, posFromMouse } from "../measurement/position_measurement.js" +import { eventInWidget } from "../measurement/widgets.js" +import { simpleSelection } from "../model/selection.js" +import { selectAll, setSelection } from "../model/selection_updates.js" +import { captureRightClick, ie, ie_version, ios, mac, mobile, presto, webkit } from "../util/browser.js" +import { activeElt, removeChildrenAndAdd, selectInput } from "../util/dom.js" +import { e_preventDefault, e_stop, off, on, signalDOMEvent } from "../util/event.js" +import { hasSelection } from "../util/feature_detection.js" +import { Delayed, sel_dontScroll } from "../util/misc.js" + +// TEXTAREA INPUT STYLE + +export default class TextareaInput { + constructor(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() + // Used to work around IE issue with selection being forgotten when focus moves away from textarea + this.hasSelection = false + this.composing = null + } + + init(display) { + let input = this, cm = this.cm + this.createField(display) + const te = this.textarea + + display.wrapper.insertBefore(this.wrapper, 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", () => { + if (ie && ie_version >= 9 && this.hasSelection) this.hasSelection = null + input.poll() + }) + + on(te, "paste", e => { + if (signalDOMEvent(cm, e) || handlePaste(e, cm)) return + + cm.state.pasteIncoming = +new Date + input.fastPoll() + }) + + function prepareCopyCut(e) { + if (signalDOMEvent(cm, e)) return + if (cm.somethingSelected()) { + setLastCopied({lineWise: false, text: cm.getSelections()}) + } else if (!cm.options.lineWiseCopyCut) { + return + } else { + let ranges = copyableRanges(cm) + setLastCopied({lineWise: true, text: 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 = +new Date + } + on(te, "cut", prepareCopyCut) + on(te, "copy", prepareCopyCut) + + on(display.scroller, "paste", e => { + if (eventInWidget(display, e) || signalDOMEvent(cm, e)) return + if (!te.dispatchEvent) { + cm.state.pasteIncoming = +new Date + input.focus() + return + } + + // Pass the `paste` event to the textarea so it's handled by its event listener. + const event = new Event("paste") + event.clipboardData = e.clipboardData + te.dispatchEvent(event) + }) + + // Prevent normal selection in the editor (we handle our own) + on(display.lineSpace, "selectstart", e => { + if (!eventInWidget(display, e)) e_preventDefault(e) + }) + + on(te, "compositionstart", () => { + let start = cm.getCursor("from") + if (input.composing) input.composing.range.clear() + input.composing = { + start: start, + range: cm.markText(start, cm.getCursor("to"), {className: "CodeMirror-composing"}) + } + }) + on(te, "compositionend", () => { + if (input.composing) { + input.poll() + input.composing.range.clear() + input.composing = null + } + }) + } + + createField(_display) { + // Wraps and hides input textarea + this.wrapper = hiddenTextarea() + // The semihidden textarea that is focused when the editor is + // focused, and receives input. + this.textarea = this.wrapper.firstChild + } + + prepareSelection() { + // Redraw the selection and/or cursor + let cm = this.cm, display = cm.display, doc = cm.doc + let result = prepareSelection(cm) + + // Move the hidden textarea near the cursor to prevent scrolling artifacts + if (cm.options.moveInputWithCursor) { + let headPos = cursorCoords(cm, doc.sel.primary().head, "div") + let 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(drawn) { + let 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(typing) { + if (this.contextMenuPending || this.composing) return + let cm = this.cm + if (cm.somethingSelected()) { + this.prevInput = "" + let content = 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 + } + } + + getField() { return this.textarea } + + supportsTouch() { return false } + + focus() { + 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() { this.textarea.blur() } + + resetPosition() { + this.wrapper.style.top = this.wrapper.style.left = 0 + } + + receivedFocus() { this.slowPoll() } + + // Poll for input changes, using the normal rate of polling. This + // runs as long as the editor is focused. + slowPoll() { + if (this.pollingFast) return + this.polling.set(this.cm.options.pollInterval, () => { + this.poll() + if (this.cm.state.focused) this.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() { + let missed = false, input = this + input.pollingFast = true + function p() { + let 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() { + let 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 (this.contextMenuPending || !cm.state.focused || + (hasSelection(input) && !prevInput && !this.composing) || + cm.isReadOnly() || cm.options.disableInput || cm.state.keySeq) + return false + + let 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) { + let 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 + let same = 0, l = Math.min(prevInput.length, text.length) + while (same < l && prevInput.charCodeAt(same) == text.charCodeAt(same)) ++same + + runInOp(cm, () => { + applyTextInput(cm, text.slice(same), prevInput.length - same, + null, this.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 = this.prevInput = "" + else this.prevInput = text + + if (this.composing) { + this.composing.range.clear() + this.composing.range = cm.markText(this.composing.start, cm.getCursor("to"), + {className: "CodeMirror-composing"}) + } + }) + return true + } + + ensurePolled() { + if (this.pollingFast && this.poll()) this.pollingFast = false + } + + onKeyPress() { + if (ie && ie_version >= 9) this.hasSelection = null + this.fastPoll() + } + + onContextMenu(e) { + let input = this, cm = input.cm, display = cm.display, te = input.textarea + if (input.contextMenuPending) input.contextMenuPending() + let 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. + let reset = cm.options.resetSelectionOnContextMenu + if (reset && cm.doc.sel.contains(pos) == -1) + operation(cm, setSelection)(cm.doc, simpleSelection(pos), sel_dontScroll) + + let oldCSS = te.style.cssText, oldWrapperCSS = input.wrapper.style.cssText + let wrapperBox = input.wrapper.offsetParent.getBoundingClientRect() + input.wrapper.style.cssText = "position: static" + te.style.cssText = `position: absolute; width: 30px; height: 30px; + top: ${e.clientY - wrapperBox.top - 5}px; left: ${e.clientX - wrapperBox.left - 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);` + let oldScrollY + if (webkit) 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 = rehide + 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) { + let selected = cm.somethingSelected() + let 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() { + if (input.contextMenuPending != rehide) return + input.contextMenuPending = false + input.wrapper.style.cssText = oldWrapperCSS + 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() + let i = 0, poll = () => { + if (display.selForContextMenu == cm.doc.sel && te.selectionStart == 0 && + te.selectionEnd > 0 && input.prevInput == "\u200b") { + operation(cm, selectAll)(cm) + } else if (i++ < 10) { + display.detectingSelectAll = setTimeout(poll, 500) + } else { + display.selForContextMenu = null + display.input.reset() + } + } + display.detectingSelectAll = setTimeout(poll, 200) + } + } + + if (ie && ie_version >= 9) prepareSelectAllHack() + if (captureRightClick) { + e_stop(e) + let mouseup = () => { + off(window, "mouseup", mouseup) + setTimeout(rehide, 20) + } + on(window, "mouseup", mouseup) + } else { + setTimeout(rehide, 50) + } + } + + readOnlyChanged(val) { + if (!val) this.reset() + this.textarea.disabled = val == "nocursor" + } + + setUneditable() {} +} + +TextareaInput.prototype.needsContentAttribute = false diff --git a/docs/js/node_modules/codemirror/src/input/indent.js b/docs/js/node_modules/codemirror/src/input/indent.js new file mode 100644 index 000000000..c88772cb6 --- /dev/null +++ b/docs/js/node_modules/codemirror/src/input/indent.js @@ -0,0 +1,71 @@ +import { getContextBefore } from "../line/highlight.js" +import { Pos } from "../line/pos.js" +import { getLine } from "../line/utils_line.js" +import { replaceRange } from "../model/changes.js" +import { Range } from "../model/selection.js" +import { replaceOneSelection } from "../model/selection_updates.js" +import { countColumn, Pass, spaceStr } from "../util/misc.js" + +// Indent the given line. The how parameter can be "smart", +// "add"/null, "subtract", or "prev". When aggressive is false +// (typically set to true for forced single-line indents), empty +// lines are not indented, and places where the mode returns Pass +// are left alone. +export function indentLine(cm, n, how, aggressive) { + let doc = cm.doc, state + if (how == null) how = "add" + if (how == "smart") { + // Fall back to "prev" when the mode doesn't have an indentation + // method. + if (!doc.mode.indent) how = "prev" + else state = getContextBefore(cm, n).state + } + + let tabSize = cm.options.tabSize + let line = getLine(doc, n), curSpace = countColumn(line.text, null, tabSize) + if (line.stateAfter) line.stateAfter = null + let curSpaceString = line.text.match(/^\s*/)[0], indentation + if (!aggressive && !/\S/.test(line.text)) { + indentation = 0 + how = "not" + } else if (how == "smart") { + indentation = doc.mode.indent(state, line.text.slice(curSpaceString.length), line.text) + if (indentation == Pass || indentation > 150) { + if (!aggressive) return + how = "prev" + } + } + if (how == "prev") { + if (n > doc.first) indentation = countColumn(getLine(doc, n-1).text, null, tabSize) + else indentation = 0 + } else if (how == "add") { + indentation = curSpace + cm.options.indentUnit + } else if (how == "subtract") { + indentation = curSpace - cm.options.indentUnit + } else if (typeof how == "number") { + indentation = curSpace + how + } + indentation = Math.max(0, indentation) + + let indentString = "", pos = 0 + if (cm.options.indentWithTabs) + for (let i = Math.floor(indentation / tabSize); i; --i) {pos += tabSize; indentString += "\t"} + if (pos < indentation) indentString += spaceStr(indentation - pos) + + if (indentString != curSpaceString) { + replaceRange(doc, indentString, Pos(n, 0), Pos(n, curSpaceString.length), "+input") + line.stateAfter = null + return true + } else { + // Ensure that, if the cursor was in the whitespace at the start + // of the line, it is moved to the end of that space. + for (let i = 0; i < doc.sel.ranges.length; i++) { + let range = doc.sel.ranges[i] + if (range.head.line == n && range.head.ch < curSpaceString.length) { + let pos = Pos(n, curSpaceString.length) + replaceOneSelection(doc, i, new Range(pos, pos)) + break + } + } + } +} diff --git a/docs/js/node_modules/codemirror/src/input/input.js b/docs/js/node_modules/codemirror/src/input/input.js new file mode 100644 index 000000000..26bba1d26 --- /dev/null +++ b/docs/js/node_modules/codemirror/src/input/input.js @@ -0,0 +1,135 @@ +import { runInOp } from "../display/operations.js" +import { ensureCursorVisible } from "../display/scrolling.js" +import { Pos } from "../line/pos.js" +import { getLine } from "../line/utils_line.js" +import { makeChange } from "../model/changes.js" +import { ios, webkit } from "../util/browser.js" +import { elt } from "../util/dom.js" +import { lst, map } from "../util/misc.js" +import { signalLater } from "../util/operation_group.js" +import { splitLinesAuto } from "../util/feature_detection.js" + +import { indentLine } from "./indent.js" + +// This will be set to a {lineWise: bool, text: [string]} object, so +// that, when pasting, we know what kind of selections the copied +// text was made out of. +export let lastCopied = null + +export function setLastCopied(newLastCopied) { + lastCopied = newLastCopied +} + +export function applyTextInput(cm, inserted, deleted, sel, origin) { + let doc = cm.doc + cm.display.shift = false + if (!sel) sel = doc.sel + + let recent = +new Date - 200 + let paste = origin == "paste" || cm.state.pasteIncoming > recent + let textLines = splitLinesAuto(inserted), multiPaste = null + // When pasting N lines into N selections, insert one line per selection + if (paste && sel.ranges.length > 1) { + if (lastCopied && lastCopied.text.join("\n") == inserted) { + if (sel.ranges.length % lastCopied.text.length == 0) { + multiPaste = [] + for (let i = 0; i < lastCopied.text.length; i++) + multiPaste.push(doc.splitLines(lastCopied.text[i])) + } + } else if (textLines.length == sel.ranges.length && cm.options.pasteLinesPerSelection) { + multiPaste = map(textLines, l => [l]) + } + } + + let updateInput = cm.curOp.updateInput + // Normal behavior is to insert the new text into every selection + for (let i = sel.ranges.length - 1; i >= 0; i--) { + let range = sel.ranges[i] + let 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 && !paste) // Handle overwrite + to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + lst(textLines).length)) + else if (paste && lastCopied && lastCopied.lineWise && lastCopied.text.join("\n") == inserted) + from = to = Pos(from.line, 0) + } + let changeEvent = {from: from, to: to, text: multiPaste ? multiPaste[i % multiPaste.length] : textLines, + origin: origin || (paste ? "paste" : cm.state.cutIncoming > recent ? "cut" : "+input")} + makeChange(cm.doc, changeEvent) + signalLater(cm, "inputRead", cm, changeEvent) + } + if (inserted && !paste) + triggerElectric(cm, inserted) + + ensureCursorVisible(cm) + if (cm.curOp.updateInput < 2) cm.curOp.updateInput = updateInput + cm.curOp.typing = true + cm.state.pasteIncoming = cm.state.cutIncoming = -1 +} + +export function handlePaste(e, cm) { + let pasted = e.clipboardData && e.clipboardData.getData("Text") + if (pasted) { + e.preventDefault() + if (!cm.isReadOnly() && !cm.options.disableInput) + runInOp(cm, () => applyTextInput(cm, pasted, 0, null, "paste")) + return true + } +} + +export function triggerElectric(cm, inserted) { + // When an 'electric' character is inserted, immediately trigger a reindent + if (!cm.options.electricChars || !cm.options.smartIndent) return + let sel = cm.doc.sel + + for (let i = sel.ranges.length - 1; i >= 0; i--) { + let range = sel.ranges[i] + if (range.head.ch > 100 || (i && sel.ranges[i - 1].head.line == range.head.line)) continue + let mode = cm.getModeAt(range.head) + let indented = false + if (mode.electricChars) { + for (let 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) + } +} + +export function copyableRanges(cm) { + let text = [], ranges = [] + for (let i = 0; i < cm.doc.sel.ranges.length; i++) { + let line = cm.doc.sel.ranges[i].head.line + let 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} +} + +export function disableBrowserMagic(field, spellcheck, autocorrect, autocapitalize) { + field.setAttribute("autocorrect", autocorrect ? "" : "off") + field.setAttribute("autocapitalize", autocapitalize ? "" : "off") + field.setAttribute("spellcheck", !!spellcheck) +} + +export function hiddenTextarea() { + let te = elt("textarea", null, null, "position: absolute; bottom: -1em; padding: 0; width: 1px; height: 1em; outline: none") + let 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 +} diff --git a/docs/js/node_modules/codemirror/src/input/keymap.js b/docs/js/node_modules/codemirror/src/input/keymap.js new file mode 100644 index 000000000..046b3505a --- /dev/null +++ b/docs/js/node_modules/codemirror/src/input/keymap.js @@ -0,0 +1,148 @@ +import { flipCtrlCmd, mac, presto } from "../util/browser.js" +import { map } from "../util/misc.js" + +import { keyNames } from "./keynames.js" + +export let keyMap = {} + +keyMap.basic = { + "Left": "goCharLeft", "Right": "goCharRight", "Up": "goLineUp", "Down": "goLineDown", + "End": "goLineEnd", "Home": "goLineStartSmart", "PageUp": "goPageUp", "PageDown": "goPageDown", + "Delete": "delCharAfter", "Backspace": "delCharBefore", "Shift-Backspace": "delCharBefore", + "Tab": "defaultTab", "Shift-Tab": "indentAuto", + "Enter": "newlineAndIndent", "Insert": "toggleOverwrite", + "Esc": "singleSelection" +} +// Note that the save and find-related commands aren't defined by +// default. User code or addons can define them. Unknown commands +// are simply ignored. +keyMap.pcDefault = { + "Ctrl-A": "selectAll", "Ctrl-D": "deleteLine", "Ctrl-Z": "undo", "Shift-Ctrl-Z": "redo", "Ctrl-Y": "redo", + "Ctrl-Home": "goDocStart", "Ctrl-End": "goDocEnd", "Ctrl-Up": "goLineUp", "Ctrl-Down": "goLineDown", + "Ctrl-Left": "goGroupLeft", "Ctrl-Right": "goGroupRight", "Alt-Left": "goLineStart", "Alt-Right": "goLineEnd", + "Ctrl-Backspace": "delGroupBefore", "Ctrl-Delete": "delGroupAfter", "Ctrl-S": "save", "Ctrl-F": "find", + "Ctrl-G": "findNext", "Shift-Ctrl-G": "findPrev", "Shift-Ctrl-F": "replace", "Shift-Ctrl-R": "replaceAll", + "Ctrl-[": "indentLess", "Ctrl-]": "indentMore", + "Ctrl-U": "undoSelection", "Shift-Ctrl-U": "redoSelection", "Alt-U": "redoSelection", + "fallthrough": "basic" +} +// Very basic readline/emacs-style bindings, which are standard on Mac. +keyMap.emacsy = { + "Ctrl-F": "goCharRight", "Ctrl-B": "goCharLeft", "Ctrl-P": "goLineUp", "Ctrl-N": "goLineDown", + "Alt-F": "goWordRight", "Alt-B": "goWordLeft", "Ctrl-A": "goLineStart", "Ctrl-E": "goLineEnd", + "Ctrl-V": "goPageDown", "Shift-Ctrl-V": "goPageUp", "Ctrl-D": "delCharAfter", "Ctrl-H": "delCharBefore", + "Alt-D": "delWordAfter", "Alt-Backspace": "delWordBefore", "Ctrl-K": "killLine", "Ctrl-T": "transposeChars", + "Ctrl-O": "openLine" +} +keyMap.macDefault = { + "Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z": "redo", "Cmd-Y": "redo", + "Cmd-Home": "goDocStart", "Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cmd-Down": "goDocEnd", "Alt-Left": "goGroupLeft", + "Alt-Right": "goGroupRight", "Cmd-Left": "goLineLeft", "Cmd-Right": "goLineRight", "Alt-Backspace": "delGroupBefore", + "Ctrl-Alt-Backspace": "delGroupAfter", "Alt-Delete": "delGroupAfter", "Cmd-S": "save", "Cmd-F": "find", + "Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll", + "Cmd-[": "indentLess", "Cmd-]": "indentMore", "Cmd-Backspace": "delWrappedLineLeft", "Cmd-Delete": "delWrappedLineRight", + "Cmd-U": "undoSelection", "Shift-Cmd-U": "redoSelection", "Ctrl-Up": "goDocStart", "Ctrl-Down": "goDocEnd", + "fallthrough": ["basic", "emacsy"] +} +keyMap["default"] = mac ? keyMap.macDefault : keyMap.pcDefault + +// KEYMAP DISPATCH + +function normalizeKeyName(name) { + let parts = name.split(/-(?!$)/) + name = parts[parts.length - 1] + let alt, ctrl, shift, cmd + for (let i = 0; i < parts.length - 1; i++) { + let mod = parts[i] + if (/^(cmd|meta|m)$/i.test(mod)) cmd = true + else if (/^a(lt)?$/i.test(mod)) alt = true + else if (/^(c|ctrl|control)$/i.test(mod)) ctrl = true + else if (/^s(hift)?$/i.test(mod)) shift = true + else throw new Error("Unrecognized modifier name: " + mod) + } + if (alt) name = "Alt-" + name + if (ctrl) name = "Ctrl-" + name + if (cmd) name = "Cmd-" + name + if (shift) name = "Shift-" + name + return name +} + +// This is a kludge to keep keymaps mostly working as raw objects +// (backwards compatibility) while at the same time support features +// like normalization and multi-stroke key bindings. It compiles a +// new normalized keymap, and then updates the old object to reflect +// this. +export function normalizeKeyMap(keymap) { + let copy = {} + for (let keyname in keymap) if (keymap.hasOwnProperty(keyname)) { + let value = keymap[keyname] + if (/^(name|fallthrough|(de|at)tach)$/.test(keyname)) continue + if (value == "...") { delete keymap[keyname]; continue } + + let keys = map(keyname.split(" "), normalizeKeyName) + for (let i = 0; i < keys.length; i++) { + let val, name + if (i == keys.length - 1) { + name = keys.join(" ") + val = value + } else { + name = keys.slice(0, i + 1).join(" ") + val = "..." + } + let prev = copy[name] + if (!prev) copy[name] = val + else if (prev != val) throw new Error("Inconsistent bindings for " + name) + } + delete keymap[keyname] + } + for (let prop in copy) keymap[prop] = copy[prop] + return keymap +} + +export function lookupKey(key, map, handle, context) { + map = getKeyMap(map) + let found = map.call ? map.call(key, context) : map[key] + if (found === false) return "nothing" + if (found === "...") return "multi" + if (found != null && handle(found)) return "handled" + + if (map.fallthrough) { + if (Object.prototype.toString.call(map.fallthrough) != "[object Array]") + return lookupKey(key, map.fallthrough, handle, context) + for (let i = 0; i < map.fallthrough.length; i++) { + let result = lookupKey(key, map.fallthrough[i], handle, context) + if (result) return result + } + } +} + +// Modifier key presses don't count as 'real' key presses for the +// purpose of keymap fallthrough. +export function isModifierKey(value) { + let name = typeof value == "string" ? value : keyNames[value.keyCode] + return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod" +} + +export function addModifierNames(name, event, noShift) { + let base = name + if (event.altKey && base != "Alt") name = "Alt-" + name + if ((flipCtrlCmd ? event.metaKey : event.ctrlKey) && base != "Ctrl") name = "Ctrl-" + name + if ((flipCtrlCmd ? event.ctrlKey : event.metaKey) && base != "Cmd") name = "Cmd-" + name + if (!noShift && event.shiftKey && base != "Shift") name = "Shift-" + name + return name +} + +// Look up the name of a key as indicated by an event object. +export function keyName(event, noShift) { + if (presto && event.keyCode == 34 && event["char"]) return false + let name = keyNames[event.keyCode] + if (name == null || event.altGraphKey) return false + // Ctrl-ScrollLock has keyCode 3, same as Ctrl-Pause, + // so we'll use event.code when available (Chrome 48+, FF 38+, Safari 10.1+) + if (event.keyCode == 3 && event.code) name = event.code + return addModifierNames(name, event, noShift) +} + +export function getKeyMap(val) { + return typeof val == "string" ? keyMap[val] : val +} diff --git a/docs/js/node_modules/codemirror/src/input/keynames.js b/docs/js/node_modules/codemirror/src/input/keynames.js new file mode 100644 index 000000000..d3339038f --- /dev/null +++ b/docs/js/node_modules/codemirror/src/input/keynames.js @@ -0,0 +1,17 @@ +export let keyNames = { + 3: "Pause", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift", 17: "Ctrl", 18: "Alt", + 19: "Pause", 20: "CapsLock", 27: "Esc", 32: "Space", 33: "PageUp", 34: "PageDown", 35: "End", + 36: "Home", 37: "Left", 38: "Up", 39: "Right", 40: "Down", 44: "PrintScrn", 45: "Insert", + 46: "Delete", 59: ";", 61: "=", 91: "Mod", 92: "Mod", 93: "Mod", + 106: "*", 107: "=", 109: "-", 110: ".", 111: "/", 145: "ScrollLock", + 173: "-", 186: ";", 187: "=", 188: ",", 189: "-", 190: ".", 191: "/", 192: "`", 219: "[", 220: "\\", + 221: "]", 222: "'", 63232: "Up", 63233: "Down", 63234: "Left", 63235: "Right", 63272: "Delete", + 63273: "Home", 63275: "End", 63276: "PageUp", 63277: "PageDown", 63302: "Insert" +} + +// Number keys +for (let i = 0; i < 10; i++) keyNames[i + 48] = keyNames[i + 96] = String(i) +// Alphabetic keys +for (let i = 65; i <= 90; i++) keyNames[i] = String.fromCharCode(i) +// Function keys +for (let i = 1; i <= 12; i++) keyNames[i + 111] = keyNames[i + 63235] = "F" + i diff --git a/docs/js/node_modules/codemirror/src/input/movement.js b/docs/js/node_modules/codemirror/src/input/movement.js new file mode 100644 index 000000000..8d50fd2a0 --- /dev/null +++ b/docs/js/node_modules/codemirror/src/input/movement.js @@ -0,0 +1,110 @@ +import { Pos } from "../line/pos.js" +import { prepareMeasureForLine, measureCharPrepared, wrappedLineExtentChar } from "../measurement/position_measurement.js" +import { getBidiPartAt, getOrder } from "../util/bidi.js" +import { findFirst, lst, skipExtendingChars } from "../util/misc.js" + +function moveCharLogically(line, ch, dir) { + let target = skipExtendingChars(line.text, ch + dir, dir) + return target < 0 || target > line.text.length ? null : target +} + +export function moveLogically(line, start, dir) { + let ch = moveCharLogically(line, start.ch, dir) + return ch == null ? null : new Pos(start.line, ch, dir < 0 ? "after" : "before") +} + +export function endOfLine(visually, cm, lineObj, lineNo, dir) { + if (visually) { + let order = getOrder(lineObj, cm.doc.direction) + if (order) { + let part = dir < 0 ? lst(order) : order[0] + let moveInStorageOrder = (dir < 0) == (part.level == 1) + let sticky = moveInStorageOrder ? "after" : "before" + let ch + // With a wrapped rtl chunk (possibly spanning multiple bidi parts), + // it could be that the last bidi part is not on the last visual line, + // since visual lines contain content order-consecutive chunks. + // Thus, in rtl, we are looking for the first (content-order) character + // in the rtl chunk that is on the last line (that is, the same line + // as the last (content-order) character). + if (part.level > 0 || cm.doc.direction == "rtl") { + let prep = prepareMeasureForLine(cm, lineObj) + ch = dir < 0 ? lineObj.text.length - 1 : 0 + let targetTop = measureCharPrepared(cm, prep, ch).top + ch = findFirst(ch => measureCharPrepared(cm, prep, ch).top == targetTop, (dir < 0) == (part.level == 1) ? part.from : part.to - 1, ch) + if (sticky == "before") ch = moveCharLogically(lineObj, ch, 1) + } else ch = dir < 0 ? part.to : part.from + return new Pos(lineNo, ch, sticky) + } + } + return new Pos(lineNo, dir < 0 ? lineObj.text.length : 0, dir < 0 ? "before" : "after") +} + +export function moveVisually(cm, line, start, dir) { + let bidi = getOrder(line, cm.doc.direction) + if (!bidi) return moveLogically(line, start, dir) + if (start.ch >= line.text.length) { + start.ch = line.text.length + start.sticky = "before" + } else if (start.ch <= 0) { + start.ch = 0 + start.sticky = "after" + } + let partPos = getBidiPartAt(bidi, start.ch, start.sticky), part = bidi[partPos] + if (cm.doc.direction == "ltr" && part.level % 2 == 0 && (dir > 0 ? part.to > start.ch : part.from < start.ch)) { + // Case 1: We move within an ltr part in an ltr editor. Even with wrapped lines, + // nothing interesting happens. + return moveLogically(line, start, dir) + } + + let mv = (pos, dir) => moveCharLogically(line, pos instanceof Pos ? pos.ch : pos, dir) + let prep + let getWrappedLineExtent = ch => { + if (!cm.options.lineWrapping) return {begin: 0, end: line.text.length} + prep = prep || prepareMeasureForLine(cm, line) + return wrappedLineExtentChar(cm, line, prep, ch) + } + let wrappedLineExtent = getWrappedLineExtent(start.sticky == "before" ? mv(start, -1) : start.ch) + + if (cm.doc.direction == "rtl" || part.level == 1) { + let moveInStorageOrder = (part.level == 1) == (dir < 0) + let ch = mv(start, moveInStorageOrder ? 1 : -1) + if (ch != null && (!moveInStorageOrder ? ch >= part.from && ch >= wrappedLineExtent.begin : ch <= part.to && ch <= wrappedLineExtent.end)) { + // Case 2: We move within an rtl part or in an rtl editor on the same visual line + let sticky = moveInStorageOrder ? "before" : "after" + return new Pos(start.line, ch, sticky) + } + } + + // Case 3: Could not move within this bidi part in this visual line, so leave + // the current bidi part + + let searchInVisualLine = (partPos, dir, wrappedLineExtent) => { + let getRes = (ch, moveInStorageOrder) => moveInStorageOrder + ? new Pos(start.line, mv(ch, 1), "before") + : new Pos(start.line, ch, "after") + + for (; partPos >= 0 && partPos < bidi.length; partPos += dir) { + let part = bidi[partPos] + let moveInStorageOrder = (dir > 0) == (part.level != 1) + let ch = moveInStorageOrder ? wrappedLineExtent.begin : mv(wrappedLineExtent.end, -1) + if (part.from <= ch && ch < part.to) return getRes(ch, moveInStorageOrder) + ch = moveInStorageOrder ? part.from : mv(part.to, -1) + if (wrappedLineExtent.begin <= ch && ch < wrappedLineExtent.end) return getRes(ch, moveInStorageOrder) + } + } + + // Case 3a: Look for other bidi parts on the same visual line + let res = searchInVisualLine(partPos + dir, dir, wrappedLineExtent) + if (res) return res + + // Case 3b: Look for other bidi parts on the next visual line + let nextCh = dir > 0 ? wrappedLineExtent.end : mv(wrappedLineExtent.begin, -1) + if (nextCh != null && !(dir > 0 && nextCh == line.text.length)) { + res = searchInVisualLine(dir > 0 ? 0 : bidi.length - 1, dir, getWrappedLineExtent(nextCh)) + if (res) return res + } + + // Case 4: Nowhere to move + return null +} diff --git a/docs/js/node_modules/codemirror/src/line/highlight.js b/docs/js/node_modules/codemirror/src/line/highlight.js new file mode 100644 index 000000000..9835d4626 --- /dev/null +++ b/docs/js/node_modules/codemirror/src/line/highlight.js @@ -0,0 +1,284 @@ +import { countColumn } from "../util/misc.js" +import { copyState, innerMode, startState } from "../modes.js" +import StringStream from "../util/StringStream.js" + +import { getLine, lineNo } from "./utils_line.js" +import { clipPos } from "./pos.js" + +class SavedContext { + constructor(state, lookAhead) { + this.state = state + this.lookAhead = lookAhead + } +} + +class Context { + constructor(doc, state, line, lookAhead) { + this.state = state + this.doc = doc + this.line = line + this.maxLookAhead = lookAhead || 0 + this.baseTokens = null + this.baseTokenPos = 1 + } + + lookAhead(n) { + let line = this.doc.getLine(this.line + n) + if (line != null && n > this.maxLookAhead) this.maxLookAhead = n + return line + } + + baseToken(n) { + if (!this.baseTokens) return null + while (this.baseTokens[this.baseTokenPos] <= n) + this.baseTokenPos += 2 + let type = this.baseTokens[this.baseTokenPos + 1] + return {type: type && type.replace(/( |^)overlay .*/, ""), + size: this.baseTokens[this.baseTokenPos] - n} + } + + nextLine() { + this.line++ + if (this.maxLookAhead > 0) this.maxLookAhead-- + } + + static fromSaved(doc, saved, line) { + if (saved instanceof SavedContext) + return new Context(doc, copyState(doc.mode, saved.state), line, saved.lookAhead) + else + return new Context(doc, copyState(doc.mode, saved), line) + } + + save(copy) { + let state = copy !== false ? copyState(this.doc.mode, this.state) : this.state + return this.maxLookAhead > 0 ? new SavedContext(state, this.maxLookAhead) : state + } +} + + +// Compute a style array (an array starting with a mode generation +// -- for invalidation -- followed by pairs of end positions and +// style strings), which is used to highlight the tokens on the +// line. +export function highlightLine(cm, line, context, forceToEnd) { + // A styles array always starts with a number identifying the + // mode/overlays that it is based on (for easy invalidation). + let st = [cm.state.modeGen], lineClasses = {} + // Compute the base array of styles + runMode(cm, line.text, cm.doc.mode, context, (end, style) => st.push(end, style), + lineClasses, forceToEnd) + let state = context.state + + // Run overlays, adjust style array. + for (let o = 0; o < cm.state.overlays.length; ++o) { + context.baseTokens = st + let overlay = cm.state.overlays[o], i = 1, at = 0 + context.state = true + runMode(cm, line.text, overlay.mode, context, (end, style) => { + let start = i + // Ensure there's a token end at the current position, and that i points at it + while (at < end) { + let i_end = st[i] + if (i_end > end) + st.splice(i, 1, end, st[i+1], i_end) + i += 2 + at = Math.min(end, i_end) + } + if (!style) return + if (overlay.opaque) { + st.splice(start, i - start, end, "overlay " + style) + i = start + 2 + } else { + for (; start < i; start += 2) { + let cur = st[start+1] + st[start+1] = (cur ? cur + " " : "") + "overlay " + style + } + } + }, lineClasses) + context.state = state + context.baseTokens = null + context.baseTokenPos = 1 + } + + return {styles: st, classes: lineClasses.bgClass || lineClasses.textClass ? lineClasses : null} +} + +export function getLineStyles(cm, line, updateFrontier) { + if (!line.styles || line.styles[0] != cm.state.modeGen) { + let context = getContextBefore(cm, lineNo(line)) + let resetState = line.text.length > cm.options.maxHighlightLength && copyState(cm.doc.mode, context.state) + let result = highlightLine(cm, line, context) + if (resetState) context.state = resetState + line.stateAfter = context.save(!resetState) + line.styles = result.styles + if (result.classes) line.styleClasses = result.classes + else if (line.styleClasses) line.styleClasses = null + if (updateFrontier === cm.doc.highlightFrontier) + cm.doc.modeFrontier = Math.max(cm.doc.modeFrontier, ++cm.doc.highlightFrontier) + } + return line.styles +} + +export function getContextBefore(cm, n, precise) { + let doc = cm.doc, display = cm.display + if (!doc.mode.startState) return new Context(doc, true, n) + let start = findStartLine(cm, n, precise) + let saved = start > doc.first && getLine(doc, start - 1).stateAfter + let context = saved ? Context.fromSaved(doc, saved, start) : new Context(doc, startState(doc.mode), start) + + doc.iter(start, n, line => { + processLine(cm, line.text, context) + let pos = context.line + line.stateAfter = pos == n - 1 || pos % 5 == 0 || pos >= display.viewFrom && pos < display.viewTo ? context.save() : null + context.nextLine() + }) + if (precise) doc.modeFrontier = context.line + return context +} + +// Lightweight form of highlight -- proceed over this line and +// update state, but don't save a style array. Used for lines that +// aren't currently visible. +export function processLine(cm, text, context, startAt) { + let mode = cm.doc.mode + let stream = new StringStream(text, cm.options.tabSize, context) + stream.start = stream.pos = startAt || 0 + if (text == "") callBlankLine(mode, context.state) + while (!stream.eol()) { + readToken(mode, stream, context.state) + stream.start = stream.pos + } +} + +function callBlankLine(mode, state) { + if (mode.blankLine) return mode.blankLine(state) + if (!mode.innerMode) return + let inner = innerMode(mode, state) + if (inner.mode.blankLine) return inner.mode.blankLine(inner.state) +} + +function readToken(mode, stream, state, inner) { + for (let i = 0; i < 10; i++) { + if (inner) inner[0] = innerMode(mode, state).mode + let style = mode.token(stream, state) + if (stream.pos > stream.start) return style + } + throw new Error("Mode " + mode.name + " failed to advance stream.") +} + +class Token { + constructor(stream, type, state) { + this.start = stream.start; this.end = stream.pos + this.string = stream.current() + this.type = type || null + this.state = state + } +} + +// Utility for getTokenAt and getLineTokens +export function takeToken(cm, pos, precise, asArray) { + let doc = cm.doc, mode = doc.mode, style + pos = clipPos(doc, pos) + let line = getLine(doc, pos.line), context = getContextBefore(cm, pos.line, precise) + let stream = new StringStream(line.text, cm.options.tabSize, context), tokens + if (asArray) tokens = [] + while ((asArray || stream.pos < pos.ch) && !stream.eol()) { + stream.start = stream.pos + style = readToken(mode, stream, context.state) + if (asArray) tokens.push(new Token(stream, style, copyState(doc.mode, context.state))) + } + return asArray ? tokens : new Token(stream, style, context.state) +} + +function extractLineClasses(type, output) { + if (type) for (;;) { + let lineClass = type.match(/(?:^|\s+)line-(background-)?(\S+)/) + if (!lineClass) break + type = type.slice(0, lineClass.index) + type.slice(lineClass.index + lineClass[0].length) + let prop = lineClass[1] ? "bgClass" : "textClass" + if (output[prop] == null) + output[prop] = lineClass[2] + else if (!(new RegExp("(?:^|\s)" + lineClass[2] + "(?:$|\s)")).test(output[prop])) + output[prop] += " " + lineClass[2] + } + return type +} + +// Run the given mode's parser over a line, calling f for each token. +function runMode(cm, text, mode, context, f, lineClasses, forceToEnd) { + let flattenSpans = mode.flattenSpans + if (flattenSpans == null) flattenSpans = cm.options.flattenSpans + let curStart = 0, curStyle = null + let stream = new StringStream(text, cm.options.tabSize, context), style + let inner = cm.options.addModeClass && [null] + if (text == "") extractLineClasses(callBlankLine(mode, context.state), lineClasses) + while (!stream.eol()) { + if (stream.pos > cm.options.maxHighlightLength) { + flattenSpans = false + if (forceToEnd) processLine(cm, text, context, stream.pos) + stream.pos = text.length + style = null + } else { + style = extractLineClasses(readToken(mode, stream, context.state, inner), lineClasses) + } + if (inner) { + let mName = inner[0].name + if (mName) style = "m-" + (style ? mName + " " + style : mName) + } + if (!flattenSpans || curStyle != style) { + while (curStart < stream.start) { + curStart = Math.min(stream.start, curStart + 5000) + f(curStart, curStyle) + } + curStyle = style + } + stream.start = stream.pos + } + while (curStart < stream.pos) { + // Webkit seems to refuse to render text nodes longer than 57444 + // characters, and returns inaccurate measurements in nodes + // starting around 5000 chars. + let pos = Math.min(stream.pos, curStart + 5000) + f(pos, curStyle) + curStart = pos + } +} + +// 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) { + let minindent, minline, doc = cm.doc + let lim = precise ? -1 : n - (cm.doc.mode.innerMode ? 1000 : 100) + for (let search = n; search > lim; --search) { + if (search <= doc.first) return doc.first + let line = getLine(doc, search - 1), after = line.stateAfter + if (after && (!precise || search + (after instanceof SavedContext ? after.lookAhead : 0) <= doc.modeFrontier)) + return search + let indented = countColumn(line.text, null, cm.options.tabSize) + if (minline == null || minindent > indented) { + minline = search - 1 + minindent = indented + } + } + return minline +} + +export function retreatFrontier(doc, n) { + doc.modeFrontier = Math.min(doc.modeFrontier, n) + if (doc.highlightFrontier < n - 10) return + let start = doc.first + for (let line = n - 1; line > start; line--) { + let saved = getLine(doc, line).stateAfter + // change is on 3 + // state on line 1 looked ahead 2 -- so saw 3 + // test 1 + 2 < 3 should cover this + if (saved && (!(saved instanceof SavedContext) || line + saved.lookAhead < n)) { + start = line + 1 + break + } + } + doc.highlightFrontier = Math.min(doc.highlightFrontier, start) +} diff --git a/docs/js/node_modules/codemirror/src/line/line_data.js b/docs/js/node_modules/codemirror/src/line/line_data.js new file mode 100644 index 000000000..20dd43283 --- /dev/null +++ b/docs/js/node_modules/codemirror/src/line/line_data.js @@ -0,0 +1,349 @@ +import { getOrder } from "../util/bidi.js" +import { ie, ie_version, webkit } from "../util/browser.js" +import { elt, eltP, joinClasses } from "../util/dom.js" +import { eventMixin, signal } from "../util/event.js" +import { hasBadBidiRects, zeroWidthElement } from "../util/feature_detection.js" +import { lst, spaceStr } from "../util/misc.js" + +import { getLineStyles } from "./highlight.js" +import { attachMarkedSpans, compareCollapsedMarkers, detachMarkedSpans, lineIsHidden, visualLineContinued } from "./spans.js" +import { getLine, lineNo, updateLineHeight } from "./utils_line.js" + +// LINE DATA STRUCTURE + +// Line objects. These hold state related to a line, including +// highlighting info (the styles array). +export class Line { + constructor(text, markedSpans, estimateHeight) { + this.text = text + attachMarkedSpans(this, markedSpans) + this.height = estimateHeight ? estimateHeight(this) : 1 + } + + lineNo() { return lineNo(this) } +} +eventMixin(Line) + +// Change the content (text, markers) of a line. Automatically +// invalidates cached information and tries to re-estimate the +// line's height. +export function updateLine(line, text, markedSpans, estimateHeight) { + line.text = text + if (line.stateAfter) line.stateAfter = null + if (line.styles) line.styles = null + if (line.order != null) line.order = null + detachMarkedSpans(line) + attachMarkedSpans(line, markedSpans) + let estHeight = estimateHeight ? estimateHeight(line) : 1 + if (estHeight != line.height) updateLineHeight(line, estHeight) +} + +// Detach a line from the document tree and its markers. +export function cleanUpLine(line) { + line.parent = null + detachMarkedSpans(line) +} + +// Convert a style as returned by a mode (either null, or a string +// containing one or more styles) to a CSS style. This is cached, +// and also looks for line-wide styles. +let styleToClassCache = {}, styleToClassCacheWithMode = {} +function interpretTokenStyle(style, options) { + if (!style || /^\s*$/.test(style)) return null + let cache = options.addModeClass ? styleToClassCacheWithMode : styleToClassCache + return cache[style] || + (cache[style] = style.replace(/\S+/g, "cm-$&")) +} + +// Render the DOM representation of the text of a line. Also builds +// up a 'line map', which points at the DOM nodes that represent +// specific stretches of text, and is used by the measuring code. +// The returned object contains the DOM node, this map, and +// information about line-wide styles that were set by the mode. +export function buildLineContent(cm, lineView) { + // The padding-right forces the element to have a 'border', which + // is needed on Webkit to be able to get line-level bounding + // rectangles for it (in measureChar). + let content = eltP("span", null, null, webkit ? "padding-right: .1px" : null) + let builder = {pre: eltP("pre", [content], "CodeMirror-line"), content: content, + col: 0, pos: 0, cm: cm, + trailingSpace: false, + splitSpaces: cm.getOption("lineWrapping")} + lineView.measure = {} + + // Iterate over the logical lines that make up this visual line. + for (let i = 0; i <= (lineView.rest ? lineView.rest.length : 0); i++) { + let line = i ? lineView.rest[i - 1] : lineView.line, order + builder.pos = 0 + builder.addToken = buildToken + // Optionally wire in some hacks into the token-rendering + // algorithm, to deal with browser quirks. + if (hasBadBidiRects(cm.display.measure) && (order = getOrder(line, cm.doc.direction))) + builder.addToken = buildTokenBadBidi(builder.addToken, order) + builder.map = [] + let allowFrontierUpdate = lineView != cm.display.externalMeasured && lineNo(line) + insertLineContent(line, builder, getLineStyles(cm, line, allowFrontierUpdate)) + if (line.styleClasses) { + if (line.styleClasses.bgClass) + builder.bgClass = joinClasses(line.styleClasses.bgClass, builder.bgClass || "") + if (line.styleClasses.textClass) + builder.textClass = joinClasses(line.styleClasses.textClass, builder.textClass || "") + } + + // Ensure at least a single node is present, for measuring. + if (builder.map.length == 0) + builder.map.push(0, 0, builder.content.appendChild(zeroWidthElement(cm.display.measure))) + + // Store the map and a cache object for the current logical line + if (i == 0) { + lineView.measure.map = builder.map + lineView.measure.cache = {} + } else { + ;(lineView.measure.maps || (lineView.measure.maps = [])).push(builder.map) + ;(lineView.measure.caches || (lineView.measure.caches = [])).push({}) + } + } + + // See issue #2901 + if (webkit) { + let last = builder.content.lastChild + if (/\bcm-tab\b/.test(last.className) || (last.querySelector && last.querySelector(".cm-tab"))) + builder.content.className = "cm-tab-wrap-hack" + } + + signal(cm, "renderLine", cm, lineView.line, builder.pre) + if (builder.pre.className) + builder.textClass = joinClasses(builder.pre.className, builder.textClass || "") + + return builder +} + +export function defaultSpecialCharPlaceholder(ch) { + let token = elt("span", "\u2022", "cm-invalidchar") + token.title = "\\u" + ch.charCodeAt(0).toString(16) + token.setAttribute("aria-label", token.title) + return token +} + +// Build up the DOM representation for a single token, and add it to +// the line map. Takes care to render special characters separately. +function buildToken(builder, text, style, startStyle, endStyle, css, attributes) { + if (!text) return + let displayText = builder.splitSpaces ? splitSpaces(text, builder.trailingSpace) : text + let special = builder.cm.state.specialChars, mustWrap = false + let content + if (!special.test(text)) { + builder.col += text.length + content = document.createTextNode(displayText) + builder.map.push(builder.pos, builder.pos + text.length, content) + if (ie && ie_version < 9) mustWrap = true + builder.pos += text.length + } else { + content = document.createDocumentFragment() + let pos = 0 + while (true) { + special.lastIndex = pos + let m = special.exec(text) + let skipped = m ? m.index - pos : text.length - pos + if (skipped) { + let txt = document.createTextNode(displayText.slice(pos, pos + skipped)) + if (ie && ie_version < 9) content.appendChild(elt("span", [txt])) + else content.appendChild(txt) + builder.map.push(builder.pos, builder.pos + skipped, txt) + builder.col += skipped + builder.pos += skipped + } + if (!m) break + pos += skipped + 1 + let txt + if (m[0] == "\t") { + let tabSize = builder.cm.options.tabSize, tabWidth = tabSize - builder.col % tabSize + txt = content.appendChild(elt("span", spaceStr(tabWidth), "cm-tab")) + txt.setAttribute("role", "presentation") + txt.setAttribute("cm-text", "\t") + builder.col += tabWidth + } else if (m[0] == "\r" || m[0] == "\n") { + txt = content.appendChild(elt("span", m[0] == "\r" ? "\u240d" : "\u2424", "cm-invalidchar")) + txt.setAttribute("cm-text", m[0]) + builder.col += 1 + } else { + txt = builder.cm.options.specialCharPlaceholder(m[0]) + txt.setAttribute("cm-text", m[0]) + if (ie && ie_version < 9) content.appendChild(elt("span", [txt])) + else content.appendChild(txt) + builder.col += 1 + } + builder.map.push(builder.pos, builder.pos + 1, txt) + builder.pos++ + } + } + builder.trailingSpace = displayText.charCodeAt(text.length - 1) == 32 + if (style || startStyle || endStyle || mustWrap || css) { + let fullStyle = style || "" + if (startStyle) fullStyle += startStyle + if (endStyle) fullStyle += endStyle + let token = elt("span", [content], fullStyle, css) + if (attributes) { + for (let attr in attributes) if (attributes.hasOwnProperty(attr) && attr != "style" && attr != "class") + token.setAttribute(attr, attributes[attr]) + } + return builder.content.appendChild(token) + } + builder.content.appendChild(content) +} + +// Change some spaces to NBSP to prevent the browser from collapsing +// trailing spaces at the end of a line when rendering text (issue #1362). +function splitSpaces(text, trailingBefore) { + if (text.length > 1 && !/ /.test(text)) return text + let spaceBefore = trailingBefore, result = "" + for (let i = 0; i < text.length; i++) { + let ch = text.charAt(i) + if (ch == " " && spaceBefore && (i == text.length - 1 || text.charCodeAt(i + 1) == 32)) + ch = "\u00a0" + result += ch + spaceBefore = ch == " " + } + return result +} + +// Work around nonsense dimensions being reported for stretches of +// right-to-left text. +function buildTokenBadBidi(inner, order) { + return (builder, text, style, startStyle, endStyle, css, attributes) => { + style = style ? style + " cm-force-border" : "cm-force-border" + let start = builder.pos, end = start + text.length + for (;;) { + // Find the part that overlaps with the start of this text + let part + for (let i = 0; i < order.length; i++) { + part = order[i] + if (part.to > start && part.from <= start) break + } + if (part.to >= end) return inner(builder, text, style, startStyle, endStyle, css, attributes) + inner(builder, text.slice(0, part.to - start), style, startStyle, null, css, attributes) + startStyle = null + text = text.slice(part.to - start) + start = part.to + } + } +} + +function buildCollapsedSpan(builder, size, marker, ignoreWidget) { + let widget = !ignoreWidget && marker.widgetNode + if (widget) builder.map.push(builder.pos, builder.pos + size, widget) + if (!ignoreWidget && builder.cm.display.input.needsContentAttribute) { + if (!widget) + widget = builder.content.appendChild(document.createElement("span")) + widget.setAttribute("cm-marker", marker.id) + } + if (widget) { + builder.cm.display.input.setUneditable(widget) + builder.content.appendChild(widget) + } + builder.pos += size + builder.trailingSpace = false +} + +// Outputs a number of spans to make up a line, taking highlighting +// and marked text into account. +function insertLineContent(line, builder, styles) { + let spans = line.markedSpans, allText = line.text, at = 0 + if (!spans) { + for (let i = 1; i < styles.length; i+=2) + builder.addToken(builder, allText.slice(at, at = styles[i]), interpretTokenStyle(styles[i+1], builder.cm.options)) + return + } + + let len = allText.length, pos = 0, i = 1, text = "", style, css + let nextChange = 0, spanStyle, spanEndStyle, spanStartStyle, collapsed, attributes + for (;;) { + if (nextChange == pos) { // Update current marker set + spanStyle = spanEndStyle = spanStartStyle = css = "" + attributes = null + collapsed = null; nextChange = Infinity + let foundBookmarks = [], endStyles + for (let j = 0; j < spans.length; ++j) { + let sp = spans[j], m = sp.marker + if (m.type == "bookmark" && sp.from == pos && m.widgetNode) { + foundBookmarks.push(m) + } else if (sp.from <= pos && (sp.to == null || sp.to > pos || m.collapsed && sp.to == pos && sp.from == pos)) { + if (sp.to != null && sp.to != pos && nextChange > sp.to) { + nextChange = sp.to + spanEndStyle = "" + } + if (m.className) spanStyle += " " + m.className + if (m.css) css = (css ? css + ";" : "") + m.css + if (m.startStyle && sp.from == pos) spanStartStyle += " " + m.startStyle + if (m.endStyle && sp.to == nextChange) (endStyles || (endStyles = [])).push(m.endStyle, sp.to) + // support for the old title property + // https://github.com/codemirror/CodeMirror/pull/5673 + if (m.title) (attributes || (attributes = {})).title = m.title + if (m.attributes) { + for (let attr in m.attributes) + (attributes || (attributes = {}))[attr] = m.attributes[attr] + } + if (m.collapsed && (!collapsed || compareCollapsedMarkers(collapsed.marker, m) < 0)) + collapsed = sp + } else if (sp.from > pos && nextChange > sp.from) { + nextChange = sp.from + } + } + if (endStyles) for (let j = 0; j < endStyles.length; j += 2) + if (endStyles[j + 1] == nextChange) spanEndStyle += " " + endStyles[j] + + if (!collapsed || collapsed.from == pos) for (let j = 0; j < foundBookmarks.length; ++j) + buildCollapsedSpan(builder, 0, foundBookmarks[j]) + if (collapsed && (collapsed.from || 0) == pos) { + buildCollapsedSpan(builder, (collapsed.to == null ? len + 1 : collapsed.to) - pos, + collapsed.marker, collapsed.from == null) + if (collapsed.to == null) return + if (collapsed.to == pos) collapsed = false + } + } + if (pos >= len) break + + let upto = Math.min(len, nextChange) + while (true) { + if (text) { + let end = pos + text.length + if (!collapsed) { + let tokenText = end > upto ? text.slice(0, upto - pos) : text + builder.addToken(builder, tokenText, style ? style + spanStyle : spanStyle, + spanStartStyle, pos + tokenText.length == nextChange ? spanEndStyle : "", css, attributes) + } + if (end >= upto) {text = text.slice(upto - pos); pos = upto; break} + pos = end + spanStartStyle = "" + } + text = allText.slice(at, at = styles[i++]) + style = interpretTokenStyle(styles[i++], builder.cm.options) + } + } +} + + +// 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. +export 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. +export function buildViewArray(cm, from, to) { + let array = [], nextPos + for (let pos = from; pos < to; pos = nextPos) { + let view = new LineView(cm.doc, getLine(cm.doc, pos), pos) + nextPos = pos + view.size + array.push(view) + } + return array +} diff --git a/docs/js/node_modules/codemirror/src/line/pos.js b/docs/js/node_modules/codemirror/src/line/pos.js new file mode 100644 index 000000000..2a498f8f3 --- /dev/null +++ b/docs/js/node_modules/codemirror/src/line/pos.js @@ -0,0 +1,40 @@ +import { getLine } from "./utils_line.js" + +// A Pos instance represents a position within the text. +export function Pos(line, ch, sticky = null) { + if (!(this instanceof Pos)) return new Pos(line, ch, sticky) + this.line = line + this.ch = ch + this.sticky = sticky +} + +// Compare two positions, return 0 if they are the same, a negative +// number when a is less, and a positive number otherwise. +export function cmp(a, b) { return a.line - b.line || a.ch - b.ch } + +export function equalCursorPos(a, b) { return a.sticky == b.sticky && cmp(a, b) == 0 } + +export function copyPos(x) {return Pos(x.line, x.ch)} +export function maxPos(a, b) { return cmp(a, b) < 0 ? b : a } +export function minPos(a, b) { return cmp(a, b) < 0 ? a : b } + +// Most of the external API clips given positions to make sure they +// actually exist within the document. +export function clipLine(doc, n) {return Math.max(doc.first, Math.min(n, doc.first + doc.size - 1))} +export function clipPos(doc, pos) { + if (pos.line < doc.first) return Pos(doc.first, 0) + let 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) { + let 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 +} +export function clipPosArray(doc, array) { + let out = [] + for (let i = 0; i < array.length; i++) out[i] = clipPos(doc, array[i]) + return out +} diff --git a/docs/js/node_modules/codemirror/src/line/saw_special_spans.js b/docs/js/node_modules/codemirror/src/line/saw_special_spans.js new file mode 100644 index 000000000..d315e7ba9 --- /dev/null +++ b/docs/js/node_modules/codemirror/src/line/saw_special_spans.js @@ -0,0 +1,10 @@ +// Optimize some code when these features are not used. +export let sawReadOnlySpans = false, sawCollapsedSpans = false + +export function seeReadOnlySpans() { + sawReadOnlySpans = true +} + +export function seeCollapsedSpans() { + sawCollapsedSpans = true +} diff --git a/docs/js/node_modules/codemirror/src/line/spans.js b/docs/js/node_modules/codemirror/src/line/spans.js new file mode 100644 index 000000000..d81dec4b8 --- /dev/null +++ b/docs/js/node_modules/codemirror/src/line/spans.js @@ -0,0 +1,382 @@ +import { indexOf, lst } from "../util/misc.js" + +import { cmp } from "./pos.js" +import { sawCollapsedSpans } from "./saw_special_spans.js" +import { getLine, isLine, lineNo } from "./utils_line.js" + +// TEXTMARKER SPANS + +export function MarkedSpan(marker, from, to) { + this.marker = marker + this.from = from; this.to = to +} + +// Search an array of spans for a span matching the given marker. +export function getMarkedSpanFor(spans, marker) { + if (spans) for (let i = 0; i < spans.length; ++i) { + let span = spans[i] + if (span.marker == marker) return span + } +} +// Remove a span from an array, returning undefined if no spans are +// left (we don't store arrays for lines without spans). +export function removeMarkedSpan(spans, span) { + let r + for (let i = 0; i < spans.length; ++i) + if (spans[i] != span) (r || (r = [])).push(spans[i]) + return r +} +// Add a span to a line. +export function addMarkedSpan(line, span) { + line.markedSpans = line.markedSpans ? line.markedSpans.concat([span]) : [span] + span.marker.attachLine(line) +} + +// Used for the algorithm that adjusts markers for a change in the +// document. These functions cut an array of spans at a given +// character position, returning an array of remaining chunks (or +// undefined if nothing remains). +function markedSpansBefore(old, startCh, isInsert) { + let nw + if (old) for (let i = 0; i < old.length; ++i) { + let span = old[i], marker = span.marker + let startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= startCh : span.from < startCh) + if (startsBefore || span.from == startCh && marker.type == "bookmark" && (!isInsert || !span.marker.insertLeft)) { + let endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= startCh : span.to > startCh) + ;(nw || (nw = [])).push(new MarkedSpan(marker, span.from, endsAfter ? null : span.to)) + } + } + return nw +} +function markedSpansAfter(old, endCh, isInsert) { + let nw + if (old) for (let i = 0; i < old.length; ++i) { + let span = old[i], marker = span.marker + let endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= endCh : span.to > endCh) + if (endsAfter || span.from == endCh && marker.type == "bookmark" && (!isInsert || span.marker.insertLeft)) { + let startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= endCh : span.from < endCh) + ;(nw || (nw = [])).push(new MarkedSpan(marker, startsBefore ? null : span.from - endCh, + span.to == null ? null : span.to - endCh)) + } + } + return nw +} + +// Given a change object, compute the new set of marker spans that +// cover the line in which the change took place. Removes spans +// entirely within the change, reconnects spans belonging to the +// same marker that appear on both sides of the change, and cuts off +// spans partially within the change. Returns an array of span +// arrays with one element for each line in (after) the change. +export function stretchSpansOverChange(doc, change) { + if (change.full) return null + let oldFirst = isLine(doc, change.from.line) && getLine(doc, change.from.line).markedSpans + let oldLast = isLine(doc, change.to.line) && getLine(doc, change.to.line).markedSpans + if (!oldFirst && !oldLast) return null + + let startCh = change.from.ch, endCh = change.to.ch, isInsert = cmp(change.from, change.to) == 0 + // Get the spans that 'stick out' on both sides + let first = markedSpansBefore(oldFirst, startCh, isInsert) + let last = markedSpansAfter(oldLast, endCh, isInsert) + + // Next, merge those two ends + let sameLine = change.text.length == 1, offset = lst(change.text).length + (sameLine ? startCh : 0) + if (first) { + // Fix up .to properties of first + for (let i = 0; i < first.length; ++i) { + let span = first[i] + if (span.to == null) { + let found = getMarkedSpanFor(last, span.marker) + if (!found) span.to = startCh + else if (sameLine) span.to = found.to == null ? null : found.to + offset + } + } + } + if (last) { + // Fix up .from in last (or move them into first in case of sameLine) + for (let i = 0; i < last.length; ++i) { + let span = last[i] + if (span.to != null) span.to += offset + if (span.from == null) { + let found = getMarkedSpanFor(first, span.marker) + if (!found) { + span.from = offset + if (sameLine) (first || (first = [])).push(span) + } + } else { + span.from += offset + if (sameLine) (first || (first = [])).push(span) + } + } + } + // Make sure we didn't create any zero-length spans + if (first) first = clearEmptySpans(first) + if (last && last != first) last = clearEmptySpans(last) + + let newMarkers = [first] + if (!sameLine) { + // Fill gap with whole-line-spans + let gap = change.text.length - 2, gapMarkers + if (gap > 0 && first) + for (let i = 0; i < first.length; ++i) + if (first[i].to == null) + (gapMarkers || (gapMarkers = [])).push(new MarkedSpan(first[i].marker, null, null)) + for (let i = 0; i < gap; ++i) + newMarkers.push(gapMarkers) + newMarkers.push(last) + } + return newMarkers +} + +// Remove spans that are empty and don't have a clearWhenEmpty +// option of false. +function clearEmptySpans(spans) { + for (let i = 0; i < spans.length; ++i) { + let span = spans[i] + if (span.from != null && span.from == span.to && span.marker.clearWhenEmpty !== false) + spans.splice(i--, 1) + } + if (!spans.length) return null + return spans +} + +// Used to 'clip' out readOnly ranges when making a change. +export function removeReadOnlyRanges(doc, from, to) { + let markers = null + doc.iter(from.line, to.line + 1, line => { + if (line.markedSpans) for (let i = 0; i < line.markedSpans.length; ++i) { + let mark = line.markedSpans[i].marker + if (mark.readOnly && (!markers || indexOf(markers, mark) == -1)) + (markers || (markers = [])).push(mark) + } + }) + if (!markers) return null + let parts = [{from: from, to: to}] + for (let i = 0; i < markers.length; ++i) { + let mk = markers[i], m = mk.find(0) + for (let j = 0; j < parts.length; ++j) { + let p = parts[j] + if (cmp(p.to, m.from) < 0 || cmp(p.from, m.to) > 0) continue + let newParts = [j, 1], dfrom = cmp(p.from, m.from), dto = cmp(p.to, m.to) + if (dfrom < 0 || !mk.inclusiveLeft && !dfrom) + newParts.push({from: p.from, to: m.from}) + if (dto > 0 || !mk.inclusiveRight && !dto) + newParts.push({from: m.to, to: p.to}) + parts.splice.apply(parts, newParts) + j += newParts.length - 3 + } + } + return parts +} + +// Connect or disconnect spans from a line. +export function detachMarkedSpans(line) { + let spans = line.markedSpans + if (!spans) return + for (let i = 0; i < spans.length; ++i) + spans[i].marker.detachLine(line) + line.markedSpans = null +} +export function attachMarkedSpans(line, spans) { + if (!spans) return + for (let i = 0; i < spans.length; ++i) + spans[i].marker.attachLine(line) + line.markedSpans = spans +} + +// Helpers used when computing which overlapping collapsed span +// counts as the larger one. +function extraLeft(marker) { return marker.inclusiveLeft ? -1 : 0 } +function extraRight(marker) { return marker.inclusiveRight ? 1 : 0 } + +// Returns a number indicating which of two overlapping collapsed +// spans is larger (and thus includes the other). Falls back to +// comparing ids when the spans cover exactly the same range. +export function compareCollapsedMarkers(a, b) { + let lenDiff = a.lines.length - b.lines.length + if (lenDiff != 0) return lenDiff + let aPos = a.find(), bPos = b.find() + let fromCmp = cmp(aPos.from, bPos.from) || extraLeft(a) - extraLeft(b) + if (fromCmp) return -fromCmp + let toCmp = cmp(aPos.to, bPos.to) || extraRight(a) - extraRight(b) + if (toCmp) return toCmp + return b.id - a.id +} + +// Find out whether a line ends or starts in a collapsed span. If +// so, return the marker for that span. +function collapsedSpanAtSide(line, start) { + let sps = sawCollapsedSpans && line.markedSpans, found + if (sps) for (let sp, i = 0; i < sps.length; ++i) { + sp = sps[i] + if (sp.marker.collapsed && (start ? sp.from : sp.to) == null && + (!found || compareCollapsedMarkers(found, sp.marker) < 0)) + found = sp.marker + } + return found +} +export function collapsedSpanAtStart(line) { return collapsedSpanAtSide(line, true) } +export function collapsedSpanAtEnd(line) { return collapsedSpanAtSide(line, false) } + +export function collapsedSpanAround(line, ch) { + let sps = sawCollapsedSpans && line.markedSpans, found + if (sps) for (let i = 0; i < sps.length; ++i) { + let sp = sps[i] + if (sp.marker.collapsed && (sp.from == null || sp.from < ch) && (sp.to == null || sp.to > ch) && + (!found || compareCollapsedMarkers(found, sp.marker) < 0)) found = sp.marker + } + return found +} + +// Test whether there exists a collapsed span that partially +// overlaps (covers the start or end, but not both) of a new span. +// Such overlap is not allowed. +export function conflictingCollapsedRange(doc, lineNo, from, to, marker) { + let line = getLine(doc, lineNo) + let sps = sawCollapsedSpans && line.markedSpans + if (sps) for (let i = 0; i < sps.length; ++i) { + let sp = sps[i] + if (!sp.marker.collapsed) continue + let found = sp.marker.find(0) + let fromCmp = cmp(found.from, from) || extraLeft(sp.marker) - extraLeft(marker) + let toCmp = cmp(found.to, to) || extraRight(sp.marker) - extraRight(marker) + if (fromCmp >= 0 && toCmp <= 0 || fromCmp <= 0 && toCmp >= 0) continue + if (fromCmp <= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cmp(found.to, from) >= 0 : cmp(found.to, from) > 0) || + fromCmp >= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cmp(found.from, to) <= 0 : cmp(found.from, to) < 0)) + return true + } +} + +// A visual line is a line as drawn on the screen. Folding, for +// example, can cause multiple logical lines to appear on the same +// visual line. This finds the start of the visual line that the +// given line is part of (usually that is the line itself). +export function visualLine(line) { + let merged + while (merged = collapsedSpanAtStart(line)) + line = merged.find(-1, true).line + return line +} + +export function visualLineEnd(line) { + let merged + while (merged = collapsedSpanAtEnd(line)) + line = merged.find(1, true).line + return line +} + +// Returns an array of logical lines that continue the visual line +// started by the argument, or undefined if there are no such lines. +export function visualLineContinued(line) { + let merged, lines + while (merged = collapsedSpanAtEnd(line)) { + line = merged.find(1, true).line + ;(lines || (lines = [])).push(line) + } + return lines +} + +// Get the line number of the start of the visual line that the +// given line number is part of. +export function visualLineNo(doc, lineN) { + let line = getLine(doc, lineN), vis = visualLine(line) + if (line == vis) return lineN + return lineNo(vis) +} + +// Get the line number of the start of the next visual line after +// the given line. +export function visualLineEndNo(doc, lineN) { + if (lineN > doc.lastLine()) return lineN + let line = getLine(doc, lineN), merged + if (!lineIsHidden(doc, line)) return lineN + while (merged = collapsedSpanAtEnd(line)) + line = merged.find(1, true).line + return lineNo(line) + 1 +} + +// Compute whether a line is hidden. Lines count as hidden when they +// are part of a visual line that starts with another line, or when +// they are entirely covered by collapsed, non-widget span. +export function lineIsHidden(doc, line) { + let sps = sawCollapsedSpans && line.markedSpans + if (sps) for (let sp, i = 0; i < sps.length; ++i) { + sp = sps[i] + if (!sp.marker.collapsed) continue + if (sp.from == null) return true + if (sp.marker.widgetNode) continue + if (sp.from == 0 && sp.marker.inclusiveLeft && lineIsHiddenInner(doc, line, sp)) + return true + } +} +function lineIsHiddenInner(doc, line, span) { + if (span.to == null) { + let end = span.marker.find(1, true) + return lineIsHiddenInner(doc, end.line, getMarkedSpanFor(end.line.markedSpans, span.marker)) + } + if (span.marker.inclusiveRight && span.to == line.text.length) + return true + for (let sp, i = 0; i < line.markedSpans.length; ++i) { + sp = line.markedSpans[i] + if (sp.marker.collapsed && !sp.marker.widgetNode && sp.from == span.to && + (sp.to == null || sp.to != span.from) && + (sp.marker.inclusiveLeft || span.marker.inclusiveRight) && + lineIsHiddenInner(doc, line, sp)) return true + } +} + +// Find the height above the given line. +export function heightAtLine(lineObj) { + lineObj = visualLine(lineObj) + + let h = 0, chunk = lineObj.parent + for (let i = 0; i < chunk.lines.length; ++i) { + let line = chunk.lines[i] + if (line == lineObj) break + else h += line.height + } + for (let p = chunk.parent; p; chunk = p, p = chunk.parent) { + for (let i = 0; i < p.children.length; ++i) { + let cur = p.children[i] + if (cur == chunk) break + else h += cur.height + } + } + return h +} + +// Compute the character length of a line, taking into account +// collapsed ranges (see markText) that might hide parts, and join +// other lines onto it. +export function lineLength(line) { + if (line.height == 0) return 0 + let len = line.text.length, merged, cur = line + while (merged = collapsedSpanAtStart(cur)) { + let found = merged.find(0, true) + cur = found.from.line + len += found.from.ch - found.to.ch + } + cur = line + while (merged = collapsedSpanAtEnd(cur)) { + let 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. +export function findMaxLine(cm) { + let d = cm.display, doc = cm.doc + d.maxLine = getLine(doc, doc.first) + d.maxLineLength = lineLength(d.maxLine) + d.maxLineChanged = true + doc.iter(line => { + let len = lineLength(line) + if (len > d.maxLineLength) { + d.maxLineLength = len + d.maxLine = line + } + }) +} diff --git a/docs/js/node_modules/codemirror/src/line/utils_line.js b/docs/js/node_modules/codemirror/src/line/utils_line.js new file mode 100644 index 000000000..c88629435 --- /dev/null +++ b/docs/js/node_modules/codemirror/src/line/utils_line.js @@ -0,0 +1,85 @@ +import { indexOf } from "../util/misc.js" + +// Find the line object corresponding to the given line number. +export function getLine(doc, n) { + n -= doc.first + if (n < 0 || n >= doc.size) throw new Error("There is no line " + (n + doc.first) + " in the document.") + let chunk = doc + while (!chunk.lines) { + for (let i = 0;; ++i) { + let child = chunk.children[i], sz = child.chunkSize() + if (n < sz) { chunk = child; break } + n -= sz + } + } + return chunk.lines[n] +} + +// Get the part of a document between two positions, as an array of +// strings. +export function getBetween(doc, start, end) { + let out = [], n = start.line + doc.iter(start.line, end.line + 1, line => { + let text = line.text + if (n == end.line) text = text.slice(0, end.ch) + if (n == start.line) text = text.slice(start.ch) + out.push(text) + ++n + }) + return out +} +// Get the lines between from and to, as array of strings. +export function getLines(doc, from, to) { + let out = [] + doc.iter(from, to, line => { out.push(line.text) }) // iter aborts when callback returns truthy value + return out +} + +// Update the height of a line, propagating the height change +// upwards to parent nodes. +export function updateLineHeight(line, height) { + let diff = height - line.height + if (diff) for (let n = line; n; n = n.parent) n.height += diff +} + +// Given a line object, find its line number by walking up through +// its parent links. +export function lineNo(line) { + if (line.parent == null) return null + let cur = line.parent, no = indexOf(cur.lines, line) + for (let chunk = cur.parent; chunk; cur = chunk, chunk = chunk.parent) { + for (let i = 0;; ++i) { + if (chunk.children[i] == cur) break + no += chunk.children[i].chunkSize() + } + } + return no + cur.first +} + +// Find the line at the given vertical position, using the height +// information in the document tree. +export function lineAtHeight(chunk, h) { + let n = chunk.first + outer: do { + for (let i = 0; i < chunk.children.length; ++i) { + let child = chunk.children[i], ch = child.height + if (h < ch) { chunk = child; continue outer } + h -= ch + n += child.chunkSize() + } + return n + } while (!chunk.lines) + let i = 0 + for (; i < chunk.lines.length; ++i) { + let line = chunk.lines[i], lh = line.height + if (h < lh) break + h -= lh + } + return n + i +} + +export function isLine(doc, l) {return l >= doc.first && l < doc.first + doc.size} + +export function lineNumberFor(options, i) { + return String(options.lineNumberFormatter(i + options.firstLineNumber)) +} diff --git a/docs/js/node_modules/codemirror/src/measurement/position_measurement.js b/docs/js/node_modules/codemirror/src/measurement/position_measurement.js new file mode 100644 index 000000000..18ec11df8 --- /dev/null +++ b/docs/js/node_modules/codemirror/src/measurement/position_measurement.js @@ -0,0 +1,700 @@ +import { buildLineContent, LineView } from "../line/line_data.js" +import { clipPos, Pos } from "../line/pos.js" +import { collapsedSpanAround, heightAtLine, lineIsHidden, visualLine } from "../line/spans.js" +import { getLine, lineAtHeight, lineNo, updateLineHeight } from "../line/utils_line.js" +import { bidiOther, getBidiPartAt, getOrder } from "../util/bidi.js" +import { chrome, android, ie, ie_version } from "../util/browser.js" +import { elt, removeChildren, range, removeChildrenAndAdd } from "../util/dom.js" +import { e_target } from "../util/event.js" +import { hasBadZoomedRects } from "../util/feature_detection.js" +import { countColumn, findFirst, isExtendingChar, scrollerGap, skipExtendingChars } from "../util/misc.js" +import { updateLineForChanges } from "../display/update_line.js" + +import { widgetHeight } from "./widgets.js" + +// POSITION MEASUREMENT + +export function paddingTop(display) {return display.lineSpace.offsetTop} +export function paddingVert(display) {return display.mover.offsetHeight - display.lineSpace.offsetHeight} +export function paddingH(display) { + if (display.cachedPaddingH) return display.cachedPaddingH + let e = removeChildrenAndAdd(display.measure, elt("pre", "x", "CodeMirror-line-like")) + let style = window.getComputedStyle ? window.getComputedStyle(e) : e.currentStyle + let data = {left: parseInt(style.paddingLeft), right: parseInt(style.paddingRight)} + if (!isNaN(data.left) && !isNaN(data.right)) display.cachedPaddingH = data + return data +} + +export function scrollGap(cm) { return scrollerGap - cm.display.nativeBarWidth } +export function displayWidth(cm) { + return cm.display.scroller.clientWidth - scrollGap(cm) - cm.display.barWidth +} +export 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) { + let wrapping = cm.options.lineWrapping + let curWidth = wrapping && displayWidth(cm) + if (!lineView.measure.heights || wrapping && lineView.measure.width != curWidth) { + let heights = lineView.measure.heights = [] + if (wrapping) { + lineView.measure.width = curWidth + let rects = lineView.text.firstChild.getClientRects() + for (let i = 0; i < rects.length - 1; i++) { + let 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.) +export function mapFromLineView(lineView, line, lineN) { + if (lineView.line == line) + return {map: lineView.measure.map, cache: lineView.measure.cache} + for (let i = 0; i < lineView.rest.length; i++) + if (lineView.rest[i] == line) + return {map: lineView.measure.maps[i], cache: lineView.measure.caches[i]} + for (let 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) + let lineN = lineNo(line) + let view = cm.display.externalMeasured = new LineView(cm.doc, line, lineN) + view.lineN = lineN + let 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. +export 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. +export function findViewForLine(cm, lineN) { + if (lineN >= cm.display.viewFrom && lineN < cm.display.viewTo) + return cm.display.view[findViewIndex(cm, lineN)] + let 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. +export function prepareMeasureForLine(cm, line) { + let lineN = lineNo(line) + let view = findViewForLine(cm, lineN) + if (view && !view.text) { + view = null + } else if (view && view.changes) { + updateLineForChanges(cm, view, lineN, getDimensions(cm)) + cm.curOp.forceUpdate = true + } + if (!view) + view = updateExternalMeasurement(cm, line) + + let 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). +export function measureCharPrepared(cm, prepared, ch, bias, varHeight) { + if (prepared.before) ch = -1 + let 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} +} + +let nullRect = {left: 0, right: 0, top: 0, bottom: 0} + +export function nodeAndOffsetInLineMap(map, ch, bias) { + let node, start, end, collapse, mStart, mEnd + // First, search the line map for the text node corresponding to, + // or closest to, the target character. + for (let i = 0; i < map.length; i += 3) { + 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 getUsefulRect(rects, bias) { + let rect = nullRect + if (bias == "left") for (let i = 0; i < rects.length; i++) { + if ((rect = rects[i]).left != rect.right) break + } else for (let i = rects.length - 1; i >= 0; i--) { + if ((rect = rects[i]).left != rect.right) break + } + return rect +} + +function measureCharInner(cm, prepared, ch, bias) { + let place = nodeAndOffsetInLineMap(prepared.map, ch, bias) + let node = place.node, start = place.start, end = place.end, collapse = place.collapse + + let rect + if (node.nodeType == 3) { // If it is a text node, use a range to retrieve the coordinates. + for (let 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 + rect = getUsefulRect(range(node, start, end).getClientRects(), bias) + 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" + let 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)) { + let 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 + } + + let rtop = rect.top - prepared.rect.top, rbot = rect.bottom - prepared.rect.top + let mid = (rtop + rbot) / 2 + let heights = prepared.view.measure.heights + let i = 0 + for (; i < heights.length - 1; i++) + if (mid < heights[i]) break + let top = i ? heights[i - 1] : 0, bot = heights[i] + let 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 + let scaleX = screen.logicalXDPI / screen.deviceXDPI + let scaleY = screen.logicalYDPI / screen.deviceYDPI + return {left: rect.left * scaleX, right: rect.right * scaleX, + top: rect.top * scaleY, bottom: rect.bottom * scaleY} +} + +export function clearLineMeasurementCacheFor(lineView) { + if (lineView.measure) { + lineView.measure.cache = {} + lineView.measure.heights = null + if (lineView.rest) for (let i = 0; i < lineView.rest.length; i++) + lineView.measure.caches[i] = {} + } +} + +export function clearLineMeasurementCache(cm) { + cm.display.externalMeasure = null + removeChildren(cm.display.lineMeasure) + for (let i = 0; i < cm.display.view.length; i++) + clearLineMeasurementCacheFor(cm.display.view[i]) +} + +export 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() { + // Work around https://bugs.chromium.org/p/chromium/issues/detail?id=489206 + // which causes page_Offset and bounding client rects to use + // different reference viewports and invalidate our calculations. + if (chrome && android) return -(document.body.getBoundingClientRect().left - parseInt(getComputedStyle(document.body).marginLeft)) + return window.pageXOffset || (document.documentElement || document.body).scrollLeft +} +function pageScrollY() { + if (chrome && android) return -(document.body.getBoundingClientRect().top - parseInt(getComputedStyle(document.body).marginTop)) + return window.pageYOffset || (document.documentElement || document.body).scrollTop +} + +function widgetTopHeight(lineObj) { + let height = 0 + if (lineObj.widgets) for (let i = 0; i < lineObj.widgets.length; ++i) if (lineObj.widgets[i].above) + height += widgetHeight(lineObj.widgets[i]) + return height +} + +// 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". +export function intoCoordSystem(cm, lineObj, rect, context, includeWidgets) { + if (!includeWidgets) { + let height = widgetTopHeight(lineObj) + rect.top += height; rect.bottom += height + } + if (context == "line") return rect + if (!context) context = "local" + let yOff = heightAtLine(lineObj) + if (context == "local") yOff += paddingTop(cm.display) + else yOff -= cm.display.viewOffset + if (context == "page" || context == "window") { + let lOff = cm.display.lineSpace.getBoundingClientRect() + yOff += lOff.top + (context == "window" ? 0 : pageScrollY()) + let 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. +export function fromCoordSystem(cm, coords, context) { + if (context == "div") return coords + let left = coords.left, top = coords.top + // First move into "page" coordinate system + if (context == "page") { + left -= pageScrollX() + top -= pageScrollY() + } else if (context == "local" || !context) { + let localBox = cm.display.sizer.getBoundingClientRect() + left += localBox.left + top += localBox.top + } + + let lineSpaceBox = cm.display.lineSpace.getBoundingClientRect() + return {left: left - lineSpaceBox.left, top: top - lineSpaceBox.top} +} + +export 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. +// A cursor Pos(line, char, "before") is on the same visual line as `char - 1` +// and after `char - 1` in writing order of `char - 1` +// A cursor Pos(line, char, "after") is on the same visual line as `char` +// and before `char` in writing order of `char` +// Examples (upper-case letters are RTL, lower-case are LTR): +// Pos(0, 1, ...) +// before after +// ab a|b a|b +// aB a|B aB| +// Ab |Ab A|b +// AB B|A B|A +// Every position after the last character on a line is considered to stick +// to the last character on the line. +export 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) { + let 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) + } + let order = getOrder(lineObj, cm.doc.direction), ch = pos.ch, sticky = pos.sticky + if (ch >= lineObj.text.length) { + ch = lineObj.text.length + sticky = "before" + } else if (ch <= 0) { + ch = 0 + sticky = "after" + } + if (!order) return get(sticky == "before" ? ch - 1 : ch, sticky == "before") + + function getBidi(ch, partPos, invert) { + let part = order[partPos], right = part.level == 1 + return get(invert ? ch - 1 : ch, right != invert) + } + let partPos = getBidiPartAt(order, ch, sticky) + let other = bidiOther + let val = getBidi(ch, partPos, sticky == "before") + if (other != null) val.other = getBidi(ch, other, sticky != "before") + return val +} + +// Used to cheaply estimate the coordinates for a position. Used for +// intermediate scroll updates. +export function estimateCoords(cm, pos) { + let left = 0 + pos = clipPos(cm.doc, pos) + if (!cm.options.lineWrapping) left = charWidth(cm.display) * pos.ch + let lineObj = getLine(cm.doc, pos.line) + let 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, sticky, outside, xRel) { + let pos = Pos(line, ch, sticky) + pos.xRel = xRel + if (outside) pos.outside = outside + return pos +} + +// Compute the character position closest to the given coordinates. +// Input must be lineSpace-local ("div" coordinate system). +export function coordsChar(cm, x, y) { + let doc = cm.doc + y += cm.display.viewOffset + if (y < 0) return PosWithInfo(doc.first, 0, null, -1, -1) + let 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, null, 1, 1) + if (x < 0) x = 0 + + let lineObj = getLine(doc, lineN) + for (;;) { + let found = coordsCharInner(cm, lineObj, lineN, x, y) + let collapsed = collapsedSpanAround(lineObj, found.ch + (found.xRel > 0 || found.outside > 0 ? 1 : 0)) + if (!collapsed) return found + let rangeEnd = collapsed.find(1) + if (rangeEnd.line == lineN) return rangeEnd + lineObj = getLine(doc, lineN = rangeEnd.line) + } +} + +function wrappedLineExtent(cm, lineObj, preparedMeasure, y) { + y -= widgetTopHeight(lineObj) + let end = lineObj.text.length + let begin = findFirst(ch => measureCharPrepared(cm, preparedMeasure, ch - 1).bottom <= y, end, 0) + end = findFirst(ch => measureCharPrepared(cm, preparedMeasure, ch).top > y, begin, end) + return {begin, end} +} + +export function wrappedLineExtentChar(cm, lineObj, preparedMeasure, target) { + if (!preparedMeasure) preparedMeasure = prepareMeasureForLine(cm, lineObj) + let targetTop = intoCoordSystem(cm, lineObj, measureCharPrepared(cm, preparedMeasure, target), "line").top + return wrappedLineExtent(cm, lineObj, preparedMeasure, targetTop) +} + +// Returns true if the given side of a box is after the given +// coordinates, in top-to-bottom, left-to-right order. +function boxIsAfter(box, x, y, left) { + return box.bottom <= y ? false : box.top > y ? true : (left ? box.left : box.right) > x +} + +function coordsCharInner(cm, lineObj, lineNo, x, y) { + // Move y into line-local coordinate space + y -= heightAtLine(lineObj) + let preparedMeasure = prepareMeasureForLine(cm, lineObj) + // When directly calling `measureCharPrepared`, we have to adjust + // for the widgets at this line. + let widgetHeight = widgetTopHeight(lineObj) + let begin = 0, end = lineObj.text.length, ltr = true + + let order = getOrder(lineObj, cm.doc.direction) + // If the line isn't plain left-to-right text, first figure out + // which bidi section the coordinates fall into. + if (order) { + let part = (cm.options.lineWrapping ? coordsBidiPartWrapped : coordsBidiPart) + (cm, lineObj, lineNo, preparedMeasure, order, x, y) + ltr = part.level != 1 + // The awkward -1 offsets are needed because findFirst (called + // on these below) will treat its first bound as inclusive, + // second as exclusive, but we want to actually address the + // characters in the part's range + begin = ltr ? part.from : part.to - 1 + end = ltr ? part.to : part.from - 1 + } + + // A binary search to find the first character whose bounding box + // starts after the coordinates. If we run across any whose box wrap + // the coordinates, store that. + let chAround = null, boxAround = null + let ch = findFirst(ch => { + let box = measureCharPrepared(cm, preparedMeasure, ch) + box.top += widgetHeight; box.bottom += widgetHeight + if (!boxIsAfter(box, x, y, false)) return false + if (box.top <= y && box.left <= x) { + chAround = ch + boxAround = box + } + return true + }, begin, end) + + let baseX, sticky, outside = false + // If a box around the coordinates was found, use that + if (boxAround) { + // Distinguish coordinates nearer to the left or right side of the box + let atLeft = x - boxAround.left < boxAround.right - x, atStart = atLeft == ltr + ch = chAround + (atStart ? 0 : 1) + sticky = atStart ? "after" : "before" + baseX = atLeft ? boxAround.left : boxAround.right + } else { + // (Adjust for extended bound, if necessary.) + if (!ltr && (ch == end || ch == begin)) ch++ + // To determine which side to associate with, get the box to the + // left of the character and compare it's vertical position to the + // coordinates + sticky = ch == 0 ? "after" : ch == lineObj.text.length ? "before" : + (measureCharPrepared(cm, preparedMeasure, ch - (ltr ? 1 : 0)).bottom + widgetHeight <= y) == ltr ? + "after" : "before" + // Now get accurate coordinates for this place, in order to get a + // base X position + let coords = cursorCoords(cm, Pos(lineNo, ch, sticky), "line", lineObj, preparedMeasure) + baseX = coords.left + outside = y < coords.top ? -1 : y >= coords.bottom ? 1 : 0 + } + + ch = skipExtendingChars(lineObj.text, ch, 1) + return PosWithInfo(lineNo, ch, sticky, outside, x - baseX) +} + +function coordsBidiPart(cm, lineObj, lineNo, preparedMeasure, order, x, y) { + // Bidi parts are sorted left-to-right, and in a non-line-wrapping + // situation, we can take this ordering to correspond to the visual + // ordering. This finds the first part whose end is after the given + // coordinates. + let index = findFirst(i => { + let part = order[i], ltr = part.level != 1 + return boxIsAfter(cursorCoords(cm, Pos(lineNo, ltr ? part.to : part.from, ltr ? "before" : "after"), + "line", lineObj, preparedMeasure), x, y, true) + }, 0, order.length - 1) + let part = order[index] + // If this isn't the first part, the part's start is also after + // the coordinates, and the coordinates aren't on the same line as + // that start, move one part back. + if (index > 0) { + let ltr = part.level != 1 + let start = cursorCoords(cm, Pos(lineNo, ltr ? part.from : part.to, ltr ? "after" : "before"), + "line", lineObj, preparedMeasure) + if (boxIsAfter(start, x, y, true) && start.top > y) + part = order[index - 1] + } + return part +} + +function coordsBidiPartWrapped(cm, lineObj, _lineNo, preparedMeasure, order, x, y) { + // In a wrapped line, rtl text on wrapping boundaries can do things + // that don't correspond to the ordering in our `order` array at + // all, so a binary search doesn't work, and we want to return a + // part that only spans one line so that the binary search in + // coordsCharInner is safe. As such, we first find the extent of the + // wrapped line, and then do a flat search in which we discard any + // spans that aren't on the line. + let {begin, end} = wrappedLineExtent(cm, lineObj, preparedMeasure, y) + if (/\s/.test(lineObj.text.charAt(end - 1))) end-- + let part = null, closestDist = null + for (let i = 0; i < order.length; i++) { + let p = order[i] + if (p.from >= end || p.to <= begin) continue + let ltr = p.level != 1 + let endX = measureCharPrepared(cm, preparedMeasure, ltr ? Math.min(end, p.to) - 1 : Math.max(begin, p.from)).right + // Weigh against spans ending before this, so that they are only + // picked if nothing ends after + let dist = endX < x ? x - endX + 1e9 : endX - x + if (!part || closestDist > dist) { + part = p + closestDist = dist + } + } + if (!part) part = order[order.length - 1] + // Clip the part to the wrapped line. + if (part.from < begin) part = {from: begin, to: part.to, level: part.level} + if (part.to > end) part = {from: part.from, to: end, level: part.level} + return part +} + +let measureText +// Compute the default text height. +export function textHeight(display) { + if (display.cachedTextHeight != null) return display.cachedTextHeight + if (measureText == null) { + measureText = elt("pre", null, "CodeMirror-line-like") + // Measure a bunch of lines, for browsers that compute + // fractional heights. + for (let i = 0; i < 49; ++i) { + measureText.appendChild(document.createTextNode("x")) + measureText.appendChild(elt("br")) + } + measureText.appendChild(document.createTextNode("x")) + } + removeChildrenAndAdd(display.measure, measureText) + let height = measureText.offsetHeight / 50 + if (height > 3) display.cachedTextHeight = height + removeChildren(display.measure) + return height || 1 +} + +// Compute the default character width. +export function charWidth(display) { + if (display.cachedCharWidth != null) return display.cachedCharWidth + let anchor = elt("span", "xxxxxxxxxx") + let pre = elt("pre", [anchor], "CodeMirror-line-like") + removeChildrenAndAdd(display.measure, pre) + let rect = anchor.getBoundingClientRect(), width = (rect.right - rect.left) / 10 + if (width > 2) display.cachedCharWidth = width + return width || 10 +} + +// 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. +export function getDimensions(cm) { + let d = cm.display, left = {}, width = {} + let gutterLeft = d.gutters.clientLeft + for (let n = d.gutters.firstChild, i = 0; n; n = n.nextSibling, ++i) { + let id = cm.display.gutterSpecs[i].className + left[id] = n.offsetLeft + n.clientLeft + gutterLeft + width[id] = n.clientWidth + } + return {fixedPos: compensateForHScroll(d), + gutterTotalWidth: d.gutters.offsetWidth, + gutterLeft: left, + gutterWidth: width, + wrapperWidth: d.wrapper.clientWidth} +} + +// Computes display.scroller.scrollLeft + display.gutters.offsetWidth, +// but using getBoundingClientRect to get a sub-pixel-accurate +// result. +export function compensateForHScroll(display) { + return display.scroller.getBoundingClientRect().left - display.sizer.getBoundingClientRect().left +} + +// 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). +export function estimateHeight(cm) { + let th = textHeight(cm.display), wrapping = cm.options.lineWrapping + let perLine = wrapping && Math.max(5, cm.display.scroller.clientWidth / charWidth(cm.display) - 3) + return line => { + if (lineIsHidden(cm.doc, line)) return 0 + + let widgetsHeight = 0 + if (line.widgets) for (let 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 + } +} + +export function estimateLineHeights(cm) { + let doc = cm.doc, est = estimateHeight(cm) + doc.iter(line => { + let estHeight = est(line) + if (estHeight != line.height) updateLineHeight(line, estHeight) + }) +} + +// 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. +export function posFromMouse(cm, e, liberal, forRect) { + let display = cm.display + if (!liberal && e_target(e).getAttribute("cm-not-content") == "true") return null + + let 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 } + let coords = coordsChar(cm, x, y), line + if (forRect && coords.xRel == 1 && (line = getLine(cm.doc, coords.line).text).length == coords.ch) { + let 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 +} + +// Find the view element corresponding to a given line. Return null +// when the line isn't visible. +export function findViewIndex(cm, n) { + if (n >= cm.display.viewTo) return null + n -= cm.display.viewFrom + if (n < 0) return null + let view = cm.display.view + for (let i = 0; i < view.length; i++) { + n -= view[i].size + if (n < 0) return i + } +} diff --git a/docs/js/node_modules/codemirror/src/measurement/widgets.js b/docs/js/node_modules/codemirror/src/measurement/widgets.js new file mode 100644 index 000000000..39d7553d1 --- /dev/null +++ b/docs/js/node_modules/codemirror/src/measurement/widgets.js @@ -0,0 +1,26 @@ +import { contains, elt, removeChildrenAndAdd } from "../util/dom.js" +import { e_target } from "../util/event.js" + +export function widgetHeight(widget) { + if (widget.height != null) return widget.height + let cm = widget.doc.cm + if (!cm) return 0 + if (!contains(document.body, widget.node)) { + let parentStyle = "position: relative;" + if (widget.coverGutter) + parentStyle += "margin-left: -" + cm.display.gutters.offsetWidth + "px;" + if (widget.noHScroll) + parentStyle += "width: " + cm.display.wrapper.clientWidth + "px;" + removeChildrenAndAdd(cm.display.measure, elt("div", [widget.node], null, parentStyle)) + } + return widget.height = widget.node.parentNode.offsetHeight +} + +// Return true when the given mouse event happened in a widget +export function eventInWidget(display, e) { + for (let 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 + } +} diff --git a/docs/js/node_modules/codemirror/src/model/Doc.js b/docs/js/node_modules/codemirror/src/model/Doc.js new file mode 100644 index 000000000..956f752b9 --- /dev/null +++ b/docs/js/node_modules/codemirror/src/model/Doc.js @@ -0,0 +1,432 @@ +import CodeMirror from "../edit/CodeMirror.js" +import { docMethodOp } from "../display/operations.js" +import { Line } from "../line/line_data.js" +import { clipPos, clipPosArray, Pos } from "../line/pos.js" +import { visualLine } from "../line/spans.js" +import { getBetween, getLine, getLines, isLine, lineNo } from "../line/utils_line.js" +import { classTest } from "../util/dom.js" +import { splitLinesAuto } from "../util/feature_detection.js" +import { createObj, map, isEmpty, sel_dontScroll } from "../util/misc.js" +import { ensureCursorVisible, scrollToCoords } from "../display/scrolling.js" + +import { changeLine, makeChange, makeChangeFromHistory, replaceRange } from "./changes.js" +import { computeReplacedSel } from "./change_measurement.js" +import { BranchChunk, LeafChunk } from "./chunk.js" +import { directionChanged, linkedDocs, updateDoc } from "./document_data.js" +import { copyHistoryArray, History } from "./history.js" +import { addLineWidget } from "./line_widget.js" +import { copySharedMarkers, detachSharedMarkers, findSharedMarkers, markText } from "./mark_text.js" +import { normalizeSelection, Range, simpleSelection } from "./selection.js" +import { extendSelection, extendSelections, setSelection, setSelectionReplaceHistory, setSimpleSelection } from "./selection_updates.js" + +let nextDocId = 0 +let Doc = function(text, mode, firstLine, lineSep, direction) { + if (!(this instanceof Doc)) return new Doc(text, mode, firstLine, lineSep, direction) + if (firstLine == null) firstLine = 0 + + BranchChunk.call(this, [new LeafChunk([new Line("", null)])]) + this.first = firstLine + this.scrollTop = this.scrollLeft = 0 + this.cantEdit = false + this.cleanGeneration = 1 + this.modeFrontier = this.highlightFrontier = firstLine + let start = Pos(firstLine, 0) + this.sel = simpleSelection(start) + this.history = new History(null) + this.id = ++nextDocId + this.modeOption = mode + this.lineSep = lineSep + this.direction = (direction == "rtl") ? "rtl" : "ltr" + this.extend = false + + if (typeof text == "string") text = this.splitLines(text) + updateDoc(this, {from: start, to: start, text: text}) + setSelection(this, simpleSelection(start), sel_dontScroll) +} + +Doc.prototype = createObj(BranchChunk.prototype, { + constructor: Doc, + // Iterate over the document. Supports two forms -- with only one + // argument, it calls that for each line in the document. With + // three, it iterates over the range given by the first two (with + // the second being non-inclusive). + iter: function(from, to, op) { + if (op) this.iterN(from - this.first, to - from, op) + else this.iterN(this.first, this.first + this.size, from) + }, + + // Non-public interface for adding and removing lines. + insert: function(at, lines) { + let height = 0 + for (let i = 0; i < lines.length; ++i) height += lines[i].height + this.insertInner(at - this.first, lines, height) + }, + remove: function(at, n) { this.removeInner(at - this.first, n) }, + + // From here, the methods are part of the public interface. Most + // are also available from CodeMirror (editor) instances. + + getValue: function(lineSep) { + let lines = getLines(this, this.first, this.first + this.size) + if (lineSep === false) return lines + return lines.join(lineSep || this.lineSeparator()) + }, + setValue: docMethodOp(function(code) { + let top = Pos(this.first, 0), last = this.first + this.size - 1 + makeChange(this, {from: top, to: Pos(last, getLine(this, last).text.length), + text: this.splitLines(code), origin: "setValue", full: true}, true) + if (this.cm) scrollToCoords(this.cm, 0, 0) + setSelection(this, simpleSelection(top), sel_dontScroll) + }), + replaceRange: function(code, from, to, origin) { + from = clipPos(this, from) + to = to ? clipPos(this, to) : from + replaceRange(this, code, from, to, origin) + }, + getRange: function(from, to, lineSep) { + let lines = getBetween(this, clipPos(this, from), clipPos(this, to)) + if (lineSep === false) return lines + return lines.join(lineSep || this.lineSeparator()) + }, + + getLine: function(line) {let l = this.getLineHandle(line); return l && l.text}, + + getLineHandle: function(line) {if (isLine(this, line)) return getLine(this, line)}, + getLineNumber: function(line) {return lineNo(line)}, + + getLineHandleVisualStart: function(line) { + if (typeof line == "number") line = getLine(this, line) + return visualLine(line) + }, + + lineCount: function() {return this.size}, + firstLine: function() {return this.first}, + lastLine: function() {return this.first + this.size - 1}, + + clipPos: function(pos) {return clipPos(this, pos)}, + + getCursor: function(start) { + let range = this.sel.primary(), pos + if (start == null || start == "head") pos = range.head + else if (start == "anchor") pos = range.anchor + else if (start == "end" || start == "to" || start === false) pos = range.to() + else pos = range.from() + return pos + }, + listSelections: function() { return this.sel.ranges }, + somethingSelected: function() {return this.sel.somethingSelected()}, + + setCursor: docMethodOp(function(line, ch, options) { + setSimpleSelection(this, clipPos(this, typeof line == "number" ? Pos(line, ch || 0) : line), null, options) + }), + setSelection: docMethodOp(function(anchor, head, options) { + setSimpleSelection(this, clipPos(this, anchor), clipPos(this, head || anchor), options) + }), + extendSelection: docMethodOp(function(head, other, options) { + extendSelection(this, clipPos(this, head), other && clipPos(this, other), options) + }), + extendSelections: docMethodOp(function(heads, options) { + extendSelections(this, clipPosArray(this, heads), options) + }), + extendSelectionsBy: docMethodOp(function(f, options) { + let heads = map(this.sel.ranges, f) + extendSelections(this, clipPosArray(this, heads), options) + }), + setSelections: docMethodOp(function(ranges, primary, options) { + if (!ranges.length) return + let out = [] + for (let i = 0; i < ranges.length; i++) + out[i] = new Range(clipPos(this, ranges[i].anchor), + clipPos(this, ranges[i].head)) + if (primary == null) primary = Math.min(ranges.length - 1, this.sel.primIndex) + setSelection(this, normalizeSelection(this.cm, out, primary), options) + }), + addSelection: docMethodOp(function(anchor, head, options) { + let ranges = this.sel.ranges.slice(0) + ranges.push(new Range(clipPos(this, anchor), clipPos(this, head || anchor))) + setSelection(this, normalizeSelection(this.cm, ranges, ranges.length - 1), options) + }), + + getSelection: function(lineSep) { + let ranges = this.sel.ranges, lines + for (let i = 0; i < ranges.length; i++) { + let sel = getBetween(this, ranges[i].from(), ranges[i].to()) + lines = lines ? lines.concat(sel) : sel + } + if (lineSep === false) return lines + else return lines.join(lineSep || this.lineSeparator()) + }, + getSelections: function(lineSep) { + let parts = [], ranges = this.sel.ranges + for (let i = 0; i < ranges.length; i++) { + let sel = getBetween(this, ranges[i].from(), ranges[i].to()) + if (lineSep !== false) sel = sel.join(lineSep || this.lineSeparator()) + parts[i] = sel + } + return parts + }, + replaceSelection: function(code, collapse, origin) { + let dup = [] + for (let i = 0; i < this.sel.ranges.length; i++) + dup[i] = code + this.replaceSelections(dup, collapse, origin || "+input") + }, + replaceSelections: docMethodOp(function(code, collapse, origin) { + let changes = [], sel = this.sel + for (let i = 0; i < sel.ranges.length; i++) { + let range = sel.ranges[i] + changes[i] = {from: range.from(), to: range.to(), text: this.splitLines(code[i]), origin: origin} + } + let newSel = collapse && collapse != "end" && computeReplacedSel(this, changes, collapse) + for (let i = changes.length - 1; i >= 0; i--) + makeChange(this, changes[i]) + if (newSel) setSelectionReplaceHistory(this, newSel) + else if (this.cm) ensureCursorVisible(this.cm) + }), + undo: docMethodOp(function() {makeChangeFromHistory(this, "undo")}), + redo: docMethodOp(function() {makeChangeFromHistory(this, "redo")}), + undoSelection: docMethodOp(function() {makeChangeFromHistory(this, "undo", true)}), + redoSelection: docMethodOp(function() {makeChangeFromHistory(this, "redo", true)}), + + setExtending: function(val) {this.extend = val}, + getExtending: function() {return this.extend}, + + historySize: function() { + let hist = this.history, done = 0, undone = 0 + for (let i = 0; i < hist.done.length; i++) if (!hist.done[i].ranges) ++done + for (let i = 0; i < hist.undone.length; i++) if (!hist.undone[i].ranges) ++undone + return {undo: done, redo: undone} + }, + clearHistory: function() {this.history = new History(this.history.maxGeneration)}, + + markClean: function() { + this.cleanGeneration = this.changeGeneration(true) + }, + changeGeneration: function(forceSplit) { + if (forceSplit) + this.history.lastOp = this.history.lastSelOp = this.history.lastOrigin = null + return this.history.generation + }, + isClean: function (gen) { + return this.history.generation == (gen || this.cleanGeneration) + }, + + getHistory: function() { + return {done: copyHistoryArray(this.history.done), + undone: copyHistoryArray(this.history.undone)} + }, + setHistory: function(histData) { + let hist = this.history = new History(this.history.maxGeneration) + hist.done = copyHistoryArray(histData.done.slice(0), null, true) + hist.undone = copyHistoryArray(histData.undone.slice(0), null, true) + }, + + setGutterMarker: docMethodOp(function(line, gutterID, value) { + return changeLine(this, line, "gutter", line => { + let markers = line.gutterMarkers || (line.gutterMarkers = {}) + markers[gutterID] = value + if (!value && isEmpty(markers)) line.gutterMarkers = null + return true + }) + }), + + clearGutter: docMethodOp(function(gutterID) { + this.iter(line => { + if (line.gutterMarkers && line.gutterMarkers[gutterID]) { + changeLine(this, line, "gutter", () => { + line.gutterMarkers[gutterID] = null + if (isEmpty(line.gutterMarkers)) line.gutterMarkers = null + return true + }) + } + }) + }), + + lineInfo: function(line) { + let n + if (typeof line == "number") { + if (!isLine(this, line)) return null + n = line + line = getLine(this, line) + if (!line) return null + } else { + n = lineNo(line) + if (n == null) return null + } + return {line: n, handle: line, text: line.text, gutterMarkers: line.gutterMarkers, + textClass: line.textClass, bgClass: line.bgClass, wrapClass: line.wrapClass, + widgets: line.widgets} + }, + + addLineClass: docMethodOp(function(handle, where, cls) { + return changeLine(this, handle, where == "gutter" ? "gutter" : "class", line => { + let prop = where == "text" ? "textClass" + : where == "background" ? "bgClass" + : where == "gutter" ? "gutterClass" : "wrapClass" + if (!line[prop]) line[prop] = cls + else if (classTest(cls).test(line[prop])) return false + else line[prop] += " " + cls + return true + }) + }), + removeLineClass: docMethodOp(function(handle, where, cls) { + return changeLine(this, handle, where == "gutter" ? "gutter" : "class", line => { + let prop = where == "text" ? "textClass" + : where == "background" ? "bgClass" + : where == "gutter" ? "gutterClass" : "wrapClass" + let cur = line[prop] + if (!cur) return false + else if (cls == null) line[prop] = null + else { + let found = cur.match(classTest(cls)) + if (!found) return false + let end = found.index + found[0].length + line[prop] = cur.slice(0, found.index) + (!found.index || end == cur.length ? "" : " ") + cur.slice(end) || null + } + return true + }) + }), + + addLineWidget: docMethodOp(function(handle, node, options) { + return addLineWidget(this, handle, node, options) + }), + removeLineWidget: function(widget) { widget.clear() }, + + markText: function(from, to, options) { + return markText(this, clipPos(this, from), clipPos(this, to), options, options && options.type || "range") + }, + setBookmark: function(pos, options) { + let realOpts = {replacedWith: options && (options.nodeType == null ? options.widget : options), + insertLeft: options && options.insertLeft, + clearWhenEmpty: false, shared: options && options.shared, + handleMouseEvents: options && options.handleMouseEvents} + pos = clipPos(this, pos) + return markText(this, pos, pos, realOpts, "bookmark") + }, + findMarksAt: function(pos) { + pos = clipPos(this, pos) + let markers = [], spans = getLine(this, pos.line).markedSpans + if (spans) for (let i = 0; i < spans.length; ++i) { + let span = spans[i] + if ((span.from == null || span.from <= pos.ch) && + (span.to == null || span.to >= pos.ch)) + markers.push(span.marker.parent || span.marker) + } + return markers + }, + findMarks: function(from, to, filter) { + from = clipPos(this, from); to = clipPos(this, to) + let found = [], lineNo = from.line + this.iter(from.line, to.line + 1, line => { + let spans = line.markedSpans + if (spans) for (let i = 0; i < spans.length; i++) { + let span = spans[i] + if (!(span.to != null && lineNo == from.line && from.ch >= span.to || + span.from == null && lineNo != from.line || + span.from != null && lineNo == to.line && span.from >= to.ch) && + (!filter || filter(span.marker))) + found.push(span.marker.parent || span.marker) + } + ++lineNo + }) + return found + }, + getAllMarks: function() { + let markers = [] + this.iter(line => { + let sps = line.markedSpans + if (sps) for (let i = 0; i < sps.length; ++i) + if (sps[i].from != null) markers.push(sps[i].marker) + }) + return markers + }, + + posFromIndex: function(off) { + let ch, lineNo = this.first, sepSize = this.lineSeparator().length + this.iter(line => { + let sz = line.text.length + sepSize + if (sz > off) { ch = off; return true } + off -= sz + ++lineNo + }) + return clipPos(this, Pos(lineNo, ch)) + }, + indexFromPos: function (coords) { + coords = clipPos(this, coords) + let index = coords.ch + if (coords.line < this.first || coords.ch < 0) return 0 + let sepSize = this.lineSeparator().length + this.iter(this.first, coords.line, line => { // iter aborts when callback returns a truthy value + index += line.text.length + sepSize + }) + return index + }, + + copy: function(copyHistory) { + let doc = new Doc(getLines(this, this.first, this.first + this.size), + this.modeOption, this.first, this.lineSep, this.direction) + doc.scrollTop = this.scrollTop; doc.scrollLeft = this.scrollLeft + doc.sel = this.sel + doc.extend = false + if (copyHistory) { + doc.history.undoDepth = this.history.undoDepth + doc.setHistory(this.getHistory()) + } + return doc + }, + + linkedDoc: function(options) { + if (!options) options = {} + let from = this.first, to = this.first + this.size + if (options.from != null && options.from > from) from = options.from + if (options.to != null && options.to < to) to = options.to + let copy = new Doc(getLines(this, from, to), options.mode || this.modeOption, from, this.lineSep, this.direction) + if (options.sharedHist) copy.history = this.history + ;(this.linked || (this.linked = [])).push({doc: copy, sharedHist: options.sharedHist}) + copy.linked = [{doc: this, isParent: true, sharedHist: options.sharedHist}] + copySharedMarkers(copy, findSharedMarkers(this)) + return copy + }, + unlinkDoc: function(other) { + if (other instanceof CodeMirror) other = other.doc + if (this.linked) for (let i = 0; i < this.linked.length; ++i) { + let link = this.linked[i] + if (link.doc != other) continue + this.linked.splice(i, 1) + other.unlinkDoc(this) + detachSharedMarkers(findSharedMarkers(this)) + break + } + // If the histories were shared, split them again + if (other.history == this.history) { + let splitIds = [other.id] + linkedDocs(other, doc => splitIds.push(doc.id), true) + other.history = new History(null) + other.history.done = copyHistoryArray(this.history.done, splitIds) + other.history.undone = copyHistoryArray(this.history.undone, splitIds) + } + }, + iterLinkedDocs: function(f) {linkedDocs(this, f)}, + + getMode: function() {return this.mode}, + getEditor: function() {return this.cm}, + + splitLines: function(str) { + if (this.lineSep) return str.split(this.lineSep) + return splitLinesAuto(str) + }, + lineSeparator: function() { return this.lineSep || "\n" }, + + setDirection: docMethodOp(function (dir) { + if (dir != "rtl") dir = "ltr" + if (dir == this.direction) return + this.direction = dir + this.iter(line => line.order = null) + if (this.cm) directionChanged(this.cm) + }) +}) + +// Public alias. +Doc.prototype.eachLine = Doc.prototype.iter + +export default Doc diff --git a/docs/js/node_modules/codemirror/src/model/change_measurement.js b/docs/js/node_modules/codemirror/src/model/change_measurement.js new file mode 100644 index 000000000..010e7f81e --- /dev/null +++ b/docs/js/node_modules/codemirror/src/model/change_measurement.js @@ -0,0 +1,61 @@ +import { cmp, Pos } from "../line/pos.js" +import { lst } from "../util/misc.js" + +import { normalizeSelection, Range, Selection } from "./selection.js" + +// Compute the position of the end of a change (its 'to' property +// refers to the pre-change end). +export function changeEnd(change) { + if (!change.text) return change.to + return Pos(change.from.line + change.text.length - 1, + lst(change.text).length + (change.text.length == 1 ? change.from.ch : 0)) +} + +// Adjust a position to refer to the post-change position of the +// same text, or the end of the change if the change covers it. +function adjustForChange(pos, change) { + if (cmp(pos, change.from) < 0) return pos + if (cmp(pos, change.to) <= 0) return changeEnd(change) + + let line = pos.line + change.text.length - (change.to.line - change.from.line) - 1, ch = pos.ch + if (pos.line == change.to.line) ch += changeEnd(change).ch - change.to.ch + return Pos(line, ch) +} + +export function computeSelAfterChange(doc, change) { + let out = [] + for (let i = 0; i < doc.sel.ranges.length; i++) { + let range = doc.sel.ranges[i] + out.push(new Range(adjustForChange(range.anchor, change), + adjustForChange(range.head, change))) + } + return normalizeSelection(doc.cm, out, doc.sel.primIndex) +} + +function offsetPos(pos, old, nw) { + if (pos.line == old.line) + return Pos(nw.line, pos.ch - old.ch + nw.ch) + else + return Pos(nw.line + (pos.line - old.line), pos.ch) +} + +// Used by replaceSelections to allow moving the selection to the +// start or around the replaced test. Hint may be "start" or "around". +export function computeReplacedSel(doc, changes, hint) { + let out = [] + let oldPrev = Pos(doc.first, 0), newPrev = oldPrev + for (let i = 0; i < changes.length; i++) { + let change = changes[i] + let from = offsetPos(change.from, oldPrev, newPrev) + let to = offsetPos(changeEnd(change), oldPrev, newPrev) + oldPrev = change.to + newPrev = to + if (hint == "around") { + let range = doc.sel.ranges[i], inv = cmp(range.head, range.anchor) < 0 + out[i] = new Range(inv ? to : from, inv ? from : to) + } else { + out[i] = new Range(from, from) + } + } + return new Selection(out, doc.sel.primIndex) +} diff --git a/docs/js/node_modules/codemirror/src/model/changes.js b/docs/js/node_modules/codemirror/src/model/changes.js new file mode 100644 index 000000000..48d2f6bb9 --- /dev/null +++ b/docs/js/node_modules/codemirror/src/model/changes.js @@ -0,0 +1,339 @@ +import { retreatFrontier } from "../line/highlight.js" +import { startWorker } from "../display/highlight_worker.js" +import { operation } from "../display/operations.js" +import { regChange, regLineChange } from "../display/view_tracking.js" +import { clipLine, clipPos, cmp, Pos } from "../line/pos.js" +import { sawReadOnlySpans } from "../line/saw_special_spans.js" +import { lineLength, removeReadOnlyRanges, stretchSpansOverChange, visualLine } from "../line/spans.js" +import { getBetween, getLine, lineNo } from "../line/utils_line.js" +import { estimateHeight } from "../measurement/position_measurement.js" +import { hasHandler, signal, signalCursorActivity } from "../util/event.js" +import { indexOf, lst, map, sel_dontScroll } from "../util/misc.js" +import { signalLater } from "../util/operation_group.js" + +import { changeEnd, computeSelAfterChange } from "./change_measurement.js" +import { isWholeLineUpdate, linkedDocs, updateDoc } from "./document_data.js" +import { addChangeToHistory, historyChangeFromChange, mergeOldSpans, pushSelectionToHistory } from "./history.js" +import { Range, Selection } from "./selection.js" +import { setSelection, setSelectionNoUndo, skipAtomic } from "./selection_updates.js" + +// UPDATING + +// Allow "beforeChange" event handlers to influence a change +function filterChange(doc, change, update) { + let obj = { + canceled: false, + from: change.from, + to: change.to, + text: change.text, + origin: change.origin, + cancel: () => obj.canceled = true + } + if (update) obj.update = (from, to, text, origin) => { + if (from) obj.from = clipPos(doc, from) + if (to) obj.to = clipPos(doc, to) + if (text) obj.text = text + if (origin !== undefined) obj.origin = origin + } + signal(doc, "beforeChange", doc, obj) + if (doc.cm) signal(doc.cm, "beforeChange", doc.cm, obj) + + if (obj.canceled) { + if (doc.cm) doc.cm.curOp.updateInput = 2 + return null + } + return {from: obj.from, to: obj.to, text: obj.text, origin: obj.origin} +} + +// Apply a change to a document, and add it to the document's +// history, and propagating it to all linked documents. +export function makeChange(doc, change, ignoreReadOnly) { + if (doc.cm) { + if (!doc.cm.curOp) return operation(doc.cm, makeChange)(doc, change, ignoreReadOnly) + if (doc.cm.state.suppressEdits) return + } + + if (hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange")) { + change = filterChange(doc, change, true) + if (!change) return + } + + // Possibly split or suppress the update based on the presence + // of read-only spans in its range. + let split = sawReadOnlySpans && !ignoreReadOnly && removeReadOnlyRanges(doc, change.from, change.to) + if (split) { + for (let i = split.length - 1; i >= 0; --i) + makeChangeInner(doc, {from: split[i].from, to: split[i].to, text: i ? [""] : change.text, origin: change.origin}) + } else { + makeChangeInner(doc, change) + } +} + +function makeChangeInner(doc, change) { + if (change.text.length == 1 && change.text[0] == "" && cmp(change.from, change.to) == 0) return + let selAfter = computeSelAfterChange(doc, change) + addChangeToHistory(doc, change, selAfter, doc.cm ? doc.cm.curOp.id : NaN) + + makeChangeSingleDoc(doc, change, selAfter, stretchSpansOverChange(doc, change)) + let rebased = [] + + linkedDocs(doc, (doc, sharedHist) => { + if (!sharedHist && indexOf(rebased, doc.history) == -1) { + rebaseHist(doc.history, change) + rebased.push(doc.history) + } + makeChangeSingleDoc(doc, change, null, stretchSpansOverChange(doc, change)) + }) +} + +// Revert a change stored in a document's history. +export function makeChangeFromHistory(doc, type, allowSelectionOnly) { + let suppress = doc.cm && doc.cm.state.suppressEdits + if (suppress && !allowSelectionOnly) return + + let hist = doc.history, event, selAfter = doc.sel + let source = type == "undo" ? hist.done : hist.undone, dest = type == "undo" ? hist.undone : hist.done + + // Verify that there is a useable event (so that ctrl-z won't + // needlessly clear selection events) + let i = 0 + for (; i < source.length; i++) { + event = source[i] + if (allowSelectionOnly ? event.ranges && !event.equals(doc.sel) : !event.ranges) + break + } + if (i == source.length) return + hist.lastOrigin = hist.lastSelOrigin = null + + for (;;) { + event = source.pop() + if (event.ranges) { + pushSelectionToHistory(event, dest) + if (allowSelectionOnly && !event.equals(doc.sel)) { + setSelection(doc, event, {clearRedo: false}) + return + } + selAfter = event + } else if (suppress) { + source.push(event) + return + } else break + } + + // Build up a reverse change object to add to the opposite history + // stack (redo when undoing, and vice versa). + let antiChanges = [] + pushSelectionToHistory(selAfter, dest) + dest.push({changes: antiChanges, generation: hist.generation}) + hist.generation = event.generation || ++hist.maxGeneration + + let filter = hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange") + + for (let i = event.changes.length - 1; i >= 0; --i) { + let change = event.changes[i] + change.origin = type + if (filter && !filterChange(doc, change, false)) { + source.length = 0 + return + } + + antiChanges.push(historyChangeFromChange(doc, change)) + + let after = i ? computeSelAfterChange(doc, change) : lst(source) + makeChangeSingleDoc(doc, change, after, mergeOldSpans(doc, change)) + if (!i && doc.cm) doc.cm.scrollIntoView({from: change.from, to: changeEnd(change)}) + let rebased = [] + + // Propagate to the linked documents + linkedDocs(doc, (doc, sharedHist) => { + if (!sharedHist && indexOf(rebased, doc.history) == -1) { + rebaseHist(doc.history, change) + rebased.push(doc.history) + } + makeChangeSingleDoc(doc, change, null, mergeOldSpans(doc, change)) + }) + } +} + +// Sub-views need their line numbers shifted when text is added +// above or below them in the parent document. +function shiftDoc(doc, distance) { + if (distance == 0) return + doc.first += distance + doc.sel = new Selection(map(doc.sel.ranges, range => new Range( + Pos(range.anchor.line + distance, range.anchor.ch), + Pos(range.head.line + distance, range.head.ch) + )), doc.sel.primIndex) + if (doc.cm) { + regChange(doc.cm, doc.first, doc.first - distance, distance) + for (let d = doc.cm.display, l = d.viewFrom; l < d.viewTo; l++) + regLineChange(doc.cm, l, "gutter") + } +} + +// More lower-level change function, handling only a single document +// (not linked ones). +function makeChangeSingleDoc(doc, change, selAfter, spans) { + if (doc.cm && !doc.cm.curOp) + return operation(doc.cm, makeChangeSingleDoc)(doc, change, selAfter, spans) + + if (change.to.line < doc.first) { + shiftDoc(doc, change.text.length - 1 - (change.to.line - change.from.line)) + return + } + if (change.from.line > doc.lastLine()) return + + // Clip the change to the size of this doc + if (change.from.line < doc.first) { + let shift = change.text.length - 1 - (doc.first - change.from.line) + shiftDoc(doc, shift) + change = {from: Pos(doc.first, 0), to: Pos(change.to.line + shift, change.to.ch), + text: [lst(change.text)], origin: change.origin} + } + let last = doc.lastLine() + if (change.to.line > last) { + change = {from: change.from, to: Pos(last, getLine(doc, last).text.length), + text: [change.text[0]], origin: change.origin} + } + + change.removed = getBetween(doc, change.from, change.to) + + if (!selAfter) selAfter = computeSelAfterChange(doc, change) + if (doc.cm) makeChangeSingleDocInEditor(doc.cm, change, spans) + else updateDoc(doc, change, spans) + setSelectionNoUndo(doc, selAfter, sel_dontScroll) + + if (doc.cantEdit && skipAtomic(doc, Pos(doc.firstLine(), 0))) + doc.cantEdit = false +} + +// Handle the interaction of a change to a document with the editor +// that this document is part of. +function makeChangeSingleDocInEditor(cm, change, spans) { + let doc = cm.doc, display = cm.display, from = change.from, to = change.to + + let recomputeMaxLength = false, checkWidthStart = from.line + if (!cm.options.lineWrapping) { + checkWidthStart = lineNo(visualLine(getLine(doc, from.line))) + doc.iter(checkWidthStart, to.line + 1, line => { + if (line == display.maxLine) { + recomputeMaxLength = true + return true + } + }) + } + + if (doc.sel.contains(change.from, change.to) > -1) + signalCursorActivity(cm) + + updateDoc(doc, change, spans, estimateHeight(cm)) + + if (!cm.options.lineWrapping) { + doc.iter(checkWidthStart, from.line + change.text.length, line => { + let len = lineLength(line) + if (len > display.maxLineLength) { + display.maxLine = line + display.maxLineLength = len + display.maxLineChanged = true + recomputeMaxLength = false + } + }) + if (recomputeMaxLength) cm.curOp.updateMaxLine = true + } + + retreatFrontier(doc, from.line) + startWorker(cm, 400) + + let lendiff = change.text.length - (to.line - from.line) - 1 + // Remember that these lines changed, for updating the display + if (change.full) + regChange(cm) + else if (from.line == to.line && change.text.length == 1 && !isWholeLineUpdate(cm.doc, change)) + regLineChange(cm, from.line, "text") + else + regChange(cm, from.line, to.line + 1, lendiff) + + let changesHandler = hasHandler(cm, "changes"), changeHandler = hasHandler(cm, "change") + if (changeHandler || changesHandler) { + let obj = { + from: from, to: to, + text: change.text, + removed: change.removed, + origin: change.origin + } + if (changeHandler) signalLater(cm, "change", cm, obj) + if (changesHandler) (cm.curOp.changeObjs || (cm.curOp.changeObjs = [])).push(obj) + } + cm.display.selForContextMenu = null +} + +export function replaceRange(doc, code, from, to, origin) { + if (!to) to = from + if (cmp(to, from) < 0) [from, to] = [to, from] + if (typeof code == "string") code = doc.splitLines(code) + makeChange(doc, {from, to, text: code, origin}) +} + +// Rebasing/resetting history to deal with externally-sourced changes + +function rebaseHistSelSingle(pos, from, to, diff) { + if (to < pos.line) { + pos.line += diff + } else if (from < pos.line) { + pos.line = from + pos.ch = 0 + } +} + +// Tries to rebase an array of history events given a change in the +// document. If the change touches the same lines as the event, the +// event, and everything 'behind' it, is discarded. If the change is +// before the event, the event's positions are updated. Uses a +// copy-on-write scheme for the positions, to avoid having to +// reallocate them all on every rebase, but also avoid problems with +// shared position objects being unsafely updated. +function rebaseHistArray(array, from, to, diff) { + for (let i = 0; i < array.length; ++i) { + let sub = array[i], ok = true + if (sub.ranges) { + if (!sub.copied) { sub = array[i] = sub.deepCopy(); sub.copied = true } + for (let j = 0; j < sub.ranges.length; j++) { + rebaseHistSelSingle(sub.ranges[j].anchor, from, to, diff) + rebaseHistSelSingle(sub.ranges[j].head, from, to, diff) + } + continue + } + for (let j = 0; j < sub.changes.length; ++j) { + let cur = sub.changes[j] + if (to < cur.from.line) { + cur.from = Pos(cur.from.line + diff, cur.from.ch) + cur.to = Pos(cur.to.line + diff, cur.to.ch) + } else if (from <= cur.to.line) { + ok = false + break + } + } + if (!ok) { + array.splice(0, i + 1) + i = 0 + } + } +} + +function rebaseHist(hist, change) { + let from = change.from.line, to = change.to.line, diff = change.text.length - (to - from) - 1 + rebaseHistArray(hist.done, from, to, diff) + rebaseHistArray(hist.undone, from, to, diff) +} + +// Utility for applying a change to a line by handle or number, +// returning the number and optionally registering the line as +// changed. +export function changeLine(doc, handle, changeType, op) { + let no = handle, line = handle + if (typeof handle == "number") line = getLine(doc, clipLine(doc, handle)) + else no = lineNo(handle) + if (no == null) return null + if (op(line, no) && doc.cm) regLineChange(doc.cm, no, changeType) + return line +} diff --git a/docs/js/node_modules/codemirror/src/model/chunk.js b/docs/js/node_modules/codemirror/src/model/chunk.js new file mode 100644 index 000000000..d82716ded --- /dev/null +++ b/docs/js/node_modules/codemirror/src/model/chunk.js @@ -0,0 +1,167 @@ +import { cleanUpLine } from "../line/line_data.js" +import { indexOf } from "../util/misc.js" +import { signalLater } from "../util/operation_group.js" + +// The document is represented as a BTree consisting of leaves, with +// chunk of lines in them, and branches, with up to ten leaves or +// other branch nodes below them. The top node is always a branch +// node, and is the document object itself (meaning it has +// additional methods and properties). +// +// All nodes have parent links. The tree is used both to go from +// line numbers to line objects, and to go from objects to numbers. +// It also indexes by height, and is used to convert between height +// and line object, and to find the total height of the document. +// +// See also http://marijnhaverbeke.nl/blog/codemirror-line-tree.html + +export function LeafChunk(lines) { + this.lines = lines + this.parent = null + let height = 0 + for (let i = 0; i < lines.length; ++i) { + lines[i].parent = this + height += lines[i].height + } + this.height = height +} + +LeafChunk.prototype = { + chunkSize() { return this.lines.length }, + + // Remove the n lines at offset 'at'. + removeInner(at, n) { + for (let i = at, e = at + n; i < e; ++i) { + let line = this.lines[i] + this.height -= line.height + cleanUpLine(line) + signalLater(line, "delete") + } + this.lines.splice(at, n) + }, + + // Helper used to collapse a small branch into a single leaf. + collapse(lines) { + lines.push.apply(lines, this.lines) + }, + + // Insert the given array of lines at offset 'at', count them as + // having the given height. + insertInner(at, lines, height) { + this.height += height + this.lines = this.lines.slice(0, at).concat(lines).concat(this.lines.slice(at)) + for (let i = 0; i < lines.length; ++i) lines[i].parent = this + }, + + // Used to iterate over a part of the tree. + iterN(at, n, op) { + for (let e = at + n; at < e; ++at) + if (op(this.lines[at])) return true + } +} + +export function BranchChunk(children) { + this.children = children + let size = 0, height = 0 + for (let i = 0; i < children.length; ++i) { + let ch = children[i] + size += ch.chunkSize(); height += ch.height + ch.parent = this + } + this.size = size + this.height = height + this.parent = null +} + +BranchChunk.prototype = { + chunkSize() { return this.size }, + + removeInner(at, n) { + this.size -= n + for (let i = 0; i < this.children.length; ++i) { + let child = this.children[i], sz = child.chunkSize() + if (at < sz) { + let rm = Math.min(n, sz - at), oldHeight = child.height + child.removeInner(at, rm) + this.height -= oldHeight - child.height + if (sz == rm) { this.children.splice(i--, 1); child.parent = null } + if ((n -= rm) == 0) break + at = 0 + } else at -= sz + } + // If the result is smaller than 25 lines, ensure that it is a + // single leaf node. + if (this.size - n < 25 && + (this.children.length > 1 || !(this.children[0] instanceof LeafChunk))) { + let lines = [] + this.collapse(lines) + this.children = [new LeafChunk(lines)] + this.children[0].parent = this + } + }, + + collapse(lines) { + for (let i = 0; i < this.children.length; ++i) this.children[i].collapse(lines) + }, + + insertInner(at, lines, height) { + this.size += lines.length + this.height += height + for (let i = 0; i < this.children.length; ++i) { + let child = this.children[i], sz = child.chunkSize() + if (at <= sz) { + child.insertInner(at, lines, height) + if (child.lines && child.lines.length > 50) { + // To avoid memory thrashing when child.lines is huge (e.g. first view of a large file), it's never spliced. + // Instead, small slices are taken. They're taken in order because sequential memory accesses are fastest. + let remaining = child.lines.length % 25 + 25 + for (let pos = remaining; pos < child.lines.length;) { + let leaf = new LeafChunk(child.lines.slice(pos, pos += 25)) + child.height -= leaf.height + this.children.splice(++i, 0, leaf) + leaf.parent = this + } + child.lines = child.lines.slice(0, remaining) + this.maybeSpill() + } + break + } + at -= sz + } + }, + + // When a node has grown, check whether it should be split. + maybeSpill() { + if (this.children.length <= 10) return + let me = this + do { + let spilled = me.children.splice(me.children.length - 5, 5) + let sibling = new BranchChunk(spilled) + if (!me.parent) { // Become the parent node + let copy = new BranchChunk(me.children) + copy.parent = me + me.children = [copy, sibling] + me = copy + } else { + me.size -= sibling.size + me.height -= sibling.height + let myIndex = indexOf(me.parent.children, me) + me.parent.children.splice(myIndex + 1, 0, sibling) + } + sibling.parent = me.parent + } while (me.children.length > 10) + me.parent.maybeSpill() + }, + + iterN(at, n, op) { + for (let i = 0; i < this.children.length; ++i) { + let child = this.children[i], sz = child.chunkSize() + if (at < sz) { + let used = Math.min(n, sz - at) + if (child.iterN(at, used, op)) return true + if ((n -= used) == 0) break + at = 0 + } else at -= sz + } + } +} diff --git a/docs/js/node_modules/codemirror/src/model/document_data.js b/docs/js/node_modules/codemirror/src/model/document_data.js new file mode 100644 index 000000000..d946e7af1 --- /dev/null +++ b/docs/js/node_modules/codemirror/src/model/document_data.js @@ -0,0 +1,111 @@ +import { loadMode } from "../display/mode_state.js" +import { runInOp } from "../display/operations.js" +import { regChange } from "../display/view_tracking.js" +import { Line, updateLine } from "../line/line_data.js" +import { findMaxLine } from "../line/spans.js" +import { getLine } from "../line/utils_line.js" +import { estimateLineHeights } from "../measurement/position_measurement.js" +import { addClass, rmClass } from "../util/dom.js" +import { lst } from "../util/misc.js" +import { signalLater } from "../util/operation_group.js" + +// DOCUMENT DATA STRUCTURE + +// By default, updates that start and end at the beginning of a line +// are treated specially, in order to make the association of line +// widgets and marker elements with the text behave more intuitive. +export function isWholeLineUpdate(doc, change) { + return change.from.ch == 0 && change.to.ch == 0 && lst(change.text) == "" && + (!doc.cm || doc.cm.options.wholeLineUpdateBefore) +} + +// Perform a change on the document data structure. +export function updateDoc(doc, change, markedSpans, estimateHeight) { + function spansFor(n) {return markedSpans ? markedSpans[n] : null} + function update(line, text, spans) { + updateLine(line, text, spans, estimateHeight) + signalLater(line, "change", line, change) + } + function linesFor(start, end) { + let result = [] + for (let i = start; i < end; ++i) + result.push(new Line(text[i], spansFor(i), estimateHeight)) + return result + } + + let from = change.from, to = change.to, text = change.text + let firstLine = getLine(doc, from.line), lastLine = getLine(doc, to.line) + let lastText = lst(text), lastSpans = spansFor(text.length - 1), nlines = to.line - from.line + + // Adjust the line structure + if (change.full) { + doc.insert(0, linesFor(0, text.length)) + doc.remove(text.length, doc.size - text.length) + } else if (isWholeLineUpdate(doc, change)) { + // This is a whole-line replace. Treated specially to make + // sure line objects move the way they are supposed to. + let added = linesFor(0, text.length - 1) + update(lastLine, lastLine.text, lastSpans) + if (nlines) doc.remove(from.line, nlines) + if (added.length) doc.insert(from.line, added) + } else if (firstLine == lastLine) { + if (text.length == 1) { + update(firstLine, firstLine.text.slice(0, from.ch) + lastText + firstLine.text.slice(to.ch), lastSpans) + } else { + let added = linesFor(1, text.length - 1) + added.push(new Line(lastText + firstLine.text.slice(to.ch), lastSpans, estimateHeight)) + update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0)) + doc.insert(from.line + 1, added) + } + } else if (text.length == 1) { + update(firstLine, firstLine.text.slice(0, from.ch) + text[0] + lastLine.text.slice(to.ch), spansFor(0)) + doc.remove(from.line + 1, nlines) + } else { + update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0)) + update(lastLine, lastText + lastLine.text.slice(to.ch), lastSpans) + let added = linesFor(1, text.length - 1) + if (nlines > 1) doc.remove(from.line + 1, nlines - 1) + doc.insert(from.line + 1, added) + } + + signalLater(doc, "change", doc, change) +} + +// Call f for all linked documents. +export function linkedDocs(doc, f, sharedHistOnly) { + function propagate(doc, skip, sharedHist) { + if (doc.linked) for (let i = 0; i < doc.linked.length; ++i) { + let rel = doc.linked[i] + if (rel.doc == skip) continue + let shared = sharedHist && rel.sharedHist + if (sharedHistOnly && !shared) continue + f(rel.doc, shared) + propagate(rel.doc, doc, shared) + } + } + propagate(doc, null, true) +} + +// Attach a document to an editor. +export function attachDoc(cm, doc) { + if (doc.cm) throw new Error("This document is already in use.") + cm.doc = doc + doc.cm = cm + estimateLineHeights(cm) + loadMode(cm) + setDirectionClass(cm) + if (!cm.options.lineWrapping) findMaxLine(cm) + cm.options.mode = doc.modeOption + regChange(cm) +} + +function setDirectionClass(cm) { + ;(cm.doc.direction == "rtl" ? addClass : rmClass)(cm.display.lineDiv, "CodeMirror-rtl") +} + +export function directionChanged(cm) { + runInOp(cm, () => { + setDirectionClass(cm) + regChange(cm) + }) +} diff --git a/docs/js/node_modules/codemirror/src/model/history.js b/docs/js/node_modules/codemirror/src/model/history.js new file mode 100644 index 000000000..2d9359f00 --- /dev/null +++ b/docs/js/node_modules/codemirror/src/model/history.js @@ -0,0 +1,228 @@ +import { cmp, copyPos } from "../line/pos.js" +import { stretchSpansOverChange } from "../line/spans.js" +import { getBetween } from "../line/utils_line.js" +import { signal } from "../util/event.js" +import { indexOf, lst } from "../util/misc.js" + +import { changeEnd } from "./change_measurement.js" +import { linkedDocs } from "./document_data.js" +import { Selection } from "./selection.js" + +export function History(startGen) { + // Arrays of change events and selections. Doing something adds an + // event to done and clears undo. Undoing moves events from done + // to undone, redoing moves them in the other direction. + this.done = []; this.undone = [] + this.undoDepth = Infinity + // Used to track when changes can be merged into a single undo + // event + this.lastModTime = this.lastSelTime = 0 + this.lastOp = this.lastSelOp = null + this.lastOrigin = this.lastSelOrigin = null + // Used by the isClean() method + this.generation = this.maxGeneration = startGen || 1 +} + +// Create a history change event from an updateDoc-style change +// object. +export function historyChangeFromChange(doc, change) { + let histChange = {from: copyPos(change.from), to: changeEnd(change), text: getBetween(doc, change.from, change.to)} + attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1) + linkedDocs(doc, doc => attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1), true) + return histChange +} + +// Pop all selection events off the end of a history array. Stop at +// a change event. +function clearSelectionEvents(array) { + while (array.length) { + let last = lst(array) + if (last.ranges) array.pop() + else break + } +} + +// Find the top change event in the history. Pop off selection +// events that are in the way. +function lastChangeEvent(hist, force) { + if (force) { + clearSelectionEvents(hist.done) + return lst(hist.done) + } else if (hist.done.length && !lst(hist.done).ranges) { + return lst(hist.done) + } else if (hist.done.length > 1 && !hist.done[hist.done.length - 2].ranges) { + hist.done.pop() + return lst(hist.done) + } +} + +// Register a change in the history. Merges changes that are within +// a single operation, or are close together with an origin that +// allows merging (starting with "+") into a single event. +export function addChangeToHistory(doc, change, selAfter, opId) { + let hist = doc.history + hist.undone.length = 0 + let time = +new Date, cur + let last + + if ((hist.lastOp == opId || + hist.lastOrigin == change.origin && change.origin && + ((change.origin.charAt(0) == "+" && hist.lastModTime > time - (doc.cm ? doc.cm.options.historyEventDelay : 500)) || + change.origin.charAt(0) == "*")) && + (cur = lastChangeEvent(hist, hist.lastOp == opId))) { + // Merge this change into the last event + last = lst(cur.changes) + if (cmp(change.from, change.to) == 0 && cmp(change.from, last.to) == 0) { + // Optimized case for simple insertion -- don't want to add + // new changesets for every character typed + last.to = changeEnd(change) + } else { + // Add new sub-event + cur.changes.push(historyChangeFromChange(doc, change)) + } + } else { + // Can not be merged, start a new event. + let before = lst(hist.done) + if (!before || !before.ranges) + pushSelectionToHistory(doc.sel, hist.done) + cur = {changes: [historyChangeFromChange(doc, change)], + generation: hist.generation} + hist.done.push(cur) + while (hist.done.length > hist.undoDepth) { + hist.done.shift() + if (!hist.done[0].ranges) hist.done.shift() + } + } + hist.done.push(selAfter) + hist.generation = ++hist.maxGeneration + hist.lastModTime = hist.lastSelTime = time + hist.lastOp = hist.lastSelOp = opId + hist.lastOrigin = hist.lastSelOrigin = change.origin + + if (!last) signal(doc, "historyAdded") +} + +function selectionEventCanBeMerged(doc, origin, prev, sel) { + let ch = origin.charAt(0) + return ch == "*" || + ch == "+" && + prev.ranges.length == sel.ranges.length && + prev.somethingSelected() == sel.somethingSelected() && + new Date - doc.history.lastSelTime <= (doc.cm ? doc.cm.options.historyEventDelay : 500) +} + +// Called whenever the selection changes, sets the new selection as +// the pending selection in the history, and pushes the old pending +// selection into the 'done' array when it was significantly +// different (in number of selected ranges, emptiness, or time). +export function addSelectionToHistory(doc, sel, opId, options) { + let hist = doc.history, origin = options && options.origin + + // A new event is started when the previous origin does not match + // the current, or the origins don't allow matching. Origins + // starting with * are always merged, those starting with + are + // merged when similar and close together in time. + if (opId == hist.lastSelOp || + (origin && hist.lastSelOrigin == origin && + (hist.lastModTime == hist.lastSelTime && hist.lastOrigin == origin || + selectionEventCanBeMerged(doc, origin, lst(hist.done), sel)))) + hist.done[hist.done.length - 1] = sel + else + pushSelectionToHistory(sel, hist.done) + + hist.lastSelTime = +new Date + hist.lastSelOrigin = origin + hist.lastSelOp = opId + if (options && options.clearRedo !== false) + clearSelectionEvents(hist.undone) +} + +export function pushSelectionToHistory(sel, dest) { + let top = lst(dest) + if (!(top && top.ranges && top.equals(sel))) + dest.push(sel) +} + +// Used to store marked span information in the history. +function attachLocalSpans(doc, change, from, to) { + let existing = change["spans_" + doc.id], n = 0 + doc.iter(Math.max(doc.first, from), Math.min(doc.first + doc.size, to), line => { + if (line.markedSpans) + (existing || (existing = change["spans_" + doc.id] = {}))[n] = line.markedSpans + ++n + }) +} + +// When un/re-doing restores text containing marked spans, those +// that have been explicitly cleared should not be restored. +function removeClearedSpans(spans) { + if (!spans) return null + let out + for (let i = 0; i < spans.length; ++i) { + if (spans[i].marker.explicitlyCleared) { if (!out) out = spans.slice(0, i) } + else if (out) out.push(spans[i]) + } + return !out ? spans : out.length ? out : null +} + +// Retrieve and filter the old marked spans stored in a change event. +function getOldSpans(doc, change) { + let found = change["spans_" + doc.id] + if (!found) return null + let nw = [] + for (let i = 0; i < change.text.length; ++i) + nw.push(removeClearedSpans(found[i])) + return nw +} + +// Used for un/re-doing changes from the history. Combines the +// result of computing the existing spans with the set of spans that +// existed in the history (so that deleting around a span and then +// undoing brings back the span). +export function mergeOldSpans(doc, change) { + let old = getOldSpans(doc, change) + let stretched = stretchSpansOverChange(doc, change) + if (!old) return stretched + if (!stretched) return old + + for (let i = 0; i < old.length; ++i) { + let oldCur = old[i], stretchCur = stretched[i] + if (oldCur && stretchCur) { + spans: for (let j = 0; j < stretchCur.length; ++j) { + let span = stretchCur[j] + for (let k = 0; k < oldCur.length; ++k) + if (oldCur[k].marker == span.marker) continue spans + oldCur.push(span) + } + } else if (stretchCur) { + old[i] = stretchCur + } + } + return old +} + +// Used both to provide a JSON-safe object in .getHistory, and, when +// detaching a document, to split the history in two +export function copyHistoryArray(events, newGroup, instantiateSel) { + let copy = [] + for (let i = 0; i < events.length; ++i) { + let event = events[i] + if (event.ranges) { + copy.push(instantiateSel ? Selection.prototype.deepCopy.call(event) : event) + continue + } + let changes = event.changes, newChanges = [] + copy.push({changes: newChanges}) + for (let j = 0; j < changes.length; ++j) { + let change = changes[j], m + newChanges.push({from: change.from, to: change.to, text: change.text}) + if (newGroup) for (var prop in change) if (m = prop.match(/^spans_(\d+)$/)) { + if (indexOf(newGroup, Number(m[1])) > -1) { + lst(newChanges)[prop] = change[prop] + delete change[prop] + } + } + } + } + return copy +} diff --git a/docs/js/node_modules/codemirror/src/model/line_widget.js b/docs/js/node_modules/codemirror/src/model/line_widget.js new file mode 100644 index 000000000..5444d89df --- /dev/null +++ b/docs/js/node_modules/codemirror/src/model/line_widget.js @@ -0,0 +1,78 @@ +import { runInOp } from "../display/operations.js" +import { addToScrollTop } from "../display/scrolling.js" +import { regLineChange } from "../display/view_tracking.js" +import { heightAtLine, lineIsHidden } from "../line/spans.js" +import { lineNo, updateLineHeight } from "../line/utils_line.js" +import { widgetHeight } from "../measurement/widgets.js" +import { changeLine } from "./changes.js" +import { eventMixin } from "../util/event.js" +import { signalLater } from "../util/operation_group.js" + +// Line widgets are block elements displayed above or below a line. + +export class LineWidget { + constructor(doc, node, options) { + if (options) for (let opt in options) if (options.hasOwnProperty(opt)) + this[opt] = options[opt] + this.doc = doc + this.node = node + } + + clear() { + let cm = this.doc.cm, ws = this.line.widgets, line = this.line, no = lineNo(line) + if (no == null || !ws) return + for (let i = 0; i < ws.length; ++i) if (ws[i] == this) ws.splice(i--, 1) + if (!ws.length) line.widgets = null + let height = widgetHeight(this) + updateLineHeight(line, Math.max(0, line.height - height)) + if (cm) { + runInOp(cm, () => { + adjustScrollWhenAboveVisible(cm, line, -height) + regLineChange(cm, no, "widget") + }) + signalLater(cm, "lineWidgetCleared", cm, this, no) + } + } + + changed() { + let oldH = this.height, cm = this.doc.cm, line = this.line + this.height = null + let diff = widgetHeight(this) - oldH + if (!diff) return + if (!lineIsHidden(this.doc, line)) updateLineHeight(line, line.height + diff) + if (cm) { + runInOp(cm, () => { + cm.curOp.forceUpdate = true + adjustScrollWhenAboveVisible(cm, line, diff) + signalLater(cm, "lineWidgetChanged", cm, this, lineNo(line)) + }) + } + } +} +eventMixin(LineWidget) + +function adjustScrollWhenAboveVisible(cm, line, diff) { + if (heightAtLine(line) < ((cm.curOp && cm.curOp.scrollTop) || cm.doc.scrollTop)) + addToScrollTop(cm, diff) +} + +export function addLineWidget(doc, handle, node, options) { + let widget = new LineWidget(doc, node, options) + let cm = doc.cm + if (cm && widget.noHScroll) cm.display.alignWidgets = true + changeLine(doc, handle, "widget", line => { + let widgets = line.widgets || (line.widgets = []) + if (widget.insertAt == null) widgets.push(widget) + else widgets.splice(Math.min(widgets.length - 1, Math.max(0, widget.insertAt)), 0, widget) + widget.line = line + if (cm && !lineIsHidden(doc, line)) { + let aboveVisible = heightAtLine(line) < doc.scrollTop + updateLineHeight(line, line.height + widgetHeight(widget)) + if (aboveVisible) addToScrollTop(cm, widget.height) + cm.curOp.forceUpdate = true + } + return true + }) + if (cm) signalLater(cm, "lineWidgetAdded", cm, widget, typeof handle == "number" ? handle : lineNo(handle)) + return widget +} diff --git a/docs/js/node_modules/codemirror/src/model/mark_text.js b/docs/js/node_modules/codemirror/src/model/mark_text.js new file mode 100644 index 000000000..088f9c98e --- /dev/null +++ b/docs/js/node_modules/codemirror/src/model/mark_text.js @@ -0,0 +1,293 @@ +import { eltP } from "../util/dom.js" +import { eventMixin, hasHandler, on } from "../util/event.js" +import { endOperation, operation, runInOp, startOperation } from "../display/operations.js" +import { clipPos, cmp, Pos } from "../line/pos.js" +import { lineNo, updateLineHeight } from "../line/utils_line.js" +import { clearLineMeasurementCacheFor, findViewForLine, textHeight } from "../measurement/position_measurement.js" +import { seeReadOnlySpans, seeCollapsedSpans } from "../line/saw_special_spans.js" +import { addMarkedSpan, conflictingCollapsedRange, getMarkedSpanFor, lineIsHidden, lineLength, MarkedSpan, removeMarkedSpan, visualLine } from "../line/spans.js" +import { copyObj, indexOf, lst } from "../util/misc.js" +import { signalLater } from "../util/operation_group.js" +import { widgetHeight } from "../measurement/widgets.js" +import { regChange, regLineChange } from "../display/view_tracking.js" + +import { linkedDocs } from "./document_data.js" +import { addChangeToHistory } from "./history.js" +import { reCheckSelection } from "./selection_updates.js" + +// TEXTMARKERS + +// Created with markText and setBookmark methods. A TextMarker is a +// handle that can be used to clear or find a marked position in the +// document. Line objects hold arrays (markedSpans) containing +// {from, to, marker} object pointing to such marker objects, and +// indicating that such a marker is present on that line. Multiple +// lines may point to the same marker when it spans across lines. +// The spans will have null for their from/to properties when the +// marker continues beyond the start/end of the line. Markers have +// links back to the lines they currently touch. + +// Collapsed markers have unique ids, in order to be able to order +// them, which is needed for uniquely determining an outer marker +// when they overlap (they may nest, but not partially overlap). +let nextMarkerId = 0 + +export class TextMarker { + constructor(doc, type) { + this.lines = [] + this.type = type + this.doc = doc + this.id = ++nextMarkerId + } + + // Clear the marker. + clear() { + if (this.explicitlyCleared) return + let cm = this.doc.cm, withOp = cm && !cm.curOp + if (withOp) startOperation(cm) + if (hasHandler(this, "clear")) { + let found = this.find() + if (found) signalLater(this, "clear", found.from, found.to) + } + let min = null, max = null + for (let i = 0; i < this.lines.length; ++i) { + let line = this.lines[i] + let span = getMarkedSpanFor(line.markedSpans, this) + if (cm && !this.collapsed) regLineChange(cm, lineNo(line), "text") + else if (cm) { + if (span.to != null) max = lineNo(line) + if (span.from != null) min = lineNo(line) + } + line.markedSpans = removeMarkedSpan(line.markedSpans, span) + if (span.from == null && this.collapsed && !lineIsHidden(this.doc, line) && cm) + updateLineHeight(line, textHeight(cm.display)) + } + if (cm && this.collapsed && !cm.options.lineWrapping) for (let i = 0; i < this.lines.length; ++i) { + let visual = visualLine(this.lines[i]), len = lineLength(visual) + if (len > cm.display.maxLineLength) { + cm.display.maxLine = visual + cm.display.maxLineLength = len + cm.display.maxLineChanged = true + } + } + + if (min != null && cm && this.collapsed) regChange(cm, min, max + 1) + this.lines.length = 0 + this.explicitlyCleared = true + if (this.atomic && this.doc.cantEdit) { + this.doc.cantEdit = false + if (cm) reCheckSelection(cm.doc) + } + if (cm) signalLater(cm, "markerCleared", cm, this, min, max) + if (withOp) endOperation(cm) + if (this.parent) this.parent.clear() + } + + // Find the position of the marker in the document. Returns a {from, + // to} object by default. Side can be passed to get a specific side + // -- 0 (both), -1 (left), or 1 (right). When lineObj is true, the + // Pos objects returned contain a line object, rather than a line + // number (used to prevent looking up the same line twice). + find(side, lineObj) { + if (side == null && this.type == "bookmark") side = 1 + let from, to + for (let i = 0; i < this.lines.length; ++i) { + let line = this.lines[i] + let span = getMarkedSpanFor(line.markedSpans, this) + if (span.from != null) { + from = Pos(lineObj ? line : lineNo(line), span.from) + if (side == -1) return from + } + if (span.to != null) { + to = Pos(lineObj ? line : lineNo(line), span.to) + if (side == 1) return to + } + } + return from && {from: from, to: to} + } + + // Signals that the marker's widget changed, and surrounding layout + // should be recomputed. + changed() { + let pos = this.find(-1, true), widget = this, cm = this.doc.cm + if (!pos || !cm) return + runInOp(cm, () => { + let line = pos.line, lineN = lineNo(pos.line) + let view = findViewForLine(cm, lineN) + if (view) { + clearLineMeasurementCacheFor(view) + cm.curOp.selectionChanged = cm.curOp.forceUpdate = true + } + cm.curOp.updateMaxLine = true + if (!lineIsHidden(widget.doc, line) && widget.height != null) { + let oldHeight = widget.height + widget.height = null + let dHeight = widgetHeight(widget) - oldHeight + if (dHeight) + updateLineHeight(line, line.height + dHeight) + } + signalLater(cm, "markerChanged", cm, this) + }) + } + + attachLine(line) { + if (!this.lines.length && this.doc.cm) { + let op = this.doc.cm.curOp + if (!op.maybeHiddenMarkers || indexOf(op.maybeHiddenMarkers, this) == -1) + (op.maybeUnhiddenMarkers || (op.maybeUnhiddenMarkers = [])).push(this) + } + this.lines.push(line) + } + + detachLine(line) { + this.lines.splice(indexOf(this.lines, line), 1) + if (!this.lines.length && this.doc.cm) { + let op = this.doc.cm.curOp + ;(op.maybeHiddenMarkers || (op.maybeHiddenMarkers = [])).push(this) + } + } +} +eventMixin(TextMarker) + +// Create a marker, wire it up to the right lines, and +export function markText(doc, from, to, options, type) { + // Shared markers (across linked documents) are handled separately + // (markTextShared will call out to this again, once per + // document). + if (options && options.shared) return markTextShared(doc, from, to, options, type) + // Ensure we are in an operation. + if (doc.cm && !doc.cm.curOp) return operation(doc.cm, markText)(doc, from, to, options, type) + + let marker = new TextMarker(doc, type), diff = cmp(from, to) + if (options) copyObj(options, marker, false) + // Don't connect empty markers unless clearWhenEmpty is false + if (diff > 0 || diff == 0 && marker.clearWhenEmpty !== false) + return marker + if (marker.replacedWith) { + // Showing up as a widget implies collapsed (widget replaces text) + marker.collapsed = true + marker.widgetNode = eltP("span", [marker.replacedWith], "CodeMirror-widget") + if (!options.handleMouseEvents) marker.widgetNode.setAttribute("cm-ignore-events", "true") + if (options.insertLeft) marker.widgetNode.insertLeft = true + } + if (marker.collapsed) { + if (conflictingCollapsedRange(doc, from.line, from, to, marker) || + from.line != to.line && conflictingCollapsedRange(doc, to.line, from, to, marker)) + throw new Error("Inserting collapsed marker partially overlapping an existing one") + seeCollapsedSpans() + } + + if (marker.addToHistory) + addChangeToHistory(doc, {from: from, to: to, origin: "markText"}, doc.sel, NaN) + + let curLine = from.line, cm = doc.cm, updateMaxLine + doc.iter(curLine, to.line + 1, line => { + if (cm && marker.collapsed && !cm.options.lineWrapping && visualLine(line) == cm.display.maxLine) + updateMaxLine = true + if (marker.collapsed && curLine != from.line) updateLineHeight(line, 0) + addMarkedSpan(line, new MarkedSpan(marker, + curLine == from.line ? from.ch : null, + curLine == to.line ? to.ch : null)) + ++curLine + }) + // lineIsHidden depends on the presence of the spans, so needs a second pass + if (marker.collapsed) doc.iter(from.line, to.line + 1, line => { + if (lineIsHidden(doc, line)) updateLineHeight(line, 0) + }) + + if (marker.clearOnEnter) on(marker, "beforeCursorEnter", () => marker.clear()) + + if (marker.readOnly) { + seeReadOnlySpans() + if (doc.history.done.length || doc.history.undone.length) + doc.clearHistory() + } + if (marker.collapsed) { + marker.id = ++nextMarkerId + marker.atomic = true + } + if (cm) { + // Sync editor state + if (updateMaxLine) cm.curOp.updateMaxLine = true + if (marker.collapsed) + regChange(cm, from.line, to.line + 1) + else if (marker.className || marker.startStyle || marker.endStyle || marker.css || + marker.attributes || marker.title) + for (let i = from.line; i <= to.line; i++) regLineChange(cm, i, "text") + if (marker.atomic) reCheckSelection(cm.doc) + signalLater(cm, "markerAdded", cm, marker) + } + return marker +} + +// SHARED TEXTMARKERS + +// A shared marker spans multiple linked documents. It is +// implemented as a meta-marker-object controlling multiple normal +// markers. +export class SharedTextMarker { + constructor(markers, primary) { + this.markers = markers + this.primary = primary + for (let i = 0; i < markers.length; ++i) + markers[i].parent = this + } + + clear() { + if (this.explicitlyCleared) return + this.explicitlyCleared = true + for (let i = 0; i < this.markers.length; ++i) + this.markers[i].clear() + signalLater(this, "clear") + } + + find(side, lineObj) { + return this.primary.find(side, lineObj) + } +} +eventMixin(SharedTextMarker) + +function markTextShared(doc, from, to, options, type) { + options = copyObj(options) + options.shared = false + let markers = [markText(doc, from, to, options, type)], primary = markers[0] + let widget = options.widgetNode + linkedDocs(doc, doc => { + if (widget) options.widgetNode = widget.cloneNode(true) + markers.push(markText(doc, clipPos(doc, from), clipPos(doc, to), options, type)) + for (let i = 0; i < doc.linked.length; ++i) + if (doc.linked[i].isParent) return + primary = lst(markers) + }) + return new SharedTextMarker(markers, primary) +} + +export function findSharedMarkers(doc) { + return doc.findMarks(Pos(doc.first, 0), doc.clipPos(Pos(doc.lastLine())), m => m.parent) +} + +export function copySharedMarkers(doc, markers) { + for (let i = 0; i < markers.length; i++) { + let marker = markers[i], pos = marker.find() + let mFrom = doc.clipPos(pos.from), mTo = doc.clipPos(pos.to) + if (cmp(mFrom, mTo)) { + let subMark = markText(doc, mFrom, mTo, marker.primary, marker.primary.type) + marker.markers.push(subMark) + subMark.parent = marker + } + } +} + +export function detachSharedMarkers(markers) { + for (let i = 0; i < markers.length; i++) { + let marker = markers[i], linked = [marker.primary.doc] + linkedDocs(marker.primary.doc, d => linked.push(d)) + for (let j = 0; j < marker.markers.length; j++) { + let subMarker = marker.markers[j] + if (indexOf(linked, subMarker.doc) == -1) { + subMarker.parent = null + marker.markers.splice(j--, 1) + } + } + } +} diff --git a/docs/js/node_modules/codemirror/src/model/selection.js b/docs/js/node_modules/codemirror/src/model/selection.js new file mode 100644 index 000000000..793cb4ca0 --- /dev/null +++ b/docs/js/node_modules/codemirror/src/model/selection.js @@ -0,0 +1,84 @@ +import { cmp, copyPos, equalCursorPos, maxPos, minPos } from "../line/pos.js" +import { indexOf } from "../util/misc.js" + +// 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). +export class Selection { + constructor(ranges, primIndex) { + this.ranges = ranges + this.primIndex = primIndex + } + + primary() { return this.ranges[this.primIndex] } + + equals(other) { + if (other == this) return true + if (other.primIndex != this.primIndex || other.ranges.length != this.ranges.length) return false + for (let i = 0; i < this.ranges.length; i++) { + let here = this.ranges[i], there = other.ranges[i] + if (!equalCursorPos(here.anchor, there.anchor) || !equalCursorPos(here.head, there.head)) return false + } + return true + } + + deepCopy() { + let out = [] + for (let 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() { + for (let i = 0; i < this.ranges.length; i++) + if (!this.ranges[i].empty()) return true + return false + } + + contains(pos, end) { + if (!end) end = pos + for (let i = 0; i < this.ranges.length; i++) { + let range = this.ranges[i] + if (cmp(end, range.from()) >= 0 && cmp(pos, range.to()) <= 0) + return i + } + return -1 + } +} + +export class Range { + constructor(anchor, head) { + this.anchor = anchor; this.head = head + } + + from() { return minPos(this.anchor, this.head) } + to() { return maxPos(this.anchor, this.head) } + empty() { 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). +export function normalizeSelection(cm, ranges, primIndex) { + let mayTouch = cm && cm.options.selectionsMayTouch + let prim = ranges[primIndex] + ranges.sort((a, b) => cmp(a.from(), b.from())) + primIndex = indexOf(ranges, prim) + for (let i = 1; i < ranges.length; i++) { + let cur = ranges[i], prev = ranges[i - 1] + let diff = cmp(prev.to(), cur.from()) + if (mayTouch && !cur.empty() ? diff > 0 : diff >= 0) { + let from = minPos(prev.from(), cur.from()), to = maxPos(prev.to(), cur.to()) + let 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) +} + +export function simpleSelection(anchor, head) { + return new Selection([new Range(anchor, head || anchor)], 0) +} diff --git a/docs/js/node_modules/codemirror/src/model/selection_updates.js b/docs/js/node_modules/codemirror/src/model/selection_updates.js new file mode 100644 index 000000000..4db2bd7f5 --- /dev/null +++ b/docs/js/node_modules/codemirror/src/model/selection_updates.js @@ -0,0 +1,216 @@ +import { signalLater } from "../util/operation_group.js" +import { ensureCursorVisible } from "../display/scrolling.js" +import { clipPos, cmp, Pos } from "../line/pos.js" +import { getLine } from "../line/utils_line.js" +import { hasHandler, signal, signalCursorActivity } from "../util/event.js" +import { lst, sel_dontScroll } from "../util/misc.js" + +import { addSelectionToHistory } from "./history.js" +import { normalizeSelection, Range, Selection, simpleSelection } from "./selection.js" + +// 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. +export function extendRange(range, head, other, extend) { + if (extend) { + let anchor = range.anchor + if (other) { + let 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. +export function extendSelection(doc, head, other, options, extend) { + if (extend == null) extend = doc.cm && (doc.cm.display.shift || doc.extend) + setSelection(doc, new Selection([extendRange(doc.sel.primary(), head, other, extend)], 0), options) +} + +// Extend all selections (pos is an array of selections with length +// equal the number of selections) +export function extendSelections(doc, heads, options) { + let out = [] + let extend = doc.cm && (doc.cm.display.shift || doc.extend) + for (let i = 0; i < doc.sel.ranges.length; i++) + out[i] = extendRange(doc.sel.ranges[i], heads[i], null, extend) + let newSel = normalizeSelection(doc.cm, out, doc.sel.primIndex) + setSelection(doc, newSel, options) +} + +// Updates a single range in the selection. +export function replaceOneSelection(doc, i, range, options) { + let ranges = doc.sel.ranges.slice(0) + ranges[i] = range + setSelection(doc, normalizeSelection(doc.cm, ranges, doc.sel.primIndex), options) +} + +// Reset the selection to a single range. +export 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, options) { + let obj = { + ranges: sel.ranges, + update: function(ranges) { + this.ranges = [] + for (let i = 0; i < ranges.length; i++) + this.ranges[i] = new Range(clipPos(doc, ranges[i].anchor), + clipPos(doc, ranges[i].head)) + }, + origin: options && options.origin + } + signal(doc, "beforeSelectionChange", doc, obj) + if (doc.cm) signal(doc.cm, "beforeSelectionChange", doc.cm, obj) + if (obj.ranges != sel.ranges) return normalizeSelection(doc.cm, obj.ranges, obj.ranges.length - 1) + else return sel +} + +export function setSelectionReplaceHistory(doc, sel, options) { + let 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. +export function setSelection(doc, sel, options) { + setSelectionNoUndo(doc, sel, options) + addSelectionToHistory(doc, doc.sel, doc.cm ? doc.cm.curOp.id : NaN, options) +} + +export function setSelectionNoUndo(doc, sel, options) { + if (hasHandler(doc, "beforeSelectionChange") || doc.cm && hasHandler(doc.cm, "beforeSelectionChange")) + sel = filterSelectionChange(doc, sel, options) + + let 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 = 1 + doc.cm.curOp.selectionChanged = true + signalCursorActivity(doc.cm) + } + signalLater(doc, "cursorActivity", doc) +} + +// Verify that the selection does not partially select any atomic +// marked ranges. +export function reCheckSelection(doc) { + setSelectionInner(doc, skipAtomicInSelection(doc, doc.sel, null, false)) +} + +// Return a selection that does not partially select any atomic +// ranges. +function skipAtomicInSelection(doc, sel, bias, mayClear) { + let out + for (let i = 0; i < sel.ranges.length; i++) { + let range = sel.ranges[i] + let old = sel.ranges.length == doc.sel.ranges.length && doc.sel.ranges[i] + let newAnchor = skipAtomic(doc, range.anchor, old && old.anchor, bias, mayClear) + let newHead = skipAtomic(doc, range.head, old && old.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(doc.cm, out, sel.primIndex) : sel +} + +function skipAtomicInner(doc, pos, oldPos, dir, mayClear) { + let line = getLine(doc, pos.line) + if (line.markedSpans) for (let i = 0; i < line.markedSpans.length; ++i) { + let sp = line.markedSpans[i], m = sp.marker + + // Determine if we should prevent the cursor being placed to the left/right of an atomic marker + // Historically this was determined using the inclusiveLeft/Right option, but the new way to control it + // is with selectLeft/Right + let preventCursorLeft = ("selectLeft" in m) ? !m.selectLeft : m.inclusiveLeft + let preventCursorRight = ("selectRight" in m) ? !m.selectRight : m.inclusiveRight + + if ((sp.from == null || (preventCursorLeft ? sp.from <= pos.ch : sp.from < pos.ch)) && + (sp.to == null || (preventCursorRight ? sp.to >= pos.ch : sp.to > pos.ch))) { + if (mayClear) { + signal(m, "beforeCursorEnter") + if (m.explicitlyCleared) { + if (!line.markedSpans) break + else {--i; continue} + } + } + if (!m.atomic) continue + + if (oldPos) { + let near = m.find(dir < 0 ? 1 : -1), diff + if (dir < 0 ? preventCursorRight : preventCursorLeft) + near = movePos(doc, near, -dir, near && near.line == pos.line ? line : null) + if (near && near.line == pos.line && (diff = cmp(near, oldPos)) && (dir < 0 ? diff < 0 : diff > 0)) + return skipAtomicInner(doc, near, pos, dir, mayClear) + } + + let far = m.find(dir < 0 ? -1 : 1) + if (dir < 0 ? preventCursorLeft : preventCursorRight) + far = movePos(doc, far, dir, far.line == pos.line ? line : null) + return far ? skipAtomicInner(doc, far, pos, dir, mayClear) : null + } + } + return pos +} + +// Ensure a given position is not inside an atomic range. +export function skipAtomic(doc, pos, oldPos, bias, mayClear) { + let dir = bias || 1 + let found = skipAtomicInner(doc, pos, oldPos, dir, mayClear) || + (!mayClear && skipAtomicInner(doc, pos, oldPos, dir, true)) || + skipAtomicInner(doc, pos, oldPos, -dir, mayClear) || + (!mayClear && skipAtomicInner(doc, pos, oldPos, -dir, true)) + if (!found) { + doc.cantEdit = true + return Pos(doc.first, 0) + } + return found +} + +function movePos(doc, pos, dir, line) { + if (dir < 0 && pos.ch == 0) { + if (pos.line > doc.first) return clipPos(doc, Pos(pos.line - 1)) + else return null + } else if (dir > 0 && pos.ch == (line || getLine(doc, pos.line)).text.length) { + if (pos.line < doc.first + doc.size - 1) return Pos(pos.line + 1, 0) + else return null + } else { + return new Pos(pos.line, pos.ch + dir) + } +} + +export function selectAll(cm) { + cm.setSelection(Pos(cm.firstLine(), 0), Pos(cm.lastLine()), sel_dontScroll) +} diff --git a/docs/js/node_modules/codemirror/src/modes.js b/docs/js/node_modules/codemirror/src/modes.js new file mode 100644 index 000000000..838451702 --- /dev/null +++ b/docs/js/node_modules/codemirror/src/modes.js @@ -0,0 +1,96 @@ +import { copyObj, createObj } from "./util/misc.js" + +// Known modes, by name and by MIME +export let modes = {}, mimeModes = {} + +// Extra arguments are stored as the mode's dependencies, which is +// used by (legacy) mechanisms like loadmode.js to automatically +// load a mode. (Preferred mechanism is the require/define calls.) +export function defineMode(name, mode) { + if (arguments.length > 2) + mode.dependencies = Array.prototype.slice.call(arguments, 2) + modes[name] = mode +} + +export function defineMIME(mime, spec) { + mimeModes[mime] = spec +} + +// Given a MIME type, a {name, ...options} config object, or a name +// string, return a mode config object. +export function resolveMode(spec) { + if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) { + spec = mimeModes[spec] + } else if (spec && typeof spec.name == "string" && mimeModes.hasOwnProperty(spec.name)) { + let found = mimeModes[spec.name] + if (typeof found == "string") found = {name: found} + spec = createObj(found, spec) + spec.name = found.name + } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+xml$/.test(spec)) { + return resolveMode("application/xml") + } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+json$/.test(spec)) { + return resolveMode("application/json") + } + if (typeof spec == "string") return {name: spec} + else return spec || {name: "null"} +} + +// Given a mode spec (anything that resolveMode accepts), find and +// initialize an actual mode object. +export function getMode(options, spec) { + spec = resolveMode(spec) + let mfactory = modes[spec.name] + if (!mfactory) return getMode(options, "text/plain") + let modeObj = mfactory(options, spec) + if (modeExtensions.hasOwnProperty(spec.name)) { + let exts = modeExtensions[spec.name] + for (let prop in exts) { + if (!exts.hasOwnProperty(prop)) continue + if (modeObj.hasOwnProperty(prop)) modeObj["_" + prop] = modeObj[prop] + modeObj[prop] = exts[prop] + } + } + modeObj.name = spec.name + if (spec.helperType) modeObj.helperType = spec.helperType + if (spec.modeProps) for (let prop in spec.modeProps) + modeObj[prop] = spec.modeProps[prop] + + return modeObj +} + +// This can be used to attach properties to mode objects from +// outside the actual mode definition. +export let modeExtensions = {} +export function extendMode(mode, properties) { + let exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {}) + copyObj(properties, exts) +} + +export function copyState(mode, state) { + if (state === true) return state + if (mode.copyState) return mode.copyState(state) + let nstate = {} + for (let n in state) { + let val = state[n] + if (val instanceof Array) val = val.concat([]) + nstate[n] = val + } + return nstate +} + +// Given a mode and a state (for that mode), find the inner mode and +// state at the position that the state refers to. +export function innerMode(mode, state) { + let info + while (mode.innerMode) { + info = mode.innerMode(state) + if (!info || info.mode == mode) break + state = info.state + mode = info.mode + } + return info || {mode: mode, state: state} +} + +export function startState(mode, a1, a2) { + return mode.startState ? mode.startState(a1, a2) : true +} diff --git a/docs/js/node_modules/codemirror/src/util/StringStream.js b/docs/js/node_modules/codemirror/src/util/StringStream.js new file mode 100644 index 000000000..022c4bc20 --- /dev/null +++ b/docs/js/node_modules/codemirror/src/util/StringStream.js @@ -0,0 +1,90 @@ +import { countColumn } from "./misc.js" + +// STRING STREAM + +// Fed to the mode parsers, provides helper functions to make +// parsers more succinct. + +class StringStream { + constructor(string, tabSize, lineOracle) { + this.pos = this.start = 0 + this.string = string + this.tabSize = tabSize || 8 + this.lastColumnPos = this.lastColumnValue = 0 + this.lineStart = 0 + this.lineOracle = lineOracle + } + + eol() {return this.pos >= this.string.length} + sol() {return this.pos == this.lineStart} + peek() {return this.string.charAt(this.pos) || undefined} + next() { + if (this.pos < this.string.length) + return this.string.charAt(this.pos++) + } + eat(match) { + let ch = this.string.charAt(this.pos) + let ok + if (typeof match == "string") ok = ch == match + else ok = ch && (match.test ? match.test(ch) : match(ch)) + if (ok) {++this.pos; return ch} + } + eatWhile(match) { + let start = this.pos + while (this.eat(match)){} + return this.pos > start + } + eatSpace() { + let start = this.pos + while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) ++this.pos + return this.pos > start + } + skipToEnd() {this.pos = this.string.length} + skipTo(ch) { + let found = this.string.indexOf(ch, this.pos) + if (found > -1) {this.pos = found; return true} + } + backUp(n) {this.pos -= n} + column() { + if (this.lastColumnPos < this.start) { + this.lastColumnValue = countColumn(this.string, this.start, this.tabSize, this.lastColumnPos, this.lastColumnValue) + this.lastColumnPos = this.start + } + return this.lastColumnValue - (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0) + } + indentation() { + return countColumn(this.string, null, this.tabSize) - + (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0) + } + match(pattern, consume, caseInsensitive) { + if (typeof pattern == "string") { + let cased = str => caseInsensitive ? str.toLowerCase() : str + let substr = this.string.substr(this.pos, pattern.length) + if (cased(substr) == cased(pattern)) { + if (consume !== false) this.pos += pattern.length + return true + } + } else { + let match = this.string.slice(this.pos).match(pattern) + if (match && match.index > 0) return null + if (match && consume !== false) this.pos += match[0].length + return match + } + } + current(){return this.string.slice(this.start, this.pos)} + hideFirstChars(n, inner) { + this.lineStart += n + try { return inner() } + finally { this.lineStart -= n } + } + lookAhead(n) { + let oracle = this.lineOracle + return oracle && oracle.lookAhead(n) + } + baseToken() { + let oracle = this.lineOracle + return oracle && oracle.baseToken(this.pos) + } +} + +export default StringStream diff --git a/docs/js/node_modules/codemirror/src/util/bidi.js b/docs/js/node_modules/codemirror/src/util/bidi.js new file mode 100644 index 000000000..33ab854d8 --- /dev/null +++ b/docs/js/node_modules/codemirror/src/util/bidi.js @@ -0,0 +1,214 @@ +import { lst } from "./misc.js" + +// BIDI HELPERS + +export function iterateBidiSections(order, from, to, f) { + if (!order) return f(from, to, "ltr", 0) + let found = false + for (let i = 0; i < order.length; ++i) { + let part = order[i] + if (part.from < to && part.to > from || from == to && part.to == from) { + f(Math.max(part.from, from), Math.min(part.to, to), part.level == 1 ? "rtl" : "ltr", i) + found = true + } + } + if (!found) f(from, to, "ltr") +} + +export let bidiOther = null +export function getBidiPartAt(order, ch, sticky) { + let found + bidiOther = null + for (let i = 0; i < order.length; ++i) { + let cur = order[i] + if (cur.from < ch && cur.to > ch) return i + if (cur.to == ch) { + if (cur.from != cur.to && sticky == "before") found = i + else bidiOther = i + } + if (cur.from == ch) { + if (cur.from != cur.to && sticky != "before") found = i + else bidiOther = i + } + } + return found != null ? found : bidiOther +} + +// Bidirectional ordering algorithm +// See http://unicode.org/reports/tr9/tr9-13.html for the algorithm +// that this (partially) implements. + +// One-char codes used for character types: +// L (L): Left-to-Right +// R (R): Right-to-Left +// r (AL): Right-to-Left Arabic +// 1 (EN): European Number +// + (ES): European Number Separator +// % (ET): European Number Terminator +// n (AN): Arabic Number +// , (CS): Common Number Separator +// m (NSM): Non-Spacing Mark +// b (BN): Boundary Neutral +// s (B): Paragraph Separator +// t (S): Segment Separator +// w (WS): Whitespace +// N (ON): Other Neutrals + +// Returns null if characters are ordered as they appear +// (left-to-right), or an array of sections ({from, to, level} +// objects) in the order in which they occur visually. +let bidiOrdering = (function() { + // Character types for codepoints 0 to 0xff + let lowTypes = "bbbbbbbbbtstwsbbbbbbbbbbbbbbssstwNN%%%NNNNNN,N,N1111111111NNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNbbbbbbsbbbbbbbbbbbbbbbbbbbbbbbbbb,N%%%%NNNNLNNNNN%%11NLNNN1LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLN" + // Character types for codepoints 0x600 to 0x6f9 + let arabicTypes = "nnnnnnNNr%%r,rNNmmmmmmmmmmmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmmmmmmmmnnnnnnnnnn%nnrrrmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmnNmmmmmmrrmmNmmmmrr1111111111" + function charType(code) { + if (code <= 0xf7) return lowTypes.charAt(code) + else if (0x590 <= code && code <= 0x5f4) return "R" + else if (0x600 <= code && code <= 0x6f9) return arabicTypes.charAt(code - 0x600) + else if (0x6ee <= code && code <= 0x8ac) return "r" + else if (0x2000 <= code && code <= 0x200b) return "w" + else if (code == 0x200c) return "b" + else return "L" + } + + let bidiRE = /[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/ + let isNeutral = /[stwN]/, isStrong = /[LRr]/, countsAsLeft = /[Lb1n]/, countsAsNum = /[1n]/ + + function BidiSpan(level, from, to) { + this.level = level + this.from = from; this.to = to + } + + return function(str, direction) { + let outerType = direction == "ltr" ? "L" : "R" + + if (str.length == 0 || direction == "ltr" && !bidiRE.test(str)) return false + let len = str.length, types = [] + for (let i = 0; i < len; ++i) + types.push(charType(str.charCodeAt(i))) + + // W1. Examine each non-spacing mark (NSM) in the level run, and + // change the type of the NSM to the type of the previous + // character. If the NSM is at the start of the level run, it will + // get the type of sor. + for (let i = 0, prev = outerType; i < len; ++i) { + let type = types[i] + if (type == "m") types[i] = prev + else prev = type + } + + // W2. Search backwards from each instance of a European number + // until the first strong type (R, L, AL, or sor) is found. If an + // AL is found, change the type of the European number to Arabic + // number. + // W3. Change all ALs to R. + for (let i = 0, cur = outerType; i < len; ++i) { + let type = types[i] + if (type == "1" && cur == "r") types[i] = "n" + else if (isStrong.test(type)) { cur = type; if (type == "r") types[i] = "R" } + } + + // W4. A single European separator between two European numbers + // changes to a European number. A single common separator between + // two numbers of the same type changes to that type. + for (let i = 1, prev = types[0]; i < len - 1; ++i) { + let type = types[i] + if (type == "+" && prev == "1" && types[i+1] == "1") types[i] = "1" + else if (type == "," && prev == types[i+1] && + (prev == "1" || prev == "n")) types[i] = prev + prev = type + } + + // W5. A sequence of European terminators adjacent to European + // numbers changes to all European numbers. + // W6. Otherwise, separators and terminators change to Other + // Neutral. + for (let i = 0; i < len; ++i) { + let type = types[i] + if (type == ",") types[i] = "N" + else if (type == "%") { + let end + for (end = i + 1; end < len && types[end] == "%"; ++end) {} + let replace = (i && types[i-1] == "!") || (end < len && types[end] == "1") ? "1" : "N" + for (let j = i; j < end; ++j) types[j] = replace + i = end - 1 + } + } + + // W7. Search backwards from each instance of a European number + // until the first strong type (R, L, or sor) is found. If an L is + // found, then change the type of the European number to L. + for (let i = 0, cur = outerType; i < len; ++i) { + let type = types[i] + if (cur == "L" && type == "1") types[i] = "L" + else if (isStrong.test(type)) cur = type + } + + // N1. A sequence of neutrals takes the direction of the + // surrounding strong text if the text on both sides has the same + // direction. European and Arabic numbers act as if they were R in + // terms of their influence on neutrals. Start-of-level-run (sor) + // and end-of-level-run (eor) are used at level run boundaries. + // N2. Any remaining neutrals take the embedding direction. + for (let i = 0; i < len; ++i) { + if (isNeutral.test(types[i])) { + let end + for (end = i + 1; end < len && isNeutral.test(types[end]); ++end) {} + let before = (i ? types[i-1] : outerType) == "L" + let after = (end < len ? types[end] : outerType) == "L" + let replace = before == after ? (before ? "L" : "R") : outerType + for (let j = i; j < end; ++j) types[j] = replace + i = end - 1 + } + } + + // Here we depart from the documented algorithm, in order to avoid + // building up an actual levels array. Since there are only three + // levels (0, 1, 2) in an implementation that doesn't take + // explicit embedding into account, we can build up the order on + // the fly, without following the level-based algorithm. + let order = [], m + for (let i = 0; i < len;) { + if (countsAsLeft.test(types[i])) { + let start = i + for (++i; i < len && countsAsLeft.test(types[i]); ++i) {} + order.push(new BidiSpan(0, start, i)) + } else { + let pos = i, at = order.length + for (++i; i < len && types[i] != "L"; ++i) {} + for (let j = pos; j < i;) { + if (countsAsNum.test(types[j])) { + if (pos < j) order.splice(at, 0, new BidiSpan(1, pos, j)) + let nstart = j + for (++j; j < i && countsAsNum.test(types[j]); ++j) {} + order.splice(at, 0, new BidiSpan(2, nstart, j)) + pos = j + } else ++j + } + if (pos < i) order.splice(at, 0, new BidiSpan(1, pos, i)) + } + } + if (direction == "ltr") { + if (order[0].level == 1 && (m = str.match(/^\s+/))) { + order[0].from = m[0].length + order.unshift(new BidiSpan(0, 0, m[0].length)) + } + if (lst(order).level == 1 && (m = str.match(/\s+$/))) { + lst(order).to -= m[0].length + order.push(new BidiSpan(0, len - m[0].length, len)) + } + } + + return direction == "rtl" ? order.reverse() : order + } +})() + +// Get the bidi ordering for the given line (and cache it). Returns +// false for lines that are fully left-to-right, and an array of +// BidiSpan objects otherwise. +export function getOrder(line, direction) { + let order = line.order + if (order == null) order = line.order = bidiOrdering(line.text, direction) + return order +} diff --git a/docs/js/node_modules/codemirror/src/util/browser.js b/docs/js/node_modules/codemirror/src/util/browser.js new file mode 100644 index 000000000..9fc4602c6 --- /dev/null +++ b/docs/js/node_modules/codemirror/src/util/browser.js @@ -0,0 +1,33 @@ +// Kludges for bugs and behavior differences that can't be feature +// detected are enabled based on userAgent etc sniffing. +let userAgent = navigator.userAgent +let platform = navigator.platform + +export let gecko = /gecko\/\d/i.test(userAgent) +let ie_upto10 = /MSIE \d/.test(userAgent) +let ie_11up = /Trident\/(?:[7-9]|\d{2,})\..*rv:(\d+)/.exec(userAgent) +let edge = /Edge\/(\d+)/.exec(userAgent) +export let ie = ie_upto10 || ie_11up || edge +export let ie_version = ie && (ie_upto10 ? document.documentMode || 6 : +(edge || ie_11up)[1]) +export let webkit = !edge && /WebKit\//.test(userAgent) +let qtwebkit = webkit && /Qt\/\d+\.\d+/.test(userAgent) +export let chrome = !edge && /Chrome\//.test(userAgent) +export let presto = /Opera\//.test(userAgent) +export let safari = /Apple Computer/.test(navigator.vendor) +export let mac_geMountainLion = /Mac OS X 1\d\D([8-9]|\d\d)\D/.test(userAgent) +export let phantom = /PhantomJS/.test(userAgent) + +export let ios = !edge && /AppleWebKit/.test(userAgent) && /Mobile\/\w+/.test(userAgent) +export let android = /Android/.test(userAgent) +// This is woefully incomplete. Suggestions for alternative methods welcome. +export let mobile = ios || android || /webOS|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(userAgent) +export let mac = ios || /Mac/.test(platform) +export let chromeOS = /\bCrOS\b/.test(userAgent) +export let windows = /win/i.test(platform) + +let presto_version = presto && 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 +export let flipCtrlCmd = mac && (qtwebkit || presto && (presto_version == null || presto_version < 12.11)) +export let captureRightClick = gecko || (ie && ie_version >= 9) diff --git a/docs/js/node_modules/codemirror/src/util/dom.js b/docs/js/node_modules/codemirror/src/util/dom.js new file mode 100644 index 000000000..04d2569d2 --- /dev/null +++ b/docs/js/node_modules/codemirror/src/util/dom.js @@ -0,0 +1,97 @@ +import { ie, ios } from "./browser.js" + +export function classTest(cls) { return new RegExp("(^|\\s)" + cls + "(?:$|\\s)\\s*") } + +export let rmClass = function(node, cls) { + let current = node.className + let match = classTest(cls).exec(current) + if (match) { + let after = current.slice(match.index + match[0].length) + node.className = current.slice(0, match.index) + (after ? match[1] + after : "") + } +} + +export function removeChildren(e) { + for (let count = e.childNodes.length; count > 0; --count) + e.removeChild(e.firstChild) + return e +} + +export function removeChildrenAndAdd(parent, e) { + return removeChildren(parent).appendChild(e) +} + +export function elt(tag, content, className, style) { + let e = document.createElement(tag) + if (className) e.className = className + if (style) e.style.cssText = style + if (typeof content == "string") e.appendChild(document.createTextNode(content)) + else if (content) for (let i = 0; i < content.length; ++i) e.appendChild(content[i]) + return e +} +// wrapper for elt, which removes the elt from the accessibility tree +export function eltP(tag, content, className, style) { + let e = elt(tag, content, className, style) + e.setAttribute("role", "presentation") + return e +} + +export let range +if (document.createRange) range = function(node, start, end, endNode) { + let r = document.createRange() + r.setEnd(endNode || node, end) + r.setStart(node, start) + return r +} +else range = function(node, start, end) { + let r = document.body.createTextRange() + try { r.moveToElementText(node.parentNode) } + catch(e) { return r } + r.collapse(true) + r.moveEnd("character", end) + r.moveStart("character", start) + return r +} + +export function contains(parent, child) { + if (child.nodeType == 3) // Android browser always returns false when child is a textnode + child = child.parentNode + if (parent.contains) + return parent.contains(child) + do { + if (child.nodeType == 11) child = child.host + if (child == parent) return true + } while (child = child.parentNode) +} + +export function activeElt() { + // IE and Edge may throw an "Unspecified Error" when accessing document.activeElement. + // IE < 10 will throw when accessed while the page is loading or in an iframe. + // IE > 9 and Edge will throw when accessed in an iframe if document.body is unavailable. + let activeElement + try { + activeElement = document.activeElement + } catch(e) { + activeElement = document.body || null + } + while (activeElement && activeElement.shadowRoot && activeElement.shadowRoot.activeElement) + activeElement = activeElement.shadowRoot.activeElement + return activeElement +} + +export function addClass(node, cls) { + let current = node.className + if (!classTest(cls).test(current)) node.className += (current ? " " : "") + cls +} +export function joinClasses(a, b) { + let as = a.split(" ") + for (let i = 0; i < as.length; i++) + if (as[i] && !classTest(as[i]).test(b)) b += " " + as[i] + return b +} + +export let selectInput = function(node) { node.select() } +if (ios) // Mobile Safari apparently has a bug where select() is broken. + selectInput = function(node) { node.selectionStart = 0; node.selectionEnd = node.value.length } +else if (ie) // Suppress mysterious IE10 errors + selectInput = function(node) { try { node.select() } catch(_e) {} } diff --git a/docs/js/node_modules/codemirror/src/util/event.js b/docs/js/node_modules/codemirror/src/util/event.js new file mode 100644 index 000000000..4b6c77057 --- /dev/null +++ b/docs/js/node_modules/codemirror/src/util/event.js @@ -0,0 +1,103 @@ +import { mac } from "./browser.js" +import { indexOf } from "./misc.js" + +// EVENT HANDLING + +// Lightweight event framework. on/off also work on DOM nodes, +// registering native DOM handlers. + +const noHandlers = [] + +export let on = function(emitter, type, f) { + if (emitter.addEventListener) { + emitter.addEventListener(type, f, false) + } else if (emitter.attachEvent) { + emitter.attachEvent("on" + type, f) + } else { + let map = emitter._handlers || (emitter._handlers = {}) + map[type] = (map[type] || noHandlers).concat(f) + } +} + +export function getHandlers(emitter, type) { + return emitter._handlers && emitter._handlers[type] || noHandlers +} + +export function off(emitter, type, f) { + if (emitter.removeEventListener) { + emitter.removeEventListener(type, f, false) + } else if (emitter.detachEvent) { + emitter.detachEvent("on" + type, f) + } else { + let map = emitter._handlers, arr = map && map[type] + if (arr) { + let index = indexOf(arr, f) + if (index > -1) + map[type] = arr.slice(0, index).concat(arr.slice(index + 1)) + } + } +} + +export function signal(emitter, type /*, values...*/) { + let handlers = getHandlers(emitter, type) + if (!handlers.length) return + let args = Array.prototype.slice.call(arguments, 2) + for (let i = 0; i < handlers.length; ++i) handlers[i].apply(null, args) +} + +// The DOM events that CodeMirror handles can be overridden by +// registering a (non-DOM) handler on the editor for the event name, +// and preventDefault-ing the event in that handler. +export function signalDOMEvent(cm, e, override) { + if (typeof e == "string") + e = {type: e, preventDefault: function() { this.defaultPrevented = true }} + signal(cm, override || e.type, cm, e) + return e_defaultPrevented(e) || e.codemirrorIgnore +} + +export function signalCursorActivity(cm) { + let arr = cm._handlers && cm._handlers.cursorActivity + if (!arr) return + let set = cm.curOp.cursorActivityHandlers || (cm.curOp.cursorActivityHandlers = []) + for (let i = 0; i < arr.length; ++i) if (indexOf(set, arr[i]) == -1) + set.push(arr[i]) +} + +export function hasHandler(emitter, type) { + return getHandlers(emitter, type).length > 0 +} + +// Add on and off methods to a constructor's prototype, to make +// registering events on such objects more convenient. +export function eventMixin(ctor) { + ctor.prototype.on = function(type, f) {on(this, type, f)} + ctor.prototype.off = function(type, f) {off(this, type, f)} +} + +// Due to the fact that we still support jurassic IE versions, some +// compatibility wrappers are needed. + +export function e_preventDefault(e) { + if (e.preventDefault) e.preventDefault() + else e.returnValue = false +} +export function e_stopPropagation(e) { + if (e.stopPropagation) e.stopPropagation() + else e.cancelBubble = true +} +export function e_defaultPrevented(e) { + return e.defaultPrevented != null ? e.defaultPrevented : e.returnValue == false +} +export function e_stop(e) {e_preventDefault(e); e_stopPropagation(e)} + +export function e_target(e) {return e.target || e.srcElement} +export function e_button(e) { + let b = e.which + if (b == null) { + if (e.button & 1) b = 1 + else if (e.button & 2) b = 3 + else if (e.button & 4) b = 2 + } + if (mac && e.ctrlKey && b == 1) b = 3 + return b +} diff --git a/docs/js/node_modules/codemirror/src/util/feature_detection.js b/docs/js/node_modules/codemirror/src/util/feature_detection.js new file mode 100644 index 000000000..c33734ebb --- /dev/null +++ b/docs/js/node_modules/codemirror/src/util/feature_detection.js @@ -0,0 +1,84 @@ +import { elt, range, removeChildren, removeChildrenAndAdd } from "./dom.js" +import { ie, ie_version } from "./browser.js" + +// Detect drag-and-drop +export let dragAndDrop = function() { + // There is *some* kind of drag-and-drop support in IE6-8, but I + // couldn't get it to work yet. + if (ie && ie_version < 9) return false + let div = elt('div') + return "draggable" in div || "dragDrop" in div +}() + +let zwspSupported +export function zeroWidthElement(measure) { + if (zwspSupported == null) { + let test = elt("span", "\u200b") + removeChildrenAndAdd(measure, elt("span", [test, document.createTextNode("x")])) + if (measure.firstChild.offsetHeight != 0) + zwspSupported = test.offsetWidth <= 1 && test.offsetHeight > 2 && !(ie && ie_version < 8) + } + let node = zwspSupported ? elt("span", "\u200b") : + elt("span", "\u00a0", null, "display: inline-block; width: 1px; margin-right: -1px") + node.setAttribute("cm-text", "") + return node +} + +// Feature-detect IE's crummy client rect reporting for bidi text +let badBidiRects +export function hasBadBidiRects(measure) { + if (badBidiRects != null) return badBidiRects + let txt = removeChildrenAndAdd(measure, document.createTextNode("A\u062eA")) + let r0 = range(txt, 0, 1).getBoundingClientRect() + let r1 = range(txt, 1, 2).getBoundingClientRect() + removeChildren(measure) + if (!r0 || r0.left == r0.right) return false // Safari returns null in some cases (#2780) + return badBidiRects = (r1.right - r0.right < 3) +} + +// See if "".split is the broken IE version, if so, provide an +// alternative way to split lines. +export let splitLinesAuto = "\n\nb".split(/\n/).length != 3 ? string => { + let pos = 0, result = [], l = string.length + while (pos <= l) { + let nl = string.indexOf("\n", pos) + if (nl == -1) nl = string.length + let line = string.slice(pos, string.charAt(nl - 1) == "\r" ? nl - 1 : nl) + let rt = line.indexOf("\r") + if (rt != -1) { + result.push(line.slice(0, rt)) + pos += rt + 1 + } else { + result.push(line) + pos = nl + 1 + } + } + return result +} : string => string.split(/\r\n?|\n/) + +export let hasSelection = window.getSelection ? te => { + try { return te.selectionStart != te.selectionEnd } + catch(e) { return false } +} : te => { + let range + try {range = te.ownerDocument.selection.createRange()} + catch(e) {} + if (!range || range.parentElement() != te) return false + return range.compareEndPoints("StartToEnd", range) != 0 +} + +export let hasCopyEvent = (() => { + let e = elt("div") + if ("oncopy" in e) return true + e.setAttribute("oncopy", "return;") + return typeof e.oncopy == "function" +})() + +let badZoomedRects = null +export function hasBadZoomedRects(measure) { + if (badZoomedRects != null) return badZoomedRects + let node = removeChildrenAndAdd(measure, elt("span", "x")) + let normal = node.getBoundingClientRect() + let fromRange = range(node, 0, 1).getBoundingClientRect() + return badZoomedRects = Math.abs(normal.left - fromRange.left) > 1 +} diff --git a/docs/js/node_modules/codemirror/src/util/misc.js b/docs/js/node_modules/codemirror/src/util/misc.js new file mode 100644 index 000000000..3337989bd --- /dev/null +++ b/docs/js/node_modules/codemirror/src/util/misc.js @@ -0,0 +1,168 @@ +export function bind(f) { + let args = Array.prototype.slice.call(arguments, 1) + return function(){return f.apply(null, args)} +} + +export function copyObj(obj, target, overwrite) { + if (!target) target = {} + for (let prop in obj) + if (obj.hasOwnProperty(prop) && (overwrite !== false || !target.hasOwnProperty(prop))) + target[prop] = obj[prop] + return target +} + +// Counts the column offset in a string, taking tabs into account. +// Used mostly to find indentation. +export function countColumn(string, end, tabSize, startIndex, startValue) { + if (end == null) { + end = string.search(/[^\s\u00a0]/) + if (end == -1) end = string.length + } + for (let i = startIndex || 0, n = startValue || 0;;) { + let nextTab = string.indexOf("\t", i) + if (nextTab < 0 || nextTab >= end) + return n + (end - i) + n += nextTab - i + n += tabSize - (n % tabSize) + i = nextTab + 1 + } +} + +export class Delayed { + constructor() { + this.id = null + this.f = null + this.time = 0 + this.handler = bind(this.onTimeout, this) + } + onTimeout(self) { + self.id = 0 + if (self.time <= +new Date) { + self.f() + } else { + setTimeout(self.handler, self.time - +new Date) + } + } + set(ms, f) { + this.f = f + const time = +new Date + ms + if (!this.id || time < this.time) { + clearTimeout(this.id) + this.id = setTimeout(this.handler, ms) + this.time = time + } + } +} + +export function indexOf(array, elt) { + for (let i = 0; i < array.length; ++i) + if (array[i] == elt) return i + return -1 +} + +// Number of pixels added to scroller and sizer to hide scrollbar +export let scrollerGap = 30 + +// Returned or thrown by various protocols to signal 'I'm not +// handling this'. +export let Pass = {toString: function(){return "CodeMirror.Pass"}} + +// Reused option objects for setSelection & friends +export let sel_dontScroll = {scroll: false}, sel_mouse = {origin: "*mouse"}, sel_move = {origin: "+move"} + +// The inverse of countColumn -- find the offset that corresponds to +// a particular column. +export function findColumn(string, goal, tabSize) { + for (let pos = 0, col = 0;;) { + let nextTab = string.indexOf("\t", pos) + if (nextTab == -1) nextTab = string.length + let skipped = nextTab - pos + if (nextTab == string.length || col + skipped >= goal) + return pos + Math.min(skipped, goal - col) + col += nextTab - pos + col += tabSize - (col % tabSize) + pos = nextTab + 1 + if (col >= goal) return pos + } +} + +let spaceStrs = [""] +export function spaceStr(n) { + while (spaceStrs.length <= n) + spaceStrs.push(lst(spaceStrs) + " ") + return spaceStrs[n] +} + +export function lst(arr) { return arr[arr.length-1] } + +export function map(array, f) { + let out = [] + for (let i = 0; i < array.length; i++) out[i] = f(array[i], i) + return out +} + +export function insertSorted(array, value, score) { + let pos = 0, priority = score(value) + while (pos < array.length && score(array[pos]) <= priority) pos++ + array.splice(pos, 0, value) +} + +function nothing() {} + +export function createObj(base, props) { + let inst + if (Object.create) { + inst = Object.create(base) + } else { + nothing.prototype = base + inst = new nothing() + } + if (props) copyObj(props, inst) + return inst +} + +let nonASCIISingleCaseWordChar = /[\u00df\u0587\u0590-\u05f4\u0600-\u06ff\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc\uac00-\ud7af]/ +export function isWordCharBasic(ch) { + return /\w/.test(ch) || ch > "\x80" && + (ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(ch)) +} +export function isWordChar(ch, helper) { + if (!helper) return isWordCharBasic(ch) + if (helper.source.indexOf("\\w") > -1 && isWordCharBasic(ch)) return true + return helper.test(ch) +} + +export function isEmpty(obj) { + for (let n in obj) if (obj.hasOwnProperty(n) && obj[n]) return false + return true +} + +// Extending unicode characters. A series of a non-extending char + +// any number of extending chars is treated as a single unit as far +// as editing and measuring is concerned. This is not fully correct, +// since some scripts/fonts/browsers also treat other configurations +// of code points as a group. +let extendingChars = /[\u0300-\u036f\u0483-\u0489\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610-\u061a\u064b-\u065e\u0670\u06d6-\u06dc\u06de-\u06e4\u06e7\u06e8\u06ea-\u06ed\u0711\u0730-\u074a\u07a6-\u07b0\u07eb-\u07f3\u0816-\u0819\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0900-\u0902\u093c\u0941-\u0948\u094d\u0951-\u0955\u0962\u0963\u0981\u09bc\u09be\u09c1-\u09c4\u09cd\u09d7\u09e2\u09e3\u0a01\u0a02\u0a3c\u0a41\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a70\u0a71\u0a75\u0a81\u0a82\u0abc\u0ac1-\u0ac5\u0ac7\u0ac8\u0acd\u0ae2\u0ae3\u0b01\u0b3c\u0b3e\u0b3f\u0b41-\u0b44\u0b4d\u0b56\u0b57\u0b62\u0b63\u0b82\u0bbe\u0bc0\u0bcd\u0bd7\u0c3e-\u0c40\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62\u0c63\u0cbc\u0cbf\u0cc2\u0cc6\u0ccc\u0ccd\u0cd5\u0cd6\u0ce2\u0ce3\u0d3e\u0d41-\u0d44\u0d4d\u0d57\u0d62\u0d63\u0dca\u0dcf\u0dd2-\u0dd4\u0dd6\u0ddf\u0e31\u0e34-\u0e3a\u0e47-\u0e4e\u0eb1\u0eb4-\u0eb9\u0ebb\u0ebc\u0ec8-\u0ecd\u0f18\u0f19\u0f35\u0f37\u0f39\u0f71-\u0f7e\u0f80-\u0f84\u0f86\u0f87\u0f90-\u0f97\u0f99-\u0fbc\u0fc6\u102d-\u1030\u1032-\u1037\u1039\u103a\u103d\u103e\u1058\u1059\u105e-\u1060\u1071-\u1074\u1082\u1085\u1086\u108d\u109d\u135f\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17b7-\u17bd\u17c6\u17c9-\u17d3\u17dd\u180b-\u180d\u18a9\u1920-\u1922\u1927\u1928\u1932\u1939-\u193b\u1a17\u1a18\u1a56\u1a58-\u1a5e\u1a60\u1a62\u1a65-\u1a6c\u1a73-\u1a7c\u1a7f\u1b00-\u1b03\u1b34\u1b36-\u1b3a\u1b3c\u1b42\u1b6b-\u1b73\u1b80\u1b81\u1ba2-\u1ba5\u1ba8\u1ba9\u1c2c-\u1c33\u1c36\u1c37\u1cd0-\u1cd2\u1cd4-\u1ce0\u1ce2-\u1ce8\u1ced\u1dc0-\u1de6\u1dfd-\u1dff\u200c\u200d\u20d0-\u20f0\u2cef-\u2cf1\u2de0-\u2dff\u302a-\u302f\u3099\u309a\ua66f-\ua672\ua67c\ua67d\ua6f0\ua6f1\ua802\ua806\ua80b\ua825\ua826\ua8c4\ua8e0-\ua8f1\ua926-\ua92d\ua947-\ua951\ua980-\ua982\ua9b3\ua9b6-\ua9b9\ua9bc\uaa29-\uaa2e\uaa31\uaa32\uaa35\uaa36\uaa43\uaa4c\uaab0\uaab2-\uaab4\uaab7\uaab8\uaabe\uaabf\uaac1\uabe5\uabe8\uabed\udc00-\udfff\ufb1e\ufe00-\ufe0f\ufe20-\ufe26\uff9e\uff9f]/ +export function isExtendingChar(ch) { return ch.charCodeAt(0) >= 768 && extendingChars.test(ch) } + +// Returns a number from the range [`0`; `str.length`] unless `pos` is outside that range. +export function skipExtendingChars(str, pos, dir) { + while ((dir < 0 ? pos > 0 : pos < str.length) && isExtendingChar(str.charAt(pos))) pos += dir + return pos +} + +// Returns the value from the range [`from`; `to`] that satisfies +// `pred` and is closest to `from`. Assumes that at least `to` +// satisfies `pred`. Supports `from` being greater than `to`. +export function findFirst(pred, from, to) { + // At any point we are certain `to` satisfies `pred`, don't know + // whether `from` does. + let dir = from > to ? -1 : 1 + for (;;) { + if (from == to) return from + let midF = (from + to) / 2, mid = dir < 0 ? Math.ceil(midF) : Math.floor(midF) + if (mid == from) return pred(mid) ? from : to + if (pred(mid)) to = mid + else from = mid + dir + } +} diff --git a/docs/js/node_modules/codemirror/src/util/operation_group.js b/docs/js/node_modules/codemirror/src/util/operation_group.js new file mode 100644 index 000000000..f6815949d --- /dev/null +++ b/docs/js/node_modules/codemirror/src/util/operation_group.js @@ -0,0 +1,72 @@ +import { getHandlers } from "./event.js" + +let operationGroup = null + +export function pushOperation(op) { + if (operationGroup) { + operationGroup.ops.push(op) + } else { + op.ownsGroup = operationGroup = { + ops: [op], + delayedCallbacks: [] + } + } +} + +function fireCallbacksForOps(group) { + // Calls delayed callbacks and cursorActivity handlers until no + // new ones appear + let callbacks = group.delayedCallbacks, i = 0 + do { + for (; i < callbacks.length; i++) + callbacks[i].call(null) + for (let j = 0; j < group.ops.length; j++) { + let op = group.ops[j] + if (op.cursorActivityHandlers) + while (op.cursorActivityCalled < op.cursorActivityHandlers.length) + op.cursorActivityHandlers[op.cursorActivityCalled++].call(null, op.cm) + } + } while (i < callbacks.length) +} + +export function finishOperation(op, endCb) { + let group = op.ownsGroup + if (!group) return + + try { fireCallbacksForOps(group) } + finally { + operationGroup = null + endCb(group) + } +} + +let orphanDelayedCallbacks = null + +// Often, we want to signal events at a point where we are in the +// middle of some work, but don't want the handler to start calling +// other methods on the editor, which might be in an inconsistent +// state or simply not expect any other events to happen. +// signalLater looks whether there are any handlers, and schedules +// them to be executed when the last operation ends, or, if no +// operation is active, when a timeout fires. +export function signalLater(emitter, type /*, values...*/) { + let arr = getHandlers(emitter, type) + if (!arr.length) return + let args = Array.prototype.slice.call(arguments, 2), list + if (operationGroup) { + list = operationGroup.delayedCallbacks + } else if (orphanDelayedCallbacks) { + list = orphanDelayedCallbacks + } else { + list = orphanDelayedCallbacks = [] + setTimeout(fireOrphanDelayed, 0) + } + for (let i = 0; i < arr.length; ++i) + list.push(() => arr[i].apply(null, args)) +} + +function fireOrphanDelayed() { + let delayed = orphanDelayedCallbacks + orphanDelayedCallbacks = null + for (let i = 0; i < delayed.length; ++i) delayed[i]() +} diff --git a/docs/js/node_modules/codemirror/theme/3024-day.css b/docs/js/node_modules/codemirror/theme/3024-day.css new file mode 100644 index 000000000..713265530 --- /dev/null +++ b/docs/js/node_modules/codemirror/theme/3024-day.css @@ -0,0 +1,41 @@ +/* + + Name: 3024 day + Author: Jan T. Sott (http://github.com/idleberg) + + CodeMirror template by Jan T. Sott (https://github.com/idleberg/base16-codemirror) + Original Base16 color scheme by Chris Kempson (https://github.com/chriskempson/base16) + +*/ + +.cm-s-3024-day.CodeMirror { background: #f7f7f7; color: #3a3432; } +.cm-s-3024-day div.CodeMirror-selected { background: #d6d5d4; } + +.cm-s-3024-day .CodeMirror-line::selection, .cm-s-3024-day .CodeMirror-line > span::selection, .cm-s-3024-day .CodeMirror-line > span > span::selection { background: #d6d5d4; } +.cm-s-3024-day .CodeMirror-line::-moz-selection, .cm-s-3024-day .CodeMirror-line > span::-moz-selection, .cm-s-3024-day .CodeMirror-line > span > span::selection { background: #d9d9d9; } + +.cm-s-3024-day .CodeMirror-gutters { background: #f7f7f7; border-right: 0px; } +.cm-s-3024-day .CodeMirror-guttermarker { color: #db2d20; } +.cm-s-3024-day .CodeMirror-guttermarker-subtle { color: #807d7c; } +.cm-s-3024-day .CodeMirror-linenumber { color: #807d7c; } + +.cm-s-3024-day .CodeMirror-cursor { border-left: 1px solid #5c5855; } + +.cm-s-3024-day span.cm-comment { color: #cdab53; } +.cm-s-3024-day span.cm-atom { color: #a16a94; } +.cm-s-3024-day span.cm-number { color: #a16a94; } + +.cm-s-3024-day span.cm-property, .cm-s-3024-day span.cm-attribute { color: #01a252; } +.cm-s-3024-day span.cm-keyword { color: #db2d20; } +.cm-s-3024-day span.cm-string { color: #fded02; } + +.cm-s-3024-day span.cm-variable { color: #01a252; } +.cm-s-3024-day span.cm-variable-2 { color: #01a0e4; } +.cm-s-3024-day span.cm-def { color: #e8bbd0; } +.cm-s-3024-day span.cm-bracket { color: #3a3432; } +.cm-s-3024-day span.cm-tag { color: #db2d20; } +.cm-s-3024-day span.cm-link { color: #a16a94; } +.cm-s-3024-day span.cm-error { background: #db2d20; color: #5c5855; } + +.cm-s-3024-day .CodeMirror-activeline-background { background: #e8f2ff; } +.cm-s-3024-day .CodeMirror-matchingbracket { text-decoration: underline; color: #a16a94 !important; } diff --git a/docs/js/node_modules/codemirror/theme/3024-night.css b/docs/js/node_modules/codemirror/theme/3024-night.css new file mode 100644 index 000000000..adc5900ad --- /dev/null +++ b/docs/js/node_modules/codemirror/theme/3024-night.css @@ -0,0 +1,39 @@ +/* + + Name: 3024 night + Author: Jan T. Sott (http://github.com/idleberg) + + CodeMirror template by Jan T. Sott (https://github.com/idleberg/base16-codemirror) + Original Base16 color scheme by Chris Kempson (https://github.com/chriskempson/base16) + +*/ + +.cm-s-3024-night.CodeMirror { background: #090300; color: #d6d5d4; } +.cm-s-3024-night div.CodeMirror-selected { background: #3a3432; } +.cm-s-3024-night .CodeMirror-line::selection, .cm-s-3024-night .CodeMirror-line > span::selection, .cm-s-3024-night .CodeMirror-line > span > span::selection { background: rgba(58, 52, 50, .99); } +.cm-s-3024-night .CodeMirror-line::-moz-selection, .cm-s-3024-night .CodeMirror-line > span::-moz-selection, .cm-s-3024-night .CodeMirror-line > span > span::-moz-selection { background: rgba(58, 52, 50, .99); } +.cm-s-3024-night .CodeMirror-gutters { background: #090300; border-right: 0px; } +.cm-s-3024-night .CodeMirror-guttermarker { color: #db2d20; } +.cm-s-3024-night .CodeMirror-guttermarker-subtle { color: #5c5855; } +.cm-s-3024-night .CodeMirror-linenumber { color: #5c5855; } + +.cm-s-3024-night .CodeMirror-cursor { border-left: 1px solid #807d7c; } + +.cm-s-3024-night span.cm-comment { color: #cdab53; } +.cm-s-3024-night span.cm-atom { color: #a16a94; } +.cm-s-3024-night span.cm-number { color: #a16a94; } + +.cm-s-3024-night span.cm-property, .cm-s-3024-night span.cm-attribute { color: #01a252; } +.cm-s-3024-night span.cm-keyword { color: #db2d20; } +.cm-s-3024-night span.cm-string { color: #fded02; } + +.cm-s-3024-night span.cm-variable { color: #01a252; } +.cm-s-3024-night span.cm-variable-2 { color: #01a0e4; } +.cm-s-3024-night span.cm-def { color: #e8bbd0; } +.cm-s-3024-night span.cm-bracket { color: #d6d5d4; } +.cm-s-3024-night span.cm-tag { color: #db2d20; } +.cm-s-3024-night span.cm-link { color: #a16a94; } +.cm-s-3024-night span.cm-error { background: #db2d20; color: #807d7c; } + +.cm-s-3024-night .CodeMirror-activeline-background { background: #2F2F2F; } +.cm-s-3024-night .CodeMirror-matchingbracket { text-decoration: underline; color: white !important; } diff --git a/docs/js/node_modules/codemirror/theme/abcdef.css b/docs/js/node_modules/codemirror/theme/abcdef.css new file mode 100644 index 000000000..cf9353094 --- /dev/null +++ b/docs/js/node_modules/codemirror/theme/abcdef.css @@ -0,0 +1,32 @@ +.cm-s-abcdef.CodeMirror { background: #0f0f0f; color: #defdef; } +.cm-s-abcdef div.CodeMirror-selected { background: #515151; } +.cm-s-abcdef .CodeMirror-line::selection, .cm-s-abcdef .CodeMirror-line > span::selection, .cm-s-abcdef .CodeMirror-line > span > span::selection { background: rgba(56, 56, 56, 0.99); } +.cm-s-abcdef .CodeMirror-line::-moz-selection, .cm-s-abcdef .CodeMirror-line > span::-moz-selection, .cm-s-abcdef .CodeMirror-line > span > span::-moz-selection { background: rgba(56, 56, 56, 0.99); } +.cm-s-abcdef .CodeMirror-gutters { background: #555; border-right: 2px solid #314151; } +.cm-s-abcdef .CodeMirror-guttermarker { color: #222; } +.cm-s-abcdef .CodeMirror-guttermarker-subtle { color: azure; } +.cm-s-abcdef .CodeMirror-linenumber { color: #FFFFFF; } +.cm-s-abcdef .CodeMirror-cursor { border-left: 1px solid #00FF00; } + +.cm-s-abcdef span.cm-keyword { color: darkgoldenrod; font-weight: bold; } +.cm-s-abcdef span.cm-atom { color: #77F; } +.cm-s-abcdef span.cm-number { color: violet; } +.cm-s-abcdef span.cm-def { color: #fffabc; } +.cm-s-abcdef span.cm-variable { color: #abcdef; } +.cm-s-abcdef span.cm-variable-2 { color: #cacbcc; } +.cm-s-abcdef span.cm-variable-3, .cm-s-abcdef span.cm-type { color: #def; } +.cm-s-abcdef span.cm-property { color: #fedcba; } +.cm-s-abcdef span.cm-operator { color: #ff0; } +.cm-s-abcdef span.cm-comment { color: #7a7b7c; font-style: italic;} +.cm-s-abcdef span.cm-string { color: #2b4; } +.cm-s-abcdef span.cm-meta { color: #C9F; } +.cm-s-abcdef span.cm-qualifier { color: #FFF700; } +.cm-s-abcdef span.cm-builtin { color: #30aabc; } +.cm-s-abcdef span.cm-bracket { color: #8a8a8a; } +.cm-s-abcdef span.cm-tag { color: #FFDD44; } +.cm-s-abcdef span.cm-attribute { color: #DDFF00; } +.cm-s-abcdef span.cm-error { color: #FF0000; } +.cm-s-abcdef span.cm-header { color: aquamarine; font-weight: bold; } +.cm-s-abcdef span.cm-link { color: blueviolet; } + +.cm-s-abcdef .CodeMirror-activeline-background { background: #314151; } diff --git a/docs/js/node_modules/codemirror/theme/ambiance-mobile.css b/docs/js/node_modules/codemirror/theme/ambiance-mobile.css new file mode 100644 index 000000000..88d332e1a --- /dev/null +++ b/docs/js/node_modules/codemirror/theme/ambiance-mobile.css @@ -0,0 +1,5 @@ +.cm-s-ambiance.CodeMirror { + -webkit-box-shadow: none; + -moz-box-shadow: none; + box-shadow: none; +} diff --git a/docs/js/node_modules/codemirror/theme/ambiance.css b/docs/js/node_modules/codemirror/theme/ambiance.css new file mode 100644 index 000000000..782fca43f --- /dev/null +++ b/docs/js/node_modules/codemirror/theme/ambiance.css @@ -0,0 +1,74 @@ +/* ambiance theme for codemirror */ + +/* Color scheme */ + +.cm-s-ambiance .cm-header { color: blue; } +.cm-s-ambiance .cm-quote { color: #24C2C7; } + +.cm-s-ambiance .cm-keyword { color: #cda869; } +.cm-s-ambiance .cm-atom { color: #CF7EA9; } +.cm-s-ambiance .cm-number { color: #78CF8A; } +.cm-s-ambiance .cm-def { color: #aac6e3; } +.cm-s-ambiance .cm-variable { color: #ffb795; } +.cm-s-ambiance .cm-variable-2 { color: #eed1b3; } +.cm-s-ambiance .cm-variable-3, .cm-s-ambiance .cm-type { color: #faded3; } +.cm-s-ambiance .cm-property { color: #eed1b3; } +.cm-s-ambiance .cm-operator { color: #fa8d6a; } +.cm-s-ambiance .cm-comment { color: #555; font-style:italic; } +.cm-s-ambiance .cm-string { color: #8f9d6a; } +.cm-s-ambiance .cm-string-2 { color: #9d937c; } +.cm-s-ambiance .cm-meta { color: #D2A8A1; } +.cm-s-ambiance .cm-qualifier { color: yellow; } +.cm-s-ambiance .cm-builtin { color: #9999cc; } +.cm-s-ambiance .cm-bracket { color: #24C2C7; } +.cm-s-ambiance .cm-tag { color: #fee4ff; } +.cm-s-ambiance .cm-attribute { color: #9B859D; } +.cm-s-ambiance .cm-hr { color: pink; } +.cm-s-ambiance .cm-link { color: #F4C20B; } +.cm-s-ambiance .cm-special { color: #FF9D00; } +.cm-s-ambiance .cm-error { color: #AF2018; } + +.cm-s-ambiance .CodeMirror-matchingbracket { color: #0f0; } +.cm-s-ambiance .CodeMirror-nonmatchingbracket { color: #f22; } + +.cm-s-ambiance div.CodeMirror-selected { background: rgba(255, 255, 255, 0.15); } +.cm-s-ambiance.CodeMirror-focused div.CodeMirror-selected { background: rgba(255, 255, 255, 0.10); } +.cm-s-ambiance .CodeMirror-line::selection, .cm-s-ambiance .CodeMirror-line > span::selection, .cm-s-ambiance .CodeMirror-line > span > span::selection { background: rgba(255, 255, 255, 0.10); } +.cm-s-ambiance .CodeMirror-line::-moz-selection, .cm-s-ambiance .CodeMirror-line > span::-moz-selection, .cm-s-ambiance .CodeMirror-line > span > span::-moz-selection { background: rgba(255, 255, 255, 0.10); } + +/* Editor styling */ + +.cm-s-ambiance.CodeMirror { + line-height: 1.40em; + color: #E6E1DC; + background-color: #202020; + -webkit-box-shadow: inset 0 0 10px black; + -moz-box-shadow: inset 0 0 10px black; + box-shadow: inset 0 0 10px black; +} + +.cm-s-ambiance .CodeMirror-gutters { + background: #3D3D3D; + border-right: 1px solid #4D4D4D; + box-shadow: 0 10px 20px black; +} + +.cm-s-ambiance .CodeMirror-linenumber { + text-shadow: 0px 1px 1px #4d4d4d; + color: #111; + padding: 0 5px; +} + +.cm-s-ambiance .CodeMirror-guttermarker { color: #aaa; } +.cm-s-ambiance .CodeMirror-guttermarker-subtle { color: #111; } + +.cm-s-ambiance .CodeMirror-cursor { border-left: 1px solid #7991E8; } + +.cm-s-ambiance .CodeMirror-activeline-background { + background: none repeat scroll 0% 0% rgba(255, 255, 255, 0.031); +} + +.cm-s-ambiance.CodeMirror, +.cm-s-ambiance .CodeMirror-gutters { + background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAADICAQAAAAHUWYVAABFFUlEQVQYGbzBCeDVU/74/6fj9HIcx/FRHx9JCFmzMyGRURhLZIkUsoeRfUjS2FNDtr6WkMhO9sm+S8maJfu+Jcsg+/o/c+Z4z/t97/vezy3z+z8ekGlnYICG/o7gdk+wmSHZ1z4pJItqapjoKXWahm8NmV6eOTbWUOp6/6a/XIg6GQqmenJ2lDHyvCFZ2cBDbmtHA043VFhHwXxClWmeYAdLhV00Bd85go8VmaFCkbVkzlQENzfBDZ5gtN7HwF0KDrTwJ0dypSOzpaKCMwQHKTIreYIxlmhXTzTWkVm+LTynZhiSBT3RZQ7aGfjGEd3qyXQ1FDymqbKxpspERQN2MiRjNZlFFQXfCNFm9nM1zpAsoYjmtRTc5ajwuaXc5xrWskT97RaKzAGe5ARHhVUsDbjKklziiX5WROcJwSNCNI+9w1Jwv4Zb2r7lCMZ4oq5C0EdTx+2GzNuKpJ+iFf38JEWkHJn9DNF7mmBDITrWEg0VWL3pHU20tSZnuqWu+R3BtYa8XxV1HO7GyD32UkOpL/yDloINFTmvtId+nmAjxRw40VMwVKiwrKLE4bK5UOVntYwhOcSSXKrJHKPJedocpGjVz/ZMIbnYUPB10/eKCrs5apqpgVmWzBYWpmtKHecJPjaUuEgRDDaU0oZghCJ6zNMQ5ZhDYx05r5v2muQdM0EILtXUsaKiQX9WMEUotagQzFbUNN6NUPC2nm5pxEWGCjMc3GdJHjSU2kORLK/JGSrkfGEIjncU/CYUnOipoYemwj8tST9NsJmB7TUVXtbUtXATJVZXBMvYeTXJfobgJUPmGMP/yFaWonaa6BcFO3nqcIqCozSZoZoSr1g4zJOzuyGnxTEX3lUEJ7WcZgme8ddaWvWJo2AJR9DZU3CUIbhCSG6ybSwN6qtJVnCU2svDTP2ZInOw2cBTrqtQahtNZn9NcJ4l2NaSmSkkP1noZWnVwkLmdUPOwLZEwy2Z3S3R+4rIG9hcbpPXHFVWcQdZkn2FOta3cKWQnNRC5g1LsJah4GCzSVsKnCOY5OAFRTBekyyryeyilhFKva75r4Mc0aWanGEaThcy31s439KKxTzJYY5WTHPU1FtIHjQU3Oip4xlNzj/lBw23dYZVliQa7WAXf4shetcQfatI+jWRDBPmyNeW6A1P5kdDgyYJlba0BIM8BZu1JfrFwItyjcAMR3K0BWOIrtMEXyhyrlVEx3ui5dUBjmB/Q3CXW85R4mBD0s7B+4q5tKUjOlb9qqmhi5AZ6GFIC5HXtOobdYGlVdMVbNJ8toNTFcHxnoL+muBagcctjWnbNMuR00uI7nQESwg5q2qqrKWIfrNUmeQocY6HuyxJV02wj36w00yhpmUFenv4p6fUkZYqLyuinx2RGOjhCXYyJF84oiU00YMOOhhquNdfbOB7gU88pY4xJO8LVdp6/q2voeB4R04vIdhSE40xZObx1HGGJ/ja0LBthFInKaLPPFzuCaYaoj8JjPME8yoyxo6zlBqkiUZYgq00OYMswbWO5NGmq+xhipxHLRW29ARjNKXO0wRnear8XSg4XFPLKEPUS1GqvyLwiuBUoa7zpZ0l5xxFwWmWZC1H5h5FwU8eQ7K+g8UcVY6TMQreVQT/8uQ8Z+ALIXnSEa2pYZQneE9RZbSBNYXfWYJzW/h/4j4Dp1tYVcFIC5019Vyi4ThPqSFCzjGWaHQTBU8q6vrVwgxP9Lkm840imWKpcLCjYTtrKuwvsKSnrvHCXGkSMk9p6lhckfRpIeis+N2PiszT+mFLspyGleUhDwcLrZqmyeylxwjBcKHEapqkmyangyLZRVOijwOtCY5SsG5zL0OwlCJ4y5KznF3EUNDDrinwiyLZRzOXtlBbK5ITHFGLp8Q0R6ab6mS7enI2cFrxOyHvOCFaT1HThS1krjCwqWeurCkk+willhCC+RSZnRXBiZaC5RXRIZYKp2lyfrHwiKPKR0JDzrdU2EFgpidawlFDR6FgXUMNa+g1FY3bUQh2cLCwosRdnuQTS/S+JVrGLeWIvtQUvONJxlqSQYYKpwoN2kaocLjdVsis4Mk80ESF2YpSkzwldjHkjFCUutI/r+EHDU8oCs6yzL3PhWiEooZdFMkymlas4AcI3KmoMMNSQ3tHzjGWCrcJJdYyZC7QFGwjRL9p+MrRkAGWzIaWCn9W0F3TsK01c2ZvQw0byvxuQU0r1lM0qJO7wW0kRIMdDTtXEdzi4VIh+EoIHm0mWtAtpCixlabgn83fKTI7anJe9ST7WIK1DMGpQmYeA58ImV6ezOGOzK2Kgq01pd60cKWiUi9Lievb/0vIDPHQ05Kzt4ddPckQBQtoaurjyHnek/nKzpQLrVgKPjIkh2v4uyezpv+Xoo7fPFXaGFp1vaLKxQ4uUpQQS5VuQs7BCq4xRJv7fwpVvvFEB3j+620haOuocqMhWd6TTPAEx+mdFNGHdranFe95WrWmIvlY4F1Dle2ECgc6cto7SryuqGGGha0tFQ5V53migUKmg6XKAo4qS3mik+0OZpAhOLeZKicacgaYcyx5hypYQE02ZA4xi/pNhOQxR4klNKyqacj+mpxnLTnnGSo85++3ZCZq6lrZkXlGEX3o+C9FieccJbZWVFjC0Yo1FZnJhoYMFoI1hEZ9r6hwg75HwzBNhbZCdJEfJwTPGzJvaKImw1yYX1HDAmpXR+ZJQ/SmgqMNVQb5vgamGwLtt7VwvP7Qk1xpiM5x5Cyv93E06MZmgs0Nya2azIKOYKCGBQQW97RmhKNKF02JZqHEJ4o58qp7X5EcZmc56trXEqzjCBZ1MFGR87Ql2tSTs6CGxS05PTzRQorkbw7aKoKXFDXsYW42VJih/q+FP2BdTzDTwVqOYB13liM50vG7wy28qagyuIXMeQI/Oqq8bcn5wJI50xH00CRntyfpL1T4hydYpoXgNiFzoIUTDZnLNRzh4TBHwbYGDvZkxmlyJloyr6tRihpeUG94GnKtIznREF0tzJG/OOr73JBcrSh1k6WuTprgLU+mnSGnv6Zge0NNz+kTDdH8nuAuTdJDCNb21LCiIuqlYbqGzT3RAoZofQfjFazkqeNWdYaGvYTM001EW2oKPvVk1ldUGSgUtHFwjKM1h9jnFcmy5lChoLNaQMGGDsYbKixlaMBmmsx1QjCfflwTfO/gckW0ruZ3jugKR3R5W9hGUWqCgxuFgsuaCHorotGKzGaeZB9DMsaTnKCpMtwTvOzhYk0rdrArKCqcaWmVk1+F372ur1YkKxgatI8Qfe1gIX9wE9FgS8ESmuABIXnRUbCapcKe+nO7slClSZFzpV/LkLncEb1qiO42fS3R855Su2mCLh62t1SYZZYVmKwIHjREF2uihTzB20JOkz7dkxzYQnK0UOU494wh+VWRc6Un2kpTaVgLDFEkJ/uhzRcI0YKGgpGWOlocBU/a4fKoJ/pEaNV6jip3+Es9VXY078rGnmAdf7t9ylPXS34RBSuYPs1UecZTU78WanhBCHpZ5sAoTz0LGZKjPf9TRypqWEiTvOFglL1fCEY3wY/++rbk7C8bWebA6p6om6PgOL2kp44TFJlVNBXae2rqqdZztOJpT87GQsE9jqCPIe9VReZuQ/CIgacsyZdCpIScSYqcZk8r+nsyCzhyfhOqHGOIvrLknC8wTpFcaYiGC/RU1NRbUeUpocQOnkRpGOrIOcNRx+1uA0UrzhSSt+VyS3SJpnFWkzNDqOFGIWcfR86DnmARTQ1HKIL33ExPiemeOhYSSjzlSUZZuE4TveoJLnBUOFof6KiysCbnAEcZgcUNTDOwkqWu3RWtmGpZwlHhJENdZ3miGz0lJlsKnjbwqSHQjpxnFDlTLLwqJPMZMjd7KrzkSG7VsxXBZE+F8YZkb01Oe00yyRK9psh5SYh29ySPKBo2ylNht7ZkZnsKenjKNJu9PNEyZpaCHv4Kt6RQsLvAVp7M9kIimmCUwGeWqLMmGuIotYMmWNpSahkhZw9FqZsVnKJhsjAHvtHMsTM9fCI06Dx/u3vfUXCqfsKRc4oFY2jMsoo/7DJDwZ1CsIKnJu+J9ldkpmiCxQx1rWjI+T9FwcWWzOuaYH0Hj7klNRVWEQpmaqosakiGNTFHdjS/qnUdmf0NJW5xsL0HhimCCZZSRzmSPTXJQ4aaztAwtZnoabebJ+htCaZ7Cm535ByoqXKbX1WRc4Eh2MkRXWzImVc96Cj4VdOKVxR84VdQsIUM8Psoou2byVHyZFuq7O8otbSQ2UAoeEWTudATLGSpZzVLlXVkPU2Jc+27lsw2jmg5T5VhbeE3BT083K9WsTTkFU/Osi0rC5lRlpwRHUiesNS0sOvmqGML1aRbPAxTJD9ZKtxuob+hhl8cwYGWpJ8nub7t5p6coYbMovZ1BTdaKn1jYD6h4GFDNFyT/Kqe1XCXphXHOKLZmuRSRdBPEfVUXQzJm5YGPGGJdvAEr7hHNdGZnuBvrpciGmopOLf5N0uVMy0FfYToJk90uUCbJupaVpO53UJXR2bVpoU00V2KOo4zMFrBd0Jtz2pa0clT5Q5L8IpQ177mWQejPMEJhuQjS10ref6HHjdEhy1P1EYR7GtO0uSsKJQYLiTnG1rVScj5lyazpqWGl5uBbRWl7m6ixGOOnEsMJR7z8J0n6KMnCdxhiNYQCoZ6CmYLnO8omC3MkW3bktlPmEt/VQQHejL3+dOE5FlPdK/Mq8hZxxJtLyRrepLThYKbLZxkSb5W52vYxNOaOxUF0yxMUPwBTYqCzy01XayYK0sJyWBLqX0MwU5CzoymRzV0EjjeUeLgDpTo6ij42ZAzvD01dHUUTPLU96MdLbBME8nFBn7zJCMtJcZokn8YoqU0FS5WFKyniHobguMcmW8N0XkWZjkyN3hqOMtS08r+/xTBwpZSZ3qiVRX8SzMHHjfUNFjgHEPmY9PL3ykEzxkSre/1ZD6z/NuznuB0RcE1TWTm9zRgfUWVJiG6yrzgmWPXC8EAR4Wxhlad0ZbgQyEz3pG5RVEwwDJH2mgKpjcTiCOzn1lfUWANFbZ2BA8balnEweJC9J0iuaeZoI+ippFCztEKVvckR2iice1JvhVytrQwUAZpgsubCPaU7xUe9vWnaOpaSBEspalykhC9bUlOMpT42ZHca6hyrqKmw/wMR8H5ZmdFoBVJb03O4UL0tSNnvIeRmkrLWqrs78gcrEn2tpcboh0UPOW3UUR9PMk4T4nnNKWmCjlrefhCwxRNztfmIQVdDElvS4m1/WuOujoZCs5XVOjtKPGokJzsYCtFYoWonSPT21DheU/wWhM19FcElwqNGOsp9Q8N/cwXaiND1MmeL1Q5XROtYYgGeFq1aTMsoMmcrKjQrOFQTQ1fmBYhmW6o8Jkjc7iDJRTBIo5kgJD5yMEYA3srCg7VFKwiVJkmRCc5ohGOKhsYMn/XBLdo5taZjlb9YAlGWRimqbCsoY7HFAXLa5I1HPRxMMsQDHFkWtRNniqT9UEeNjcE7RUlrCJ4R2CSJuqlKHWvJXjAUNcITYkenuBRB84TbeepcqTj3zZyFJzgYQdHnqfgI0ddUwS6GqWpsKWhjq9cV0vBAEMN2znq+EBfIWT+pClYw5xsTlJU6GeIBsjGmmANTzJZiIYpgrM0Oa8ZMjd7NP87jxhqGOhJlnQtjuQpB+8aEE00wZFznSJPyHxgH3HkPOsJFvYk8zqCHzTs1BYOa4J3PFU+UVRZxlHDM4YavlNUuMoRveiZA2d7grMNc2g+RbSCEKzmgYsUmWmazFJyoiOZ4KnyhKOGRzWJa0+moyV4TVHDzn51Awtqaphfk/lRQ08FX1iiqxTB/kLwd0VynKfEvI6cd4XMV5bMhZ7gZUWVzYQ6Nm2BYzxJbw3bGthEUUMfgbGeorae6DxHtJoZ6alhZ0+ytiVoK1R4z5PTrOECT/SugseEOlb1MMNR4VRNcJy+V1Hg9ONClSZFZjdHlc6W6FBLdJja2MC5hhpu0DBYEY1TFGwiFAxRRCsYkiM9JRb0JNMVkW6CZYT/2EiTGWmo8k+h4FhDNE7BvppoTSFnmCV5xZKzvcCdDo7VVPnIU+I+Rc68juApC90MwcFCsJ5hDqxgScYKreruyQwTqrzoqDCmhWi4IbhB0Yrt3RGa6GfDv52rKXWhh28dyZaWUvcZeMTBaZoSGyiCtRU5J8iviioHaErs7Jkj61syVzTTgOcUOQ8buFBTYWdL5g3T4qlpe0+wvD63heAXRfCCIed9RbCsp2CiI7raUOYOTU13N8PNHvpaGvayo4a3LLT1lDrVEPT2zLUlheB1R+ZTRfKWJ+dcocLJfi11vyJ51lLqJ0WD7tRwryezjiV5W28uJO9qykzX8JDe2lHl/9oyBwa2UMfOngpXCixvKdXTk3wrsKmiVYdZIqsoWEERjbcUNDuiaQomGoIbFdEHmsyWnuR+IeriKDVLnlawlyNHKwKlSU631PKep8J4Q+ayjkSLKYLhalNHlYvttb6fHm0p6OApsZ4l2VfdqZkjuysy6ysKLlckf1KUutCTs39bmCgEyyoasIWlVaMF7mgmWtBT8Kol5xpH9IGllo8cJdopcvZ2sImlDmMIbtDk3KIpeNiS08lQw11NFPTwVFlPP6pJ2gvRfI7gQUfmNAtf6Gs0wQxDsKGlVBdF8rCa3jzdwMaGHOsItrZk7hAyOzpK9VS06j5F49b0VNGOOfKs3lDToMsMBe9ZWtHFEgxTJLs7qrygKZjUnmCYoeAqeU6jqWuLJup4WghOdvCYJnrSkSzoyRkm5M2StQwVltPkfCAk58tET/CSg+8MUecmotMEnhBKfWBIZsg2ihruMJQaoIm+tkTLKEqspMh00w95gvFCQRtDwTT1gVDDSEVdlwqZfxoQRbK0g+tbiBZxzKlpnpypejdDwTaeOvorMk/IJE10h9CqRe28hhLbe0pMsdSwv4ZbhKivo2BjDWfL8UKJgeavwlwb5KlwhyE4u4XkGE2ytZCznKLCDZZq42VzT8HLCrpruFbIfOIINmh/qCdZ1ZBc65kLHR1Bkyf5zn6pN3SvGKIlFNGplhrO9QSXanLOMQTLCa0YJCRrCZm/CZmrLTm7WzCK4GJDiWUdFeYx1LCFg3NMd0XmCuF3Y5rITLDUsYS9zoHVzwnJoYpSTQoObyEzr4cFBNqYTopoaU/wkyLZ2lPhX/5Y95ulxGTV7KjhWrOZgl8MyUUafjYraNjNU1N3IWcjT5WzWqjwtoarHSUObGYO3GCJZpsBlnJGPd6ZYLyl1GdCA2625IwwJDP8GUKymbzuyPlZlvTUsaUh5zFDhRWFzPKKZLAlWdcQbObgF9tOqOsmB1dqcqYJmWstFbZRRI9poolmqiLnU0POvxScpah2iSL5UJNzgScY5+AuIbpO0YD3NCW+dLMszFSdFCWGqG6eVq2uYVNDdICGD6W7EPRWZEY5gpsE9rUkS3mijzzJnm6UpUFXG1hCUeVoS5WfNcFpblELL2qqrCvMvRfd45oalvKU2tiQ6ePJOVMRXase9iTtLJztPxJKLWpo2CRDcJwn2sWSLKIO1WQWNTCvpVUvOZhgSC40JD0dOctaSqzkCRbXsKlb11Oip6PCJ0IwSJM31j3akRxlP7Rwn6aGaUL0qiLnJkvB3xWZ2+Q1TfCwpQH3G0o92UzmX4o/oJNQMMSQc547wVHhdk+VCw01DFYEnTxzZKAm74QmeNNR1w6WzEhNK15VJzuCdxQ53dRUDws5KvwgBMOEgpcVNe0hZI6RXT1Jd0cyj5nsaEAHgVmGaJIlWdsc5Ui2ElrRR6jrRAttNMEAIWrTDFubkZaok7/AkzfIwfuWVq0jHzuCK4QabtLUMVPB3kJ0oyHTSVFlqMALilJf2Rf8k5aaHtMfayocLBS8L89oKoxpJvnAkDPa0qp5DAUTHKWmCcnthlou8iCKaFFLHWcINd1nyIwXqrSxMNmSs6KmoL2QrKuWtlQ5V0120xQ5vRyZS1rgFkWwhiOwiuQbR0OOVhQM9iS3tiXp4RawRPMp5tDletOOBL95MpM01dZTBM9pkn5qF010rIeHFcFZhmSGpYpTsI6nwhqe5C9ynhlpp5ophuRb6WcJFldkVnVEwwxVfrVkvnWUuNLCg5bgboFHPDlDPDmnK7hUrWiIbjadDclujlZcaokOFup4Ri1kacV6jmrrK1hN9bGwpKEBQ4Q6DvIUXOmo6U5LqQM6EPyiKNjVkPnJkDPNEaxhiFay5ExW1NXVUGqcpYYdPcGiCq7z/TSlbhL4pplWXKd7NZO5QQFrefhRQW/NHOsqcIglc4UhWklR8K0QzbAw08CBDnpbgqXdeD/QUsM4RZXDFBW6WJKe/mFPdH0LtBgiq57wFLzlyQzz82qYx5D5WJP5yVJDW01BfyHnS6HKO/reZqId1WGa4Hkh2kWodJ8i6KoIPlAj2hPt76CzXsVR6koPRzWTfKqIentatYpQw2me4AA3y1Kind3SwoOKZDcFXTwl9tWU6mfgRk9d71sKtlNwrjnYw5tC5n5LdKiGry3JKNlHEd3oaMCFHrazBPMp/uNJ+V7IudcSbeOIdjUEdwl0VHCOZo5t6YluEuaC9mQeMgSfOyKnYGFHcIeQ84yQWbuJYJpZw5CzglDH7gKnWqqM9ZTaXcN0TeYhR84eQtJT76JJ1lREe7WnnvsMmRc9FQ7SBBM9mV3lCUdmHk/S2RAMt0QjFNFqQpWjDPQ01DXWUdDBkXziKPjGEP3VP+zIWU2t7im41FOloyWzn/L6dkUy3VLDaZ6appgDLHPjJEsyvJngWEPUyVBiAaHCTEXwrLvSEbV1e1gKJniicWorC1MUrVjB3uDhJE/wgSOzk1DXpk0k73qCM8xw2UvD5kJmDUfOomqMpWCkJRlvKXGmoeBm18USjVIk04SClxTB6YrgLAPLWYK9HLUt5cmc0vYES8GnTeRc6skZbQkWdxRsIcyBRzx1DbTk9FbU0caTPOgJHhJKnOGIVhQqvKmo0llRw9sabrZkDtdg3PqaKi9oatjY8B+G371paMg6+mZFNNtQ04mWBq3rYLOmtWWQp8KJnpy9DdFensyjdqZ+yY40VJlH8wcdLzC8PZnvHMFUTZUrDTkLyQaGus5X5LzpYAf3i+e/ZlhqGqWhh6Ou6xTR9Z6oi5AZZtp7Mj2EEm8oSpxiYZCHU/1fbGdNNNRRoZMhmilEb2gqHOEJDtXkHK/JnG6IrvbPCwV3NhONVdS1thBMs1T4QOBcTWa2IzhMk2nW5Kyn9tXUtpv9RsG2msxk+ZsQzRQacJncpgke0+T8y5Fzj8BiGo7XlJjaTIlpQs7KFjpqGnKuoyEPeIKnFMkZHvopgh81ySxNFWvJWcKRs70j2FOT012IllEEO1n4pD1513Yg2ssQPOThOkvyrqHUdEXOSEsihmBbTbKX1kLBPWqWkLOqJbjB3GBIZmoa8qWl4CG/iZ7oiA72ZL7TJNeZUY7kFQftDcHHluBzRbCegzMtrRjVQpX2lgoPKKLJAkcbMl01XK2p7yhL8pCBbQ3BN2avJgKvttcrWDK3CiUOVxQ8ZP+pqXKyIxnmBymCg5vJjNfkPK4+c8cIfK8ocVt7kmfd/I5SR1hKvCzUtb+lhgc00ZaO6CyhIQP1Uv4yIZjload72PXX0OIJvnFU+0Zf6MhsJwTfW0r0UwQfW4LNLZl5HK261JCZ4qnBaAreVAS3WrjV0LBnNDUNNDToCEeFfwgcb4gOEqLRhirWkexrCEYKVV711DLYEE1XBEsp5tpTGjorkomKYF9FDXv7fR3BGwbettSxnyL53MBPjsxDZjMh+VUW9NRxq1DhVk+FSxQcaGjV9Pawv6eGByw5qzoy7xk4RsOShqjJwWKe/1pEEfzkobeD/dQJmpqedcyBTy2sr4nGNRH0c0SPWTLrqAc0OQcb/gemKgqucQT7ySWKCn2EUotoCvpZct7RO2sy/QW0IWcXd7pQRQyZVwT2USRO87uhjioTLKV2brpMUcMQRbKH/N2T+UlTpaMls6cmc6CCNy3JdYYSUzzJQ4oSD3oKLncULOiJvjBEC2oqnCJkJluCYy2ZQ5so9YYlZ1VLlQU1mXEW1jZERwj/MUSRc24TdexlqLKfQBtDTScJUV8FszXBEY5ktpD5Ur9hYB4Nb1iikw3JoYpkKX+RodRKFt53MMuRnKSpY31PwYaGaILh3wxJGz9TkTPEETxoCWZrgvOlmyMzxFEwVJE5xZKzvyJ4WxEc16Gd4Xe3Weq4XH2jKRikqOkGQ87hQnC7wBmGYLAnesX3M+S87eFATauuN+Qcrh7xIxXJbUIdMw3JGE3ylCWzrieaqCn4zhGM19TQ3z1oH1AX+pWEqIc7wNGAkULBo/ZxRaV9NNyh4Br3rCHZzbzmSfawBL0dNRwpW1kK9mxPXR9povcdrGSZK9c2k0xwFGzjuniCtRSZCZ6ccZ7gaktmgAOtKbG/JnOkJrjcQTdFMsxRQ2cLY3WTIrlCw1eWKn8R6pvt4GFDso3QoL4a3nLk3G6JrtME3dSenpx7PNFTmga0EaJTLQ061sEeQoWXhSo9LTXsaSjoJQRXeZLtDclbCrYzfzHHeaKjHCVOUkQHO3JeEepr56mhiyaYYKjjNU+Fed1wS5VlhWSqI/hYUdDOkaxiKehoyOnrCV5yBHtbWFqTHCCwtpDcYolesVR5yUzTZBb3RNMd0d6WP+SvhuBmRcGxnuQzT95IC285cr41cLGQ6aJJhmi4TMGempxeimBRQw1tFKV+8jd6KuzoSTqqDxzRtpZkurvKEHxlqXKRIjjfUNNXQsNOsRScoWFLT+YeRZVD3GRN0MdQcKqQjHDMrdGGVu3iYJpQx3WGUvfbmxwFfR20WBq0oYY7LMFhhgYtr8jpaEnaOzjawWWaTP8mMr0t/EPDPoqcnxTBI5o58L7uoWnMrpoqPwgVrlAUWE+V+TQl9rawoyP6QGAlQw2TPRX+YSkxyBC8Z6jhHkXBgQL7WII3DVFnRfCrBfxewv9D6xsyjys4VkhWb9pUU627JllV0YDNHMku/ldNMMXDEo4aFnAkk4U6frNEU4XgZUPmEKHUl44KrzmYamjAbh0JFvGnaTLPu1s9jPCwjFpYiN7z1DTOk/nc07CfDFzmCf7i+bfNHXhDtLeBXzTBT5rkMvWOIxpl4EMh2LGJBu2syDnAEx2naEhHDWMMzPZEhygyS1mS5RTJr5ZkoKbEUoYqr2kqdDUE8ztK7OaIntJkFrIECwv8LJTaVx5XJE86go8dFeZ3FN3rjabCAYpoYEeC9zzJVULBbmZhDyd7ko09ydpNZ3nm2Kee4FPPXHnYEF1nqOFEC08LUVcDvYXkJHW8gTaKCk9YGOeIJhqiE4ToPEepdp7IWFjdwnWaufGMwJJCMtUTTBBK9BGCOy2tGGrJTHIwyEOzp6aPzNMOtlZkDvcEWpP5SVNhfkvDxhmSazTJXYrM9U1E0xwFVwqZQwzJxw6+kGGGUj2FglGGmnb1/G51udRSMNlTw6GGnCcUwVcOpmsqTHa06o72sw1RL02p9z0VbnMLOaIX3QKaYKSCFQzBKEUNHTSc48k53RH9wxGMtpQa5KjjW0W0n6XCCCG4yxNNdhQ4R4l1Ff+2sSd6UFHiIEOyqqFgT01mEUMD+joy75jPhOA+oVVLm309FR4yVOlp4RhLiScNmSmaYF5Pw0STrOIoWMSR2UkRXOMp+M4SHW8o8Zoi6OZgjKOaFar8zZDzkWzvKOjkKBjmCXby8JahhjXULY4KlzgKLvAwxVGhvyd4zxB1d9T0piazmKLCVZY5sKiD0y2ZSYrkUEPUbIk+dlQ4SJHTR50k1DPaUWIdTZW9NJwnJMOECgd7ou/MnppMJ02O1VT4Wsh85MnZzcFTngpXGKo84qmwgKbCL/orR/SzJ2crA+t6Mp94KvxJUeIbT3CQu1uIdlQEOzlKfS3UMcrTiFmOuroocrZrT2AcmamOKg8YomeEKm/rlT2sociMaybaUlFhuqHCM2qIJ+rg4EcDFymiDSxzaHdPcpE62pD5kyM5SBMoA1PaUtfIthS85ig1VPiPPYXgYEMNk4Qq7TXBgo7oT57gPUdwgCHzhIVFPFU6OYJzHAX9m5oNrVjeE61miDrqQ4VSa1oiURTsKHC0IfjNwU2WzK6eqK8jWln4g15TVBnqmDteCJ501PGAocJhhqjZdtBEB6lnhLreFJKxmlKbeGrqLiSThVIbCdGzloasa6lpMQXHCME2boLpJgT7yWaemu6wBONbqGNVRS0PKIL7LckbjmQtR7K8I5qtqel+T/ChJTNIKLjdUMNIRyvOEko9YYl2cwQveBikCNawJKcLBbc7+JM92mysNvd/Fqp8a0k6CNEe7cnZrxlW0wQXaXjaktnRwNOGZKYiONwS7a1JVheq3WgJHlQUGKHKmp4KAxXR/ULURcNgoa4zhKSLpZR3kxRRb0NmD0OFn+UCS7CzI1nbP6+o4x47QZE5xRCt3ZagnYcvmpYQktXdk5YKXTzBC57kKEe0VVuiSYqapssMS3C9p2CKkHOg8B8Pa8p5atrIw3qezIWanMGa5HRDNF6RM9wcacl0N+Q8Z8hsIkSnaIIdHRUOEebAPy1zbCkhM062FCJtif7PU+UtoVXzWKqM1PxXO8cfdruhFQ/a6x3JKYagvVDhQEtNiyiiSQ7OsuRsZUku0CRNDs4Sog6KKjsZgk2bYJqijgsEenoKeniinRXBn/U3lgpPdyDZynQx8IiioMnCep5Ky8mjGs6Wty0l1hUQTcNWswS3WRp2kCNZwJG8omG8JphPUaFbC8lEfabwP7VtM9yoaNCAjpR41VNhrD9LkbN722v0CoZMByFzhaW+MyzRYEWFDQwN2M4/JiT76PuljT3VU/A36eaIThb+R9oZGOAJ9tewkgGvqOMNRWYjT/Cwu99Q8LqDE4TgbLWxJ1jaDDAERsFOFrobgjUsBScaguXU8kKm2RL19tRypSHnHNlHiIZqgufs4opgQdVdwxBNNFBR6kVFqb8ogimOzB6a6HTzrlDHEpYaxjiiA4TMQobkDg2vejjfwJGWmnbVFAw3H3hq2NyQfG7hz4aC+w3BbwbesG0swYayvpAs6++Ri1Vfzx93mFChvyN5xVHTS+0p9aqCAxyZ6ZacZyw5+7uuQkFPR9DDk9NOiE7X1PCYJVjVUqq7JlrHwWALF5nfHNGjApdpqgzx5OwilDhCiDYTgnc9waGW4BdLNNUQvOtpzDOWHDH8D7TR/A/85KljEQu3NREc4Pl/6B1Hhc8Umb5CsKMmGC9EPcxoT2amwHNCmeOEnOPbklnMkbOgIvO5UMOpQrS9UGVdt6iH/fURjhI/WOpaW9OKLYRod6HCUEdOX000wpDZQ6hwg6LgZfOqo1RfT/CrJzjekXOGhpc1VW71ZLbXyyp+93ILbC1kPtIEYx0FIx1VDrLoVzXRKRYWk809yYlC9ImcrinxtabKnzRJk3lAU1OLEN1j2zrYzr2myHRXJFf4h4QKT1qSTzTB5+ZNTzTRkAxX8FcLV2uS8eoQQ2aAkFzvCM72sJIcJET3WPjRk5wi32uSS9rfZajpWEvj9hW42F4o5NytSXYy8IKHay10VYdrcl4SkqscrXpMwyGOgtkajheSxdQqmpxP1L3t4R5PqasFnrQEjytq6qgp9Y09Qx9o4S1FzhUCn1kyHSzBWLemoSGvOqLNhZyBjmCaAUYpMgt4Ck7wBBMMwWKWgjsUwTaGVsxWC1mYoKiyqqeGKYqonSIRQ3KIkHO0pmAxTdBHkbOvfllfr+AA+7gnc50huVKYK393FOyg7rbPO/izI7hE4CnHHHnJ0ogNPRUGeUpsrZZTBJcrovUcJe51BPsr6GkJdhCCsZ6aTtMEb2pqWkqeVtDXE/QVggsU/Nl86d9RMF3DxvZTA58agu810RWawCiSzzXBeU3MMW9oyJUedvNEvQyNu1f10BSMddR1vaLCYpYa/mGocLSiYDcLbQz8aMn5iyF4xBNMs1P0QEOV7o5gaWGuzSeLue4tt3ro7y4Tgm4G/mopdZgl6q0o6KzJWE3mMksNr3r+a6CbT8g5wZNzT9O7fi/zpaOmnz3BRoqos+tv9zMbdpxsqDBOEewtJLt7cg5wtKKbvldpSzRRCD43VFheCI7yZLppggMVBS/KMAdHODJvOwq2NQSbKKKPLdFWQs7Fqo+mpl01JXYRgq8dnGLhTiFzqmWsUMdpllZdbKlyvSdYxhI9YghOtxR8LgSLWHK62mGGVoxzBE8LNWzqH9CUesQzFy5RQzTc56mhi6fgXEWwpKfE5Z7M05ZgZUPmo6auiv8YKzDYwWBLMErIbKHJvOwIrvEdhOBcQ9JdU1NHQ7CXn2XIDFBKU2WAgcX9UAUzDXWd5alwuyJ41Z9rjKLCL4aCp4WarhPm2rH+SaHUYE001JDZ2ZAzXPjdMpZWvC9wmqIB2lLhQ01D5jO06hghWMndbM7yRJMsoCj1vYbnFQVrW9jak3OlEJ3s/96+p33dEPRV5GxiqaGjIthUU6FFEZyqCa5qJrpBdzSw95IUnOPIrCUUjRZQFrbw5PR0R1qiYx3cb6nrWUMrBmmiBQxVHtTew5ICP/ip6g4hed/Akob/32wvBHsIOX83cI8hGeNeNPCIkPmXe8fPKx84OMSRM1MTdXSwjCZ4S30jVGhvqTRak/OVhgGazHuOCud5onEO1lJr6ecVyaOK6H7zqlBlIaHE0oroCgfvGJIdPcmfLNGLjpz7hZwZQpUbFME0A1cIJa7VNORkgfsMBatbKgwwJM9bSvQXeNOvbIjelg6WWvo5kvbKaJJNHexkKNHL9xRyFlH8Ti2riB5wVPhUk7nGkJnoCe428LR/wRGdYIlmWebCyxou1rCk4g/ShugBDX0V0ZQWkh0dOVsagkM0yV6OoLd5ye+pRlsCr0n+KiQrGuq5yJDzrTAXHtLUMduTDBVKrSm3eHL+6ijxhFDX9Z5gVU/wliHYTMiMFpKLNMEywu80wd3meoFmt6VbRMPenhrOc6DVe4pgXU8DnnHakLOIIrlF4FZPIw6R+zxBP0dyq6OOZ4Q5sLKCcz084ok+VsMMyQhNZmmBgX5xIXOEJTmi7VsGTvMTNdHHhpzdbE8Du2oKxgvBqQKdDDnTFOylCFaxR1syz2iqrOI/FEpNc3C6f11/7+ASS6l2inq2ciTrCCzgyemrCL5SVPjQkdPZUmGy2c9Sw9FtR1sS30RmsKPCS4rkIC/2U0MduwucYolGaPjKEyhzmiPYXagyWbYz8LWBDdzRimAXzxx4z8K9hpzlhLq+NiQ97HuKorMUfK/OVvC2JfiHUPCQI/q7J2gjK+tTDNxkCc4TMssqCs4TGtLVwQihyoAWgj9bosU80XGW6Ac9TJGziaUh5+hnFcHOnlaM1iRn29NaqGENTTTSUHCH2tWTeV0osUhH6psuVLjRUmGWhm6OZEshGeNowABHcJ2Bpy2ZszRcKkRXd2QuKVEeXnbfaEq825FguqfgfE2whlChSRMdron+LATTPQ2Z369t4B9C5gs/ylzv+CMmepIDPclFQl13W0rspPd1JOcbghGOEutqCv5qacURQl3dDKyvyJlqKXGPgcM9FfawJAMVmdcspcYKOZc4GjDYkFlK05olNMHyHn4zFNykyOxt99RkHlfwmiHo60l2EKI+mhreEKp080Tbug08BVPcgoqC5zWt+NLDTZ7oNSF51N1qie7Va3uCCwyZbkINf/NED6jzOsBdZjFN8oqG3wxVunqCSYYKf3EdhJyf9YWGf7tRU2oH3VHgPr1fe5J9hOgHd7xQ0y7qBwXr23aGErP0cm64JVjZwsOGqL+mhNgZmhJLW2oY4UhedsyBgzrCKrq7BmcpNVhR6jBPq64Vgi+kn6XE68pp8J5/+0wRHGOpsKenQn9DZntPzjRLZpDAdD2fnSgkG9tmIXnUwQ6WVighs7Yi2MxQ0N3CqYaCXkJ0oyOztMDJjmSSpcpvlrk0RMMOjmArQ04PRV1DO1FwhCVaUVPpKUM03JK5SxPsIWRu8/CGHi8UHChiqGFDTbSRJWeYUDDcH6vJWUxR4k1FXbMUwV6e4AJFXS8oMqsZKqzvYQ9DDQdZckY4aGsIhtlubbd2r3j4QBMoTamdPZk7O/Bf62lacZwneNjQoGcdVU7zJOd7ghsUHOkosagic6cnWc8+4gg285R6zZP5s1/LUbCKIznTwK36PkdwlOrl4U1LwfdCCa+IrvFkmgw1PCAUXKWo0sURXWcI2muKJlgyFzhynCY4RBOsqCjoI1R5zREco0n2Vt09BQtYSizgKNHfUmUrQ5UOCh51BFcLmY7umhYqXKQomOop8bUnWNNQcIiBcYaC6xzMNOS8JQQfeqKBmmglB+97ok/lfk3ygaHSyZaCRTzRxQo6GzLfa2jWBPepw+UmT7SQEJyiyRkhBLMVOfcoMjcK0eZChfUNzFAUzCsEN5vP/X1uP/n/aoMX+K+nw/Hjr/9xOo7j7Pju61tLcgvJpTWXNbfN5jLpi6VfCOviTktKlFusQixdEKWmEBUKNaIpjZRSSOXSgzaaKLdabrm1/9nZ+/f+vd/vz/v9+Xy+zZ7PRorYoZqyLrCwQdEAixxVOEXNNnjX2nUSRlkqGmWowk8lxR50JPy9Bo6qJXaXwNvREBvnThPEPrewryLhcAnj5WE15Fqi8W7R1sAuEu86S4ENikItFN4xkv9Af4nXSnUVcLiA9xzesFpivRRVeFKtsMRaKBhuSbjOELnAUtlSQUpXgdfB4Z1oSbnFEetbQ0IrAe+Y+pqnDcEJFj6S8LDZzZHwY4e3XONNlARraomNEt2bkvGsosA3ioyHm+6jCMbI59wqt4eeara28IzEmyPgoRaUOEDhTVdEJhmCoTWfC0p8aNkCp0oYqih2iqGi4yXeMkOsn4LdLLnmKfh/YogjNsPebeFGR4m9BJHLzB61XQ3BtpISfS2FugsK9FAtLWX1dCRcrCnUp44CNzuCowUZmxSRgYaE6Za0W2u/E7CVXCiI/UOR8aAm1+OSyE3mOUcwyc1zBBeoX1kiKy0Zfxck1Gsyulti11i83QTBF5Kg3pDQThFMVHiPSlK+0cSedng/VaS8bOZbtsBcTcZAR8JP5KeqQ1OYKAi20njdNNRpgnsU//K+JnaXJaGTomr7aYIphoRn9aeShJWKEq9LcozSF7QleEfDI5LYm5bgVkFkRwVDBCVu0DDIkGupo8TZBq+/pMQURYErJQmPKGKjNDkWOLx7Jd5QizdUweIaKrlP7SwJDhZvONjLkOsBBX9UpGxnydhXkfBLQ8IxgojQbLFnJf81JytSljclYYyEFyx0kVBvKWOFJmONpshGAcsduQY5giVNCV51eOdJYo/pLhbvM0uDHSevNKRcrKZIqnCtJeEsO95RoqcgGK4ocZcho1tTYtcZvH41pNQ7vA0WrhIfOSraIIntIAi+NXWCErdbkvrWwjRLrt0NKUdL6KSOscTOdMSOUtBHwL6OLA0vNSdynaWQEnCpIvKaIrJJEbvHkmuNhn6OjM8VkSGSqn1uYJCGHnq9I3aLhNME3t6GjIkO7xrNFumpyTNX/NrwX7CrIRiqqWijI9JO4d1iieykyfiposQIQ8YjjsjlBh6oHWbwRjgYJQn2NgSnNycmJAk3NiXhx44Sxykihxm8ybUwT1OVKySc7vi3OXVkdBJ4AyXBeksDXG0IhgtYY0lY5ahCD0ehborIk5aUWRJviMA7Xt5kyRjonrXENkm8yYqgs8VzgrJmClK20uMM3jRJ0FiQICQF9hdETlLQWRIb5ki6WDfWRPobvO6a4GP5mcOrNzDFELtTkONLh9dXE8xypEg7z8A9jkhrQ6Fhjlg/QVktJXxt4WXzT/03Q8IaQWSqIuEvloQ2mqC9Jfi7wRul4RX3pSPlzpoVlmCtI2jvKHCFhjcM3sN6lqF6HxnKelLjXWbwrpR4xzuCrTUZx2qq9oAh8p6ixCUGr78g8oyjRAtB5CZFwi80VerVpI0h+IeBxa6Zg6kWvpDHaioYYuEsRbDC3eOmC2JvGYLeioxGknL2UATNJN6hmtj1DlpLvDVmocYbrGCVJKOrg4X6DgddLA203BKMFngdJJFtFd7vJLm6KEpc5yjQrkk7M80SGe34X24nSex1Ra5Omgb71JKyg8SrU3i/kARKwWpH0kOGhKkObyfd0ZGjvyXlAkVZ4xRbYJ2irFMkFY1SwyWxr2oo4zlNiV+7zmaweFpT4kR3kaDAFW6xpSqzJay05FtYR4HmZhc9UxKbbfF2V8RG1MBmSaE+kmC6JnaRXK9gsiXhJHl/U0qM0WTcbyhwkYIvFGwjSbjfwhiJt8ZSQU+Bd5+marPMOkVkD0muxYLIfEuhh60x/J92itguihJSEMySVPQnTewnEm+620rTQEMsOfo4/kP/0ARvWjitlpSX7GxBgcMEsd3EEeYWvdytd+Saawi6aCIj1CkGb6Aj9rwhx16Cf3vAwFy5pyLhVonXzy51FDpdEblbkdJbUcEPDEFzQ8qNmhzzLTmmKWKbFCXeEuRabp6rxbvAtLF442QjQ+wEA9eL1xSR7Q0JXzlSHjJ4exq89yR0laScJ/FW6z4a73pFMEfDiRZvuvijIt86RaSFOl01riV2mD1UEvxGk/Geg5aWwGki1zgKPG9J2U8PEg8qYvMsZeytiTRXBMslCU8JSlxi8EabjwUldlDNLfzTUmCgxWsjqWCOHavYAqsknKFIO0yQ61VL5AVFxk6WhEaCAkdJgt9aSkzXlKNX2jEa79waYuc7gq0N3GDJGCBhoiTXUEPsdknCUE1CK0fwsiaylSF2uiDyO4XX3pFhNd7R4itFGc0k/ElBZwWvq+GC6szVeEoS/MZ+qylwpKNKv9Z469UOjqCjwlusicyTxG6VpNxcQ8IncoR4RhLbR+NdpGGmJWOcIzJGUuKPGpQg8rrG21dOMqQssJQ4RxH5jaUqnZuQ0F4Q+cjxLwPtpZbIAk3QTJHQWBE5S1BokoVtDd6lhqr9UpHSUxMcIYl9pojsb8h4SBOsMQcqvOWC2E8EVehqiJ1hrrAEbQxeK0NGZ0Gkq+guSRgniM23bIHVkqwx4hiHd7smaOyglyIyQuM978j4VS08J/A2G1KeMBRo4fBaSNhKUEZfQewVQ/C1I+MgfbEleEzCUw7mKXI0M3hd1EESVji8x5uQ41nxs1q4RMJCCXs7Iq9acpxn22oSDnQ/sJTxsCbHIYZiLyhY05TY0ZLIOQrGaSJDDN4t8pVaIrsqqFdEegtizc1iTew5Q4ayBDMUsQMkXocaYkc0hZua412siZ1rSXlR460zRJ5SlHGe5j801RLMlJTxtaOM3Q1pvxJ45zUlWFD7rsAbpfEm1JHxG0eh8w2R7QQVzBUw28FhFp5QZzq8t2rx2joqulYTWSuJdTYfWwqMFMcovFmSyJPNyLhE4E10pHzYjOC3huArRa571ZsGajQpQx38SBP5pyZB6lMU3khDnp0MBV51BE9o2E+TY5Ml2E8S7C0o6w1xvCZjf0HkVEHCzFoyNmqC+9wdcqN+Tp7jSDheE9ws8Y5V0NJCn2bk2tqSY4okdrEhx1iDN8cSudwepWmAGXKcJXK65H9to8jYQRH7SBF01ESUJdd0TayVInaWhLkOjlXE5irKGOnI6GSWGCJa482zBI9rCr0jyTVcEuzriC1vcr6mwFGSiqy5zMwxBH/TJHwjSPhL8+01kaaSUuMFKTcLEvaUePcrSmwn8DZrgikWb7CGPxkSjhQwrRk57tctmxLsb9sZvL9LSlyuSLlWkqOjwduo8b6Uv1DkmudIeFF2dHCgxVtk8dpIvHpBxhEOdhKk7OLIUSdJ+cSRY57B+0DgGUUlNfpthTfGkauzxrvTsUUaCVhlKeteTXCoJDCa2NOKhOmC4G1H8JBd4OBZReSRGkqcb/CO1PyLJTLB4j1q8JYaIutEjSLX8YKM+a6phdMsdLFUoV5RTm9JSkuDN8WcIon0NZMNZWh1q8C7SJEwV5HxrmnnTrf3KoJBlmCYI2ilSLlfEvlE4011NNgjgthzEua0oKK7JLE7HZHlEl60BLMVFewg4EWNt0ThrVNEVkkiTwpKXSWJzdRENgvKGq4IhjsiezgSFtsfCUq8qki5S1LRQeYQQ4nemmCkImWMw3tFUoUBZk4NOeZYEp4XRKTGa6wJjrWNHBVJR4m3FCnbuD6aak2WsMTh3SZImGCIPKNgsDpVwnsa70K31lCFJZYcwwSMFcQulGTsZuEaSdBXkPGZhu0FsdUO73RHjq8MPGGIfaGIbVTk6iuI3GFgucHrIQkmWSJdBd7BBu+uOryWAhY7+Lki9rK5wtEQzWwvtbqGhIMFwWRJsElsY4m9IIg9L6lCX0VklaPAYkfkZEGDnOWowlBJjtMUkcGK4Lg6EtoZInMUBVYLgn0UsdmCyCz7gIGHFfk+k1QwTh5We7A9x+IdJ6CvIkEagms0hR50eH9UnTQJ+2oiKyVlLFUE+8gBGu8MQ3CppUHesnjTHN4QB/UGPhCTHLFPHMFrCqa73gqObUJGa03wgbhHkrCfpEpzNLE7JDS25FMKhlhKKWKfCgqstLCPu1zBXy0J2ztwjtixBu8UTRn9LVtkmCN2iyFhtME70JHRQ1KVZXqKI/KNIKYMCYs1GUMEKbM1bKOI9LDXC7zbHS+bt+1MTWS9odA9DtrYtpbImQJ2VHh/lisEwaHqUk1kjKTAKknkBEXkbkdMGwq0dnhzLJF3NJH3JVwrqOB4Sca2hti75nmJN0WzxS6UxDYoEpxpa4htVlRjkYE7DZGzJVU72uC9IyhQL4i8YfGWSYLLNcHXloyz7QhNifmKSE9JgfGmuyLhc403Xm9vqcp6gXe3xuuv8F6VJNxkyTHEkHG2g0aKXL0MsXc1bGfgas2//dCONXiNLCX+5mB7eZIl1kHh7ajwpikyzlUUWOVOsjSQlsS+M0R+pPje/dzBXRZGO0rMtgQrLLG9VSu9n6CMXS3BhwYmSoIBhsjNBmZbgusE9BCPCP5triU4VhNbJfE+swSP27aayE8tuTpYYjtrYjMVGZdp2NpS1s6aBnKSHDsbKuplKbHM4a0wMFd/5/DmGyKrJSUaW4IBrqUhx0vyfzTBBLPIUcnZdrAkNsKR0sWRspumSns6Ch0v/qqIbBYUWKvPU/CFoyrDJGwSNFhbA/MlzKqjrO80hRbpKx0Jewsi/STftwGSlKc1JZyAzx05dhLEdnfQvhZOqiHWWEAHC7+30FuRcZUgaO5gpaIK+xsiHRUsqaPElTV40xQZQ107Q9BZE1nryDVGU9ZSQ47bmhBpLcYpUt7S+xuK/FiT8qKjwXYw5ypS2iuCv7q1gtgjhuBuB8LCFY5cUuCNtsQOFcT+4Ih9JX+k8Ea6v0iCIRZOtCT0Et00JW5UeC85Cg0ScK0k411HcG1zKtre3SeITBRk7WfwDhEvaYLTHP9le0m8By0JDwn4TlLW/aJOvGHxdjYUes+ScZigCkYQdNdEOhkiezgShqkx8ueKjI8lDfK2oNiOFvrZH1hS+tk7NV7nOmLHicGWEgubkXKdwdtZknCLJXaCpkrjZBtLZFsDP9CdxWsSr05Sxl6CMmoFbCOgryX40uDtamB7SVmXW4Ihlgpmq+00tBKUUa83WbjLUNkzDmY7cow1JDygyPGlhgGKYKz4vcV7QBNbJIgM11TUqZaMdwTeSguH6rOaw1JRKzaaGyxVm2EJ/uCIrVWUcZUkcp2grMsEjK+DMwS59jQk3Kd6SEq1d0S6uVmO4Bc1lDXTUcHjluCXEq+1OlBDj1pi9zgiXxnKuE0SqTXwhqbETW6RggMEnGl/q49UT2iCzgJvRwVXS2K/d6+ZkyUl7jawSVLit46EwxVljDZwoSQ20sDBihztHfk2yA8NVZghiXwrYHQdfKAOtzsayjhY9bY0yE2CWEeJ9xfzO423xhL5syS2TFJofO2pboHob0nY4GiAgRrvGQEDa/FWSsoaaYl0syRsEt3kWoH3B01shCXhTUWe9w3Bt44SC9QCh3eShQctwbaK2ApLroGCMlZrYqvlY3qYhM0aXpFkPOuoqJ3Dm6fxXrGwVF9gCWZagjPqznfkuMKQ8DPTQRO8ZqG1hPGKEm9IgpGW4DZDgTNriTxvFiq+Lz+0cKfp4wj6OCK9JSnzNSn9LFU7UhKZZMnYwcJ8s8yRsECScK4j5UOB95HFO0CzhY4xJxuCix0lDlEUeMdS6EZBkTsUkZ4K74dugyTXS7aNgL8aqjDfkCE0ZbwkCXpaWCKhl8P7VD5jxykivSyxyZrYERbe168LYu9ZYh86IkscgVLE7tWPKmJv11CgoyJltMEbrohtVAQfO4ImltiHEroYEs7RxAarVpY8AwXMcMReFOTYWe5iiLRQxJ5Q8DtJ8LQhWOhIeFESPGsILhbNDRljNbHzNRlTFbk2S3L0NOS6V1KFJYKUbSTcIIhM0wQ/s2TM0SRMNcQmSap3jCH4yhJZKSkwyRHpYYgsFeQ4U7xoCB7VVOExhXepo9ABBsYbvGWKXPME3lyH95YioZ0gssQRWWbI+FaSMkXijZXwgiTlYdPdkNLaETxlyDVIwqeaEus0aTcYcg0RVOkpR3CSJqIddK+90JCxzsDVloyrFd5ZAr4TBKfaWa6boEA7C7s6EpYaeFPjveooY72mjIccLHJ9HUwVlDhKkmutJDJBwnp1rvulJZggKDRfbXAkvC/4l3ozQOG9a8lxjx0i7nV4jSXc7vhe3OwIxjgSHjdEhhsif9YkPGlus3iLFDnWOFhtCZbJg0UbQcIaR67JjthoCyMEZRwhiXWyxO5QxI6w5NhT4U1WsJvDO60J34fW9hwzwlKij6ZAW9ne4L0s8C6XeBMEkd/LQy1VucBRot6QMlbivaBhoBgjqGiCJNhsqVp/S2SsG6DIONCR0dXhvWbJ+MRRZJkkuEjgDXJjFQW6SSL7GXK8Z2CZg7cVsbWGoKmEpzQ5elpiy8Ryg7dMkLLUEauzeO86CuwlSOlgYLojZWeJ9xM3S1PWfEfKl5ISLQ0MEKR8YOB2QfCxJBjrKPCN4f9MkaSsqoVXJBmP7EpFZ9UQfOoOFwSzBN4MQ8LsGrymlipcJQhmy0GaQjPqCHaXRwuCZwRbqK2Fg9wlClZqYicrIgMdZfxTQ0c7TBIbrChxmuzoKG8XRaSrIhhiyNFJkrC7oIAWMEOQa5aBekPCRknCo4IKPrYkvCDI8aYmY7WFtprgekcJZ3oLIqssCSMtFbQTJKwXYy3BY5oCh2iKPCpJOE+zRdpYgi6O2KmOAgvVCYaU4ySRek1sgyFhJ403QFHiVEmJHwtybO1gs8Hr5+BETQX3War0qZngYGgtVZtoqd6vFSk/UwdZElYqyjrF4HXUeFspIi9IGKf4j92pKGAdCYMVsbcV3kRF0N+R8LUd5PCsIGWoxDtBkCI0nKofdJQxT+LtZflvuc8Q3CjwWkq8KwUpHzkK/NmSsclCL0nseQdj5FRH5CNHSgtLiW80Of5HU9Hhlsga9bnBq3fEVltKfO5IaSTmGjjc4J0otcP7QsJUSQM8pEj5/wCuUuC2DWz8AAAAAElFTkSuQmCC"); +} diff --git a/docs/js/node_modules/codemirror/theme/base16-dark.css b/docs/js/node_modules/codemirror/theme/base16-dark.css new file mode 100644 index 000000000..026a81689 --- /dev/null +++ b/docs/js/node_modules/codemirror/theme/base16-dark.css @@ -0,0 +1,38 @@ +/* + + Name: Base16 Default Dark + Author: Chris Kempson (http://chriskempson.com) + + CodeMirror template by Jan T. Sott (https://github.com/idleberg/base16-codemirror) + Original Base16 color scheme by Chris Kempson (https://github.com/chriskempson/base16) + +*/ + +.cm-s-base16-dark.CodeMirror { background: #151515; color: #e0e0e0; } +.cm-s-base16-dark div.CodeMirror-selected { background: #303030; } +.cm-s-base16-dark .CodeMirror-line::selection, .cm-s-base16-dark .CodeMirror-line > span::selection, .cm-s-base16-dark .CodeMirror-line > span > span::selection { background: rgba(48, 48, 48, .99); } +.cm-s-base16-dark .CodeMirror-line::-moz-selection, .cm-s-base16-dark .CodeMirror-line > span::-moz-selection, .cm-s-base16-dark .CodeMirror-line > span > span::-moz-selection { background: rgba(48, 48, 48, .99); } +.cm-s-base16-dark .CodeMirror-gutters { background: #151515; border-right: 0px; } +.cm-s-base16-dark .CodeMirror-guttermarker { color: #ac4142; } +.cm-s-base16-dark .CodeMirror-guttermarker-subtle { color: #505050; } +.cm-s-base16-dark .CodeMirror-linenumber { color: #505050; } +.cm-s-base16-dark .CodeMirror-cursor { border-left: 1px solid #b0b0b0; } + +.cm-s-base16-dark span.cm-comment { color: #8f5536; } +.cm-s-base16-dark span.cm-atom { color: #aa759f; } +.cm-s-base16-dark span.cm-number { color: #aa759f; } + +.cm-s-base16-dark span.cm-property, .cm-s-base16-dark span.cm-attribute { color: #90a959; } +.cm-s-base16-dark span.cm-keyword { color: #ac4142; } +.cm-s-base16-dark span.cm-string { color: #f4bf75; } + +.cm-s-base16-dark span.cm-variable { color: #90a959; } +.cm-s-base16-dark span.cm-variable-2 { color: #6a9fb5; } +.cm-s-base16-dark span.cm-def { color: #d28445; } +.cm-s-base16-dark span.cm-bracket { color: #e0e0e0; } +.cm-s-base16-dark span.cm-tag { color: #ac4142; } +.cm-s-base16-dark span.cm-link { color: #aa759f; } +.cm-s-base16-dark span.cm-error { background: #ac4142; color: #b0b0b0; } + +.cm-s-base16-dark .CodeMirror-activeline-background { background: #202020; } +.cm-s-base16-dark .CodeMirror-matchingbracket { text-decoration: underline; color: white !important; } diff --git a/docs/js/node_modules/codemirror/theme/base16-light.css b/docs/js/node_modules/codemirror/theme/base16-light.css new file mode 100644 index 000000000..1d5f582f6 --- /dev/null +++ b/docs/js/node_modules/codemirror/theme/base16-light.css @@ -0,0 +1,38 @@ +/* + + Name: Base16 Default Light + Author: Chris Kempson (http://chriskempson.com) + + CodeMirror template by Jan T. Sott (https://github.com/idleberg/base16-codemirror) + Original Base16 color scheme by Chris Kempson (https://github.com/chriskempson/base16) + +*/ + +.cm-s-base16-light.CodeMirror { background: #f5f5f5; color: #202020; } +.cm-s-base16-light div.CodeMirror-selected { background: #e0e0e0; } +.cm-s-base16-light .CodeMirror-line::selection, .cm-s-base16-light .CodeMirror-line > span::selection, .cm-s-base16-light .CodeMirror-line > span > span::selection { background: #e0e0e0; } +.cm-s-base16-light .CodeMirror-line::-moz-selection, .cm-s-base16-light .CodeMirror-line > span::-moz-selection, .cm-s-base16-light .CodeMirror-line > span > span::-moz-selection { background: #e0e0e0; } +.cm-s-base16-light .CodeMirror-gutters { background: #f5f5f5; border-right: 0px; } +.cm-s-base16-light .CodeMirror-guttermarker { color: #ac4142; } +.cm-s-base16-light .CodeMirror-guttermarker-subtle { color: #b0b0b0; } +.cm-s-base16-light .CodeMirror-linenumber { color: #b0b0b0; } +.cm-s-base16-light .CodeMirror-cursor { border-left: 1px solid #505050; } + +.cm-s-base16-light span.cm-comment { color: #8f5536; } +.cm-s-base16-light span.cm-atom { color: #aa759f; } +.cm-s-base16-light span.cm-number { color: #aa759f; } + +.cm-s-base16-light span.cm-property, .cm-s-base16-light span.cm-attribute { color: #90a959; } +.cm-s-base16-light span.cm-keyword { color: #ac4142; } +.cm-s-base16-light span.cm-string { color: #f4bf75; } + +.cm-s-base16-light span.cm-variable { color: #90a959; } +.cm-s-base16-light span.cm-variable-2 { color: #6a9fb5; } +.cm-s-base16-light span.cm-def { color: #d28445; } +.cm-s-base16-light span.cm-bracket { color: #202020; } +.cm-s-base16-light span.cm-tag { color: #ac4142; } +.cm-s-base16-light span.cm-link { color: #aa759f; } +.cm-s-base16-light span.cm-error { background: #ac4142; color: #505050; } + +.cm-s-base16-light .CodeMirror-activeline-background { background: #DDDCDC; } +.cm-s-base16-light .CodeMirror-matchingbracket { color: #f5f5f5 !important; background-color: #6A9FB5 !important} diff --git a/docs/js/node_modules/codemirror/theme/bespin.css b/docs/js/node_modules/codemirror/theme/bespin.css new file mode 100644 index 000000000..60913ba93 --- /dev/null +++ b/docs/js/node_modules/codemirror/theme/bespin.css @@ -0,0 +1,34 @@ +/* + + Name: Bespin + Author: Mozilla / Jan T. Sott + + CodeMirror template by Jan T. Sott (https://github.com/idleberg/base16-codemirror) + Original Base16 color scheme by Chris Kempson (https://github.com/chriskempson/base16) + +*/ + +.cm-s-bespin.CodeMirror {background: #28211c; color: #9d9b97;} +.cm-s-bespin div.CodeMirror-selected {background: #36312e !important;} +.cm-s-bespin .CodeMirror-gutters {background: #28211c; border-right: 0px;} +.cm-s-bespin .CodeMirror-linenumber {color: #666666;} +.cm-s-bespin .CodeMirror-cursor {border-left: 1px solid #797977 !important;} + +.cm-s-bespin span.cm-comment {color: #937121;} +.cm-s-bespin span.cm-atom {color: #9b859d;} +.cm-s-bespin span.cm-number {color: #9b859d;} + +.cm-s-bespin span.cm-property, .cm-s-bespin span.cm-attribute {color: #54be0d;} +.cm-s-bespin span.cm-keyword {color: #cf6a4c;} +.cm-s-bespin span.cm-string {color: #f9ee98;} + +.cm-s-bespin span.cm-variable {color: #54be0d;} +.cm-s-bespin span.cm-variable-2 {color: #5ea6ea;} +.cm-s-bespin span.cm-def {color: #cf7d34;} +.cm-s-bespin span.cm-error {background: #cf6a4c; color: #797977;} +.cm-s-bespin span.cm-bracket {color: #9d9b97;} +.cm-s-bespin span.cm-tag {color: #cf6a4c;} +.cm-s-bespin span.cm-link {color: #9b859d;} + +.cm-s-bespin .CodeMirror-matchingbracket { text-decoration: underline; color: white !important;} +.cm-s-bespin .CodeMirror-activeline-background { background: #404040; } diff --git a/docs/js/node_modules/codemirror/theme/blackboard.css b/docs/js/node_modules/codemirror/theme/blackboard.css new file mode 100644 index 000000000..b6eaedb18 --- /dev/null +++ b/docs/js/node_modules/codemirror/theme/blackboard.css @@ -0,0 +1,32 @@ +/* Port of TextMate's Blackboard theme */ + +.cm-s-blackboard.CodeMirror { background: #0C1021; color: #F8F8F8; } +.cm-s-blackboard div.CodeMirror-selected { background: #253B76; } +.cm-s-blackboard .CodeMirror-line::selection, .cm-s-blackboard .CodeMirror-line > span::selection, .cm-s-blackboard .CodeMirror-line > span > span::selection { background: rgba(37, 59, 118, .99); } +.cm-s-blackboard .CodeMirror-line::-moz-selection, .cm-s-blackboard .CodeMirror-line > span::-moz-selection, .cm-s-blackboard .CodeMirror-line > span > span::-moz-selection { background: rgba(37, 59, 118, .99); } +.cm-s-blackboard .CodeMirror-gutters { background: #0C1021; border-right: 0; } +.cm-s-blackboard .CodeMirror-guttermarker { color: #FBDE2D; } +.cm-s-blackboard .CodeMirror-guttermarker-subtle { color: #888; } +.cm-s-blackboard .CodeMirror-linenumber { color: #888; } +.cm-s-blackboard .CodeMirror-cursor { border-left: 1px solid #A7A7A7; } + +.cm-s-blackboard .cm-keyword { color: #FBDE2D; } +.cm-s-blackboard .cm-atom { color: #D8FA3C; } +.cm-s-blackboard .cm-number { color: #D8FA3C; } +.cm-s-blackboard .cm-def { color: #8DA6CE; } +.cm-s-blackboard .cm-variable { color: #FF6400; } +.cm-s-blackboard .cm-operator { color: #FBDE2D; } +.cm-s-blackboard .cm-comment { color: #AEAEAE; } +.cm-s-blackboard .cm-string { color: #61CE3C; } +.cm-s-blackboard .cm-string-2 { color: #61CE3C; } +.cm-s-blackboard .cm-meta { color: #D8FA3C; } +.cm-s-blackboard .cm-builtin { color: #8DA6CE; } +.cm-s-blackboard .cm-tag { color: #8DA6CE; } +.cm-s-blackboard .cm-attribute { color: #8DA6CE; } +.cm-s-blackboard .cm-header { color: #FF6400; } +.cm-s-blackboard .cm-hr { color: #AEAEAE; } +.cm-s-blackboard .cm-link { color: #8DA6CE; } +.cm-s-blackboard .cm-error { background: #9D1E15; color: #F8F8F8; } + +.cm-s-blackboard .CodeMirror-activeline-background { background: #3C3636; } +.cm-s-blackboard .CodeMirror-matchingbracket { outline:1px solid grey;color:white !important; } diff --git a/docs/js/node_modules/codemirror/theme/cobalt.css b/docs/js/node_modules/codemirror/theme/cobalt.css new file mode 100644 index 000000000..bbbda3b54 --- /dev/null +++ b/docs/js/node_modules/codemirror/theme/cobalt.css @@ -0,0 +1,25 @@ +.cm-s-cobalt.CodeMirror { background: #002240; color: white; } +.cm-s-cobalt div.CodeMirror-selected { background: #b36539; } +.cm-s-cobalt .CodeMirror-line::selection, .cm-s-cobalt .CodeMirror-line > span::selection, .cm-s-cobalt .CodeMirror-line > span > span::selection { background: rgba(179, 101, 57, .99); } +.cm-s-cobalt .CodeMirror-line::-moz-selection, .cm-s-cobalt .CodeMirror-line > span::-moz-selection, .cm-s-cobalt .CodeMirror-line > span > span::-moz-selection { background: rgba(179, 101, 57, .99); } +.cm-s-cobalt .CodeMirror-gutters { background: #002240; border-right: 1px solid #aaa; } +.cm-s-cobalt .CodeMirror-guttermarker { color: #ffee80; } +.cm-s-cobalt .CodeMirror-guttermarker-subtle { color: #d0d0d0; } +.cm-s-cobalt .CodeMirror-linenumber { color: #d0d0d0; } +.cm-s-cobalt .CodeMirror-cursor { border-left: 1px solid white; } + +.cm-s-cobalt span.cm-comment { color: #08f; } +.cm-s-cobalt span.cm-atom { color: #845dc4; } +.cm-s-cobalt span.cm-number, .cm-s-cobalt span.cm-attribute { color: #ff80e1; } +.cm-s-cobalt span.cm-keyword { color: #ffee80; } +.cm-s-cobalt span.cm-string { color: #3ad900; } +.cm-s-cobalt span.cm-meta { color: #ff9d00; } +.cm-s-cobalt span.cm-variable-2, .cm-s-cobalt span.cm-tag { color: #9effff; } +.cm-s-cobalt span.cm-variable-3, .cm-s-cobalt span.cm-def, .cm-s-cobalt .cm-type { color: white; } +.cm-s-cobalt span.cm-bracket { color: #d8d8d8; } +.cm-s-cobalt span.cm-builtin, .cm-s-cobalt span.cm-special { color: #ff9e59; } +.cm-s-cobalt span.cm-link { color: #845dc4; } +.cm-s-cobalt span.cm-error { color: #9d1e15; } + +.cm-s-cobalt .CodeMirror-activeline-background { background: #002D57; } +.cm-s-cobalt .CodeMirror-matchingbracket { outline:1px solid grey;color:white !important; } diff --git a/docs/js/node_modules/codemirror/theme/colorforth.css b/docs/js/node_modules/codemirror/theme/colorforth.css new file mode 100644 index 000000000..19095e41d --- /dev/null +++ b/docs/js/node_modules/codemirror/theme/colorforth.css @@ -0,0 +1,33 @@ +.cm-s-colorforth.CodeMirror { background: #000000; color: #f8f8f8; } +.cm-s-colorforth .CodeMirror-gutters { background: #0a001f; border-right: 1px solid #aaa; } +.cm-s-colorforth .CodeMirror-guttermarker { color: #FFBD40; } +.cm-s-colorforth .CodeMirror-guttermarker-subtle { color: #78846f; } +.cm-s-colorforth .CodeMirror-linenumber { color: #bababa; } +.cm-s-colorforth .CodeMirror-cursor { border-left: 1px solid white; } + +.cm-s-colorforth span.cm-comment { color: #ededed; } +.cm-s-colorforth span.cm-def { color: #ff1c1c; font-weight:bold; } +.cm-s-colorforth span.cm-keyword { color: #ffd900; } +.cm-s-colorforth span.cm-builtin { color: #00d95a; } +.cm-s-colorforth span.cm-variable { color: #73ff00; } +.cm-s-colorforth span.cm-string { color: #007bff; } +.cm-s-colorforth span.cm-number { color: #00c4ff; } +.cm-s-colorforth span.cm-atom { color: #606060; } + +.cm-s-colorforth span.cm-variable-2 { color: #EEE; } +.cm-s-colorforth span.cm-variable-3, .cm-s-colorforth span.cm-type { color: #DDD; } +.cm-s-colorforth span.cm-property {} +.cm-s-colorforth span.cm-operator {} + +.cm-s-colorforth span.cm-meta { color: yellow; } +.cm-s-colorforth span.cm-qualifier { color: #FFF700; } +.cm-s-colorforth span.cm-bracket { color: #cc7; } +.cm-s-colorforth span.cm-tag { color: #FFBD40; } +.cm-s-colorforth span.cm-attribute { color: #FFF700; } +.cm-s-colorforth span.cm-error { color: #f00; } + +.cm-s-colorforth div.CodeMirror-selected { background: #333d53; } + +.cm-s-colorforth span.cm-compilation { background: rgba(255, 255, 255, 0.12); } + +.cm-s-colorforth .CodeMirror-activeline-background { background: #253540; } diff --git a/docs/js/node_modules/codemirror/theme/darcula.css b/docs/js/node_modules/codemirror/theme/darcula.css new file mode 100644 index 000000000..2ec81a355 --- /dev/null +++ b/docs/js/node_modules/codemirror/theme/darcula.css @@ -0,0 +1,53 @@ +/** + Name: IntelliJ IDEA darcula theme + From IntelliJ IDEA by JetBrains + */ + +.cm-s-darcula { font-family: Consolas, Menlo, Monaco, 'Lucida Console', 'Liberation Mono', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', 'Courier New', monospace, serif;} +.cm-s-darcula.CodeMirror { background: #2B2B2B; color: #A9B7C6; } + +.cm-s-darcula span.cm-meta { color: #BBB529; } +.cm-s-darcula span.cm-number { color: #6897BB; } +.cm-s-darcula span.cm-keyword { color: #CC7832; line-height: 1em; font-weight: bold; } +.cm-s-darcula span.cm-def { color: #A9B7C6; font-style: italic; } +.cm-s-darcula span.cm-variable { color: #A9B7C6; } +.cm-s-darcula span.cm-variable-2 { color: #A9B7C6; } +.cm-s-darcula span.cm-variable-3 { color: #9876AA; } +.cm-s-darcula span.cm-type { color: #AABBCC; font-weight: bold; } +.cm-s-darcula span.cm-property { color: #FFC66D; } +.cm-s-darcula span.cm-operator { color: #A9B7C6; } +.cm-s-darcula span.cm-string { color: #6A8759; } +.cm-s-darcula span.cm-string-2 { color: #6A8759; } +.cm-s-darcula span.cm-comment { color: #61A151; font-style: italic; } +.cm-s-darcula span.cm-link { color: #CC7832; } +.cm-s-darcula span.cm-atom { color: #CC7832; } +.cm-s-darcula span.cm-error { color: #BC3F3C; } +.cm-s-darcula span.cm-tag { color: #629755; font-weight: bold; font-style: italic; text-decoration: underline; } +.cm-s-darcula span.cm-attribute { color: #6897bb; } +.cm-s-darcula span.cm-qualifier { color: #6A8759; } +.cm-s-darcula span.cm-bracket { color: #A9B7C6; } +.cm-s-darcula span.cm-builtin { color: #FF9E59; } +.cm-s-darcula span.cm-special { color: #FF9E59; } +.cm-s-darcula span.cm-matchhighlight { color: #FFFFFF; background-color: rgba(50, 89, 48, .7); font-weight: normal;} +.cm-s-darcula span.cm-searching { color: #FFFFFF; background-color: rgba(61, 115, 59, .7); font-weight: normal;} + +.cm-s-darcula .CodeMirror-cursor { border-left: 1px solid #A9B7C6; } +.cm-s-darcula .CodeMirror-activeline-background { background: #323232; } +.cm-s-darcula .CodeMirror-gutters { background: #313335; border-right: 1px solid #313335; } +.cm-s-darcula .CodeMirror-guttermarker { color: #FFEE80; } +.cm-s-darcula .CodeMirror-guttermarker-subtle { color: #D0D0D0; } +.cm-s-darcula .CodeMirrir-linenumber { color: #606366; } +.cm-s-darcula .CodeMirror-matchingbracket { background-color: #3B514D; color: #FFEF28 !important; font-weight: bold; } + +.cm-s-darcula div.CodeMirror-selected { background: #214283; } + +.CodeMirror-hints.darcula { + font-family: Menlo, Monaco, Consolas, 'Courier New', monospace; + color: #9C9E9E; + background-color: #3B3E3F !important; +} + +.CodeMirror-hints.darcula .CodeMirror-hint-active { + background-color: #494D4E !important; + color: #9C9E9E !important; +} diff --git a/docs/js/node_modules/codemirror/theme/dracula.css b/docs/js/node_modules/codemirror/theme/dracula.css new file mode 100644 index 000000000..253133efe --- /dev/null +++ b/docs/js/node_modules/codemirror/theme/dracula.css @@ -0,0 +1,40 @@ +/* + + Name: dracula + Author: Michael Kaminsky (http://github.com/mkaminsky11) + + Original dracula color scheme by Zeno Rocha (https://github.com/zenorocha/dracula-theme) + +*/ + + +.cm-s-dracula.CodeMirror, .cm-s-dracula .CodeMirror-gutters { + background-color: #282a36 !important; + color: #f8f8f2 !important; + border: none; +} +.cm-s-dracula .CodeMirror-gutters { color: #282a36; } +.cm-s-dracula .CodeMirror-cursor { border-left: solid thin #f8f8f0; } +.cm-s-dracula .CodeMirror-linenumber { color: #6D8A88; } +.cm-s-dracula .CodeMirror-selected { background: rgba(255, 255, 255, 0.10); } +.cm-s-dracula .CodeMirror-line::selection, .cm-s-dracula .CodeMirror-line > span::selection, .cm-s-dracula .CodeMirror-line > span > span::selection { background: rgba(255, 255, 255, 0.10); } +.cm-s-dracula .CodeMirror-line::-moz-selection, .cm-s-dracula .CodeMirror-line > span::-moz-selection, .cm-s-dracula .CodeMirror-line > span > span::-moz-selection { background: rgba(255, 255, 255, 0.10); } +.cm-s-dracula span.cm-comment { color: #6272a4; } +.cm-s-dracula span.cm-string, .cm-s-dracula span.cm-string-2 { color: #f1fa8c; } +.cm-s-dracula span.cm-number { color: #bd93f9; } +.cm-s-dracula span.cm-variable { color: #50fa7b; } +.cm-s-dracula span.cm-variable-2 { color: white; } +.cm-s-dracula span.cm-def { color: #50fa7b; } +.cm-s-dracula span.cm-operator { color: #ff79c6; } +.cm-s-dracula span.cm-keyword { color: #ff79c6; } +.cm-s-dracula span.cm-atom { color: #bd93f9; } +.cm-s-dracula span.cm-meta { color: #f8f8f2; } +.cm-s-dracula span.cm-tag { color: #ff79c6; } +.cm-s-dracula span.cm-attribute { color: #50fa7b; } +.cm-s-dracula span.cm-qualifier { color: #50fa7b; } +.cm-s-dracula span.cm-property { color: #66d9ef; } +.cm-s-dracula span.cm-builtin { color: #50fa7b; } +.cm-s-dracula span.cm-variable-3, .cm-s-dracula span.cm-type { color: #ffb86c; } + +.cm-s-dracula .CodeMirror-activeline-background { background: rgba(255,255,255,0.1); } +.cm-s-dracula .CodeMirror-matchingbracket { text-decoration: underline; color: white !important; } diff --git a/docs/js/node_modules/codemirror/theme/duotone-dark.css b/docs/js/node_modules/codemirror/theme/duotone-dark.css new file mode 100644 index 000000000..88fdc76c8 --- /dev/null +++ b/docs/js/node_modules/codemirror/theme/duotone-dark.css @@ -0,0 +1,35 @@ +/* +Name: DuoTone-Dark +Author: by Bram de Haan, adapted from DuoTone themes by Simurai (http://simurai.com/projects/2016/01/01/duotone-themes) + +CodeMirror template by Jan T. Sott (https://github.com/idleberg), adapted by Bram de Haan (https://github.com/atelierbram/) +*/ + +.cm-s-duotone-dark.CodeMirror { background: #2a2734; color: #6c6783; } +.cm-s-duotone-dark div.CodeMirror-selected { background: #545167!important; } +.cm-s-duotone-dark .CodeMirror-gutters { background: #2a2734; border-right: 0px; } +.cm-s-duotone-dark .CodeMirror-linenumber { color: #545167; } + +/* begin cursor */ +.cm-s-duotone-dark .CodeMirror-cursor { border-left: 1px solid #ffad5c; /* border-left: 1px solid #ffad5c80; */ border-right: .5em solid #ffad5c; /* border-right: .5em solid #ffad5c80; */ opacity: .5; } +.cm-s-duotone-dark .CodeMirror-activeline-background { background: #363342; /* background: #36334280; */ opacity: .5;} +.cm-s-duotone-dark .cm-fat-cursor .CodeMirror-cursor { background: #ffad5c; /* background: #ffad5c80; */ opacity: .5;} +/* end cursor */ + +.cm-s-duotone-dark span.cm-atom, .cm-s-duotone-dark span.cm-number, .cm-s-duotone-dark span.cm-keyword, .cm-s-duotone-dark span.cm-variable, .cm-s-duotone-dark span.cm-attribute, .cm-s-duotone-dark span.cm-quote, .cm-s-duotone-dark span.cm-hr, .cm-s-duotone-dark span.cm-link { color: #ffcc99; } + +.cm-s-duotone-dark span.cm-property { color: #9a86fd; } +.cm-s-duotone-dark span.cm-punctuation, .cm-s-duotone-dark span.cm-unit, .cm-s-duotone-dark span.cm-negative { color: #e09142; } +.cm-s-duotone-dark span.cm-string { color: #ffb870; } +.cm-s-duotone-dark span.cm-operator { color: #ffad5c; } +.cm-s-duotone-dark span.cm-positive { color: #6a51e6; } + +.cm-s-duotone-dark span.cm-variable-2, .cm-s-duotone-dark span.cm-variable-3, .cm-s-duotone-dark span.cm-type, .cm-s-duotone-dark span.cm-string-2, .cm-s-duotone-dark span.cm-url { color: #7a63ee; } +.cm-s-duotone-dark span.cm-def, .cm-s-duotone-dark span.cm-tag, .cm-s-duotone-dark span.cm-builtin, .cm-s-duotone-dark span.cm-qualifier, .cm-s-duotone-dark span.cm-header, .cm-s-duotone-dark span.cm-em { color: #eeebff; } +.cm-s-duotone-dark span.cm-bracket, .cm-s-duotone-dark span.cm-comment { color: #6c6783; } + +/* using #f00 red for errors, don't think any of the colorscheme variables will stand out enough, ... maybe by giving it a background-color ... */ +.cm-s-duotone-dark span.cm-error, .cm-s-duotone-dark span.cm-invalidchar { color: #f00; } + +.cm-s-duotone-dark span.cm-header { font-weight: normal; } +.cm-s-duotone-dark .CodeMirror-matchingbracket { text-decoration: underline; color: #eeebff !important; } diff --git a/docs/js/node_modules/codemirror/theme/duotone-light.css b/docs/js/node_modules/codemirror/theme/duotone-light.css new file mode 100644 index 000000000..d99480f7c --- /dev/null +++ b/docs/js/node_modules/codemirror/theme/duotone-light.css @@ -0,0 +1,36 @@ +/* +Name: DuoTone-Light +Author: by Bram de Haan, adapted from DuoTone themes by Simurai (http://simurai.com/projects/2016/01/01/duotone-themes) + +CodeMirror template by Jan T. Sott (https://github.com/idleberg), adapted by Bram de Haan (https://github.com/atelierbram/) +*/ + +.cm-s-duotone-light.CodeMirror { background: #faf8f5; color: #b29762; } +.cm-s-duotone-light div.CodeMirror-selected { background: #e3dcce !important; } +.cm-s-duotone-light .CodeMirror-gutters { background: #faf8f5; border-right: 0px; } +.cm-s-duotone-light .CodeMirror-linenumber { color: #cdc4b1; } + +/* begin cursor */ +.cm-s-duotone-light .CodeMirror-cursor { border-left: 1px solid #93abdc; /* border-left: 1px solid #93abdc80; */ border-right: .5em solid #93abdc; /* border-right: .5em solid #93abdc80; */ opacity: .5; } +.cm-s-duotone-light .CodeMirror-activeline-background { background: #e3dcce; /* background: #e3dcce80; */ opacity: .5; } +.cm-s-duotone-light .cm-fat-cursor .CodeMirror-cursor { background: #93abdc; /* #93abdc80; */ opacity: .5; } +/* end cursor */ + +.cm-s-duotone-light span.cm-atom, .cm-s-duotone-light span.cm-number, .cm-s-duotone-light span.cm-keyword, .cm-s-duotone-light span.cm-variable, .cm-s-duotone-light span.cm-attribute, .cm-s-duotone-light span.cm-quote, .cm-s-duotone-light-light span.cm-hr, .cm-s-duotone-light-light span.cm-link { color: #063289; } + +.cm-s-duotone-light span.cm-property { color: #b29762; } +.cm-s-duotone-light span.cm-punctuation, .cm-s-duotone-light span.cm-unit, .cm-s-duotone-light span.cm-negative { color: #063289; } +.cm-s-duotone-light span.cm-string, .cm-s-duotone-light span.cm-operator { color: #1659df; } +.cm-s-duotone-light span.cm-positive { color: #896724; } + +.cm-s-duotone-light span.cm-variable-2, .cm-s-duotone-light span.cm-variable-3, .cm-s-duotone-light span.cm-type, .cm-s-duotone-light span.cm-string-2, .cm-s-duotone-light span.cm-url { color: #896724; } +.cm-s-duotone-light span.cm-def, .cm-s-duotone-light span.cm-tag, .cm-s-duotone-light span.cm-builtin, .cm-s-duotone-light span.cm-qualifier, .cm-s-duotone-light span.cm-header, .cm-s-duotone-light span.cm-em { color: #2d2006; } +.cm-s-duotone-light span.cm-bracket, .cm-s-duotone-light span.cm-comment { color: #b6ad9a; } + +/* using #f00 red for errors, don't think any of the colorscheme variables will stand out enough, ... maybe by giving it a background-color ... */ +/* .cm-s-duotone-light span.cm-error { background: #896724; color: #728fcb; } */ +.cm-s-duotone-light span.cm-error, .cm-s-duotone-light span.cm-invalidchar { color: #f00; } + +.cm-s-duotone-light span.cm-header { font-weight: normal; } +.cm-s-duotone-light .CodeMirror-matchingbracket { text-decoration: underline; color: #faf8f5 !important; } + diff --git a/docs/js/node_modules/codemirror/theme/eclipse.css b/docs/js/node_modules/codemirror/theme/eclipse.css new file mode 100644 index 000000000..800d603f6 --- /dev/null +++ b/docs/js/node_modules/codemirror/theme/eclipse.css @@ -0,0 +1,23 @@ +.cm-s-eclipse span.cm-meta { color: #FF1717; } +.cm-s-eclipse span.cm-keyword { line-height: 1em; font-weight: bold; color: #7F0055; } +.cm-s-eclipse span.cm-atom { color: #219; } +.cm-s-eclipse span.cm-number { color: #164; } +.cm-s-eclipse span.cm-def { color: #00f; } +.cm-s-eclipse span.cm-variable { color: black; } +.cm-s-eclipse span.cm-variable-2 { color: #0000C0; } +.cm-s-eclipse span.cm-variable-3, .cm-s-eclipse span.cm-type { color: #0000C0; } +.cm-s-eclipse span.cm-property { color: black; } +.cm-s-eclipse span.cm-operator { color: black; } +.cm-s-eclipse span.cm-comment { color: #3F7F5F; } +.cm-s-eclipse span.cm-string { color: #2A00FF; } +.cm-s-eclipse span.cm-string-2 { color: #f50; } +.cm-s-eclipse span.cm-qualifier { color: #555; } +.cm-s-eclipse span.cm-builtin { color: #30a; } +.cm-s-eclipse span.cm-bracket { color: #cc7; } +.cm-s-eclipse span.cm-tag { color: #170; } +.cm-s-eclipse span.cm-attribute { color: #00c; } +.cm-s-eclipse span.cm-link { color: #219; } +.cm-s-eclipse span.cm-error { color: #f00; } + +.cm-s-eclipse .CodeMirror-activeline-background { background: #e8f2ff; } +.cm-s-eclipse .CodeMirror-matchingbracket { outline:1px solid grey; color:black !important; } diff --git a/docs/js/node_modules/codemirror/theme/elegant.css b/docs/js/node_modules/codemirror/theme/elegant.css new file mode 100644 index 000000000..45b3ea655 --- /dev/null +++ b/docs/js/node_modules/codemirror/theme/elegant.css @@ -0,0 +1,13 @@ +.cm-s-elegant span.cm-number, .cm-s-elegant span.cm-string, .cm-s-elegant span.cm-atom { color: #762; } +.cm-s-elegant span.cm-comment { color: #262; font-style: italic; line-height: 1em; } +.cm-s-elegant span.cm-meta { color: #555; font-style: italic; line-height: 1em; } +.cm-s-elegant span.cm-variable { color: black; } +.cm-s-elegant span.cm-variable-2 { color: #b11; } +.cm-s-elegant span.cm-qualifier { color: #555; } +.cm-s-elegant span.cm-keyword { color: #730; } +.cm-s-elegant span.cm-builtin { color: #30a; } +.cm-s-elegant span.cm-link { color: #762; } +.cm-s-elegant span.cm-error { background-color: #fdd; } + +.cm-s-elegant .CodeMirror-activeline-background { background: #e8f2ff; } +.cm-s-elegant .CodeMirror-matchingbracket { outline:1px solid grey; color:black !important; } diff --git a/docs/js/node_modules/codemirror/theme/erlang-dark.css b/docs/js/node_modules/codemirror/theme/erlang-dark.css new file mode 100644 index 000000000..8c8a4171a --- /dev/null +++ b/docs/js/node_modules/codemirror/theme/erlang-dark.css @@ -0,0 +1,34 @@ +.cm-s-erlang-dark.CodeMirror { background: #002240; color: white; } +.cm-s-erlang-dark div.CodeMirror-selected { background: #b36539; } +.cm-s-erlang-dark .CodeMirror-line::selection, .cm-s-erlang-dark .CodeMirror-line > span::selection, .cm-s-erlang-dark .CodeMirror-line > span > span::selection { background: rgba(179, 101, 57, .99); } +.cm-s-erlang-dark .CodeMirror-line::-moz-selection, .cm-s-erlang-dark .CodeMirror-line > span::-moz-selection, .cm-s-erlang-dark .CodeMirror-line > span > span::-moz-selection { background: rgba(179, 101, 57, .99); } +.cm-s-erlang-dark .CodeMirror-gutters { background: #002240; border-right: 1px solid #aaa; } +.cm-s-erlang-dark .CodeMirror-guttermarker { color: white; } +.cm-s-erlang-dark .CodeMirror-guttermarker-subtle { color: #d0d0d0; } +.cm-s-erlang-dark .CodeMirror-linenumber { color: #d0d0d0; } +.cm-s-erlang-dark .CodeMirror-cursor { border-left: 1px solid white; } + +.cm-s-erlang-dark span.cm-quote { color: #ccc; } +.cm-s-erlang-dark span.cm-atom { color: #f133f1; } +.cm-s-erlang-dark span.cm-attribute { color: #ff80e1; } +.cm-s-erlang-dark span.cm-bracket { color: #ff9d00; } +.cm-s-erlang-dark span.cm-builtin { color: #eaa; } +.cm-s-erlang-dark span.cm-comment { color: #77f; } +.cm-s-erlang-dark span.cm-def { color: #e7a; } +.cm-s-erlang-dark span.cm-keyword { color: #ffee80; } +.cm-s-erlang-dark span.cm-meta { color: #50fefe; } +.cm-s-erlang-dark span.cm-number { color: #ffd0d0; } +.cm-s-erlang-dark span.cm-operator { color: #d55; } +.cm-s-erlang-dark span.cm-property { color: #ccc; } +.cm-s-erlang-dark span.cm-qualifier { color: #ccc; } +.cm-s-erlang-dark span.cm-special { color: #ffbbbb; } +.cm-s-erlang-dark span.cm-string { color: #3ad900; } +.cm-s-erlang-dark span.cm-string-2 { color: #ccc; } +.cm-s-erlang-dark span.cm-tag { color: #9effff; } +.cm-s-erlang-dark span.cm-variable { color: #50fe50; } +.cm-s-erlang-dark span.cm-variable-2 { color: #e0e; } +.cm-s-erlang-dark span.cm-variable-3, .cm-s-erlang-dark span.cm-type { color: #ccc; } +.cm-s-erlang-dark span.cm-error { color: #9d1e15; } + +.cm-s-erlang-dark .CodeMirror-activeline-background { background: #013461; } +.cm-s-erlang-dark .CodeMirror-matchingbracket { outline:1px solid grey; color:white !important; } diff --git a/docs/js/node_modules/codemirror/theme/gruvbox-dark.css b/docs/js/node_modules/codemirror/theme/gruvbox-dark.css new file mode 100644 index 000000000..ded215f57 --- /dev/null +++ b/docs/js/node_modules/codemirror/theme/gruvbox-dark.css @@ -0,0 +1,37 @@ +/* + + Name: gruvbox-dark + Author: kRkk (https://github.com/krkk) + + Original gruvbox color scheme by Pavel Pertsev (https://github.com/morhetz/gruvbox) + +*/ + +.cm-s-gruvbox-dark.CodeMirror, .cm-s-gruvbox-dark .CodeMirror-gutters { background-color: #282828; color: #bdae93; } +.cm-s-gruvbox-dark .CodeMirror-gutters {background: #282828; border-right: 0px;} +.cm-s-gruvbox-dark .CodeMirror-linenumber {color: #7c6f64;} +.cm-s-gruvbox-dark .CodeMirror-cursor { border-left: 1px solid #ebdbb2; } +.cm-s-gruvbox-dark div.CodeMirror-selected { background: #928374; } +.cm-s-gruvbox-dark span.cm-meta { color: #83a598; } + +.cm-s-gruvbox-dark span.cm-comment { color: #928374; } +.cm-s-gruvbox-dark span.cm-number, span.cm-atom { color: #d3869b; } +.cm-s-gruvbox-dark span.cm-keyword { color: #f84934; } + +.cm-s-gruvbox-dark span.cm-variable { color: #ebdbb2; } +.cm-s-gruvbox-dark span.cm-variable-2 { color: #ebdbb2; } +.cm-s-gruvbox-dark span.cm-variable-3, .cm-s-gruvbox-dark span.cm-type { color: #fabd2f; } +.cm-s-gruvbox-dark span.cm-operator { color: #ebdbb2; } +.cm-s-gruvbox-dark span.cm-callee { color: #ebdbb2; } +.cm-s-gruvbox-dark span.cm-def { color: #ebdbb2; } +.cm-s-gruvbox-dark span.cm-property { color: #ebdbb2; } +.cm-s-gruvbox-dark span.cm-string { color: #b8bb26; } +.cm-s-gruvbox-dark span.cm-string-2 { color: #8ec07c; } +.cm-s-gruvbox-dark span.cm-qualifier { color: #8ec07c; } +.cm-s-gruvbox-dark span.cm-attribute { color: #8ec07c; } + +.cm-s-gruvbox-dark .CodeMirror-activeline-background { background: #3c3836; } +.cm-s-gruvbox-dark .CodeMirror-matchingbracket { background: #928374; color:#282828 !important; } + +.cm-s-gruvbox-dark span.cm-builtin { color: #fe8019; } +.cm-s-gruvbox-dark span.cm-tag { color: #fe8019; } diff --git a/docs/js/node_modules/codemirror/theme/hopscotch.css b/docs/js/node_modules/codemirror/theme/hopscotch.css new file mode 100644 index 000000000..7d05431bd --- /dev/null +++ b/docs/js/node_modules/codemirror/theme/hopscotch.css @@ -0,0 +1,34 @@ +/* + + Name: Hopscotch + Author: Jan T. Sott + + CodeMirror template by Jan T. Sott (https://github.com/idleberg/base16-codemirror) + Original Base16 color scheme by Chris Kempson (https://github.com/chriskempson/base16) + +*/ + +.cm-s-hopscotch.CodeMirror {background: #322931; color: #d5d3d5;} +.cm-s-hopscotch div.CodeMirror-selected {background: #433b42 !important;} +.cm-s-hopscotch .CodeMirror-gutters {background: #322931; border-right: 0px;} +.cm-s-hopscotch .CodeMirror-linenumber {color: #797379;} +.cm-s-hopscotch .CodeMirror-cursor {border-left: 1px solid #989498 !important;} + +.cm-s-hopscotch span.cm-comment {color: #b33508;} +.cm-s-hopscotch span.cm-atom {color: #c85e7c;} +.cm-s-hopscotch span.cm-number {color: #c85e7c;} + +.cm-s-hopscotch span.cm-property, .cm-s-hopscotch span.cm-attribute {color: #8fc13e;} +.cm-s-hopscotch span.cm-keyword {color: #dd464c;} +.cm-s-hopscotch span.cm-string {color: #fdcc59;} + +.cm-s-hopscotch span.cm-variable {color: #8fc13e;} +.cm-s-hopscotch span.cm-variable-2 {color: #1290bf;} +.cm-s-hopscotch span.cm-def {color: #fd8b19;} +.cm-s-hopscotch span.cm-error {background: #dd464c; color: #989498;} +.cm-s-hopscotch span.cm-bracket {color: #d5d3d5;} +.cm-s-hopscotch span.cm-tag {color: #dd464c;} +.cm-s-hopscotch span.cm-link {color: #c85e7c;} + +.cm-s-hopscotch .CodeMirror-matchingbracket { text-decoration: underline; color: white !important;} +.cm-s-hopscotch .CodeMirror-activeline-background { background: #302020; } diff --git a/docs/js/node_modules/codemirror/theme/icecoder.css b/docs/js/node_modules/codemirror/theme/icecoder.css new file mode 100644 index 000000000..5440fbe27 --- /dev/null +++ b/docs/js/node_modules/codemirror/theme/icecoder.css @@ -0,0 +1,43 @@ +/* +ICEcoder default theme by Matt Pass, used in code editor available at https://icecoder.net +*/ + +.cm-s-icecoder { color: #666; background: #1d1d1b; } + +.cm-s-icecoder span.cm-keyword { color: #eee; font-weight:bold; } /* off-white 1 */ +.cm-s-icecoder span.cm-atom { color: #e1c76e; } /* yellow */ +.cm-s-icecoder span.cm-number { color: #6cb5d9; } /* blue */ +.cm-s-icecoder span.cm-def { color: #b9ca4a; } /* green */ + +.cm-s-icecoder span.cm-variable { color: #6cb5d9; } /* blue */ +.cm-s-icecoder span.cm-variable-2 { color: #cc1e5c; } /* pink */ +.cm-s-icecoder span.cm-variable-3, .cm-s-icecoder span.cm-type { color: #f9602c; } /* orange */ + +.cm-s-icecoder span.cm-property { color: #eee; } /* off-white 1 */ +.cm-s-icecoder span.cm-operator { color: #9179bb; } /* purple */ +.cm-s-icecoder span.cm-comment { color: #97a3aa; } /* grey-blue */ + +.cm-s-icecoder span.cm-string { color: #b9ca4a; } /* green */ +.cm-s-icecoder span.cm-string-2 { color: #6cb5d9; } /* blue */ + +.cm-s-icecoder span.cm-meta { color: #555; } /* grey */ + +.cm-s-icecoder span.cm-qualifier { color: #555; } /* grey */ +.cm-s-icecoder span.cm-builtin { color: #214e7b; } /* bright blue */ +.cm-s-icecoder span.cm-bracket { color: #cc7; } /* grey-yellow */ + +.cm-s-icecoder span.cm-tag { color: #e8e8e8; } /* off-white 2 */ +.cm-s-icecoder span.cm-attribute { color: #099; } /* teal */ + +.cm-s-icecoder span.cm-header { color: #6a0d6a; } /* purple-pink */ +.cm-s-icecoder span.cm-quote { color: #186718; } /* dark green */ +.cm-s-icecoder span.cm-hr { color: #888; } /* mid-grey */ +.cm-s-icecoder span.cm-link { color: #e1c76e; } /* yellow */ +.cm-s-icecoder span.cm-error { color: #d00; } /* red */ + +.cm-s-icecoder .CodeMirror-cursor { border-left: 1px solid white; } +.cm-s-icecoder div.CodeMirror-selected { color: #fff; background: #037; } +.cm-s-icecoder .CodeMirror-gutters { background: #1d1d1b; min-width: 41px; border-right: 0; } +.cm-s-icecoder .CodeMirror-linenumber { color: #555; cursor: default; } +.cm-s-icecoder .CodeMirror-matchingbracket { color: #fff !important; background: #555 !important; } +.cm-s-icecoder .CodeMirror-activeline-background { background: #000; } diff --git a/docs/js/node_modules/codemirror/theme/idea.css b/docs/js/node_modules/codemirror/theme/idea.css new file mode 100644 index 000000000..eab36717a --- /dev/null +++ b/docs/js/node_modules/codemirror/theme/idea.css @@ -0,0 +1,42 @@ +/** + Name: IDEA default theme + From IntelliJ IDEA by JetBrains + */ + +.cm-s-idea span.cm-meta { color: #808000; } +.cm-s-idea span.cm-number { color: #0000FF; } +.cm-s-idea span.cm-keyword { line-height: 1em; font-weight: bold; color: #000080; } +.cm-s-idea span.cm-atom { font-weight: bold; color: #000080; } +.cm-s-idea span.cm-def { color: #000000; } +.cm-s-idea span.cm-variable { color: black; } +.cm-s-idea span.cm-variable-2 { color: black; } +.cm-s-idea span.cm-variable-3, .cm-s-idea span.cm-type { color: black; } +.cm-s-idea span.cm-property { color: black; } +.cm-s-idea span.cm-operator { color: black; } +.cm-s-idea span.cm-comment { color: #808080; } +.cm-s-idea span.cm-string { color: #008000; } +.cm-s-idea span.cm-string-2 { color: #008000; } +.cm-s-idea span.cm-qualifier { color: #555; } +.cm-s-idea span.cm-error { color: #FF0000; } +.cm-s-idea span.cm-attribute { color: #0000FF; } +.cm-s-idea span.cm-tag { color: #000080; } +.cm-s-idea span.cm-link { color: #0000FF; } +.cm-s-idea .CodeMirror-activeline-background { background: #FFFAE3; } + +.cm-s-idea span.cm-builtin { color: #30a; } +.cm-s-idea span.cm-bracket { color: #cc7; } +.cm-s-idea { font-family: Consolas, Menlo, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono, Bitstream Vera Sans Mono, Courier New, monospace, serif;} + + +.cm-s-idea .CodeMirror-matchingbracket { outline:1px solid grey; color:black !important; } + +.CodeMirror-hints.idea { + font-family: Menlo, Monaco, Consolas, 'Courier New', monospace; + color: #616569; + background-color: #ebf3fd !important; +} + +.CodeMirror-hints.idea .CodeMirror-hint-active { + background-color: #a2b8c9 !important; + color: #5c6065 !important; +} \ No newline at end of file diff --git a/docs/js/node_modules/codemirror/theme/isotope.css b/docs/js/node_modules/codemirror/theme/isotope.css new file mode 100644 index 000000000..d0d6263cf --- /dev/null +++ b/docs/js/node_modules/codemirror/theme/isotope.css @@ -0,0 +1,34 @@ +/* + + Name: Isotope + Author: David Desandro / Jan T. Sott + + CodeMirror template by Jan T. Sott (https://github.com/idleberg/base16-codemirror) + Original Base16 color scheme by Chris Kempson (https://github.com/chriskempson/base16) + +*/ + +.cm-s-isotope.CodeMirror {background: #000000; color: #e0e0e0;} +.cm-s-isotope div.CodeMirror-selected {background: #404040 !important;} +.cm-s-isotope .CodeMirror-gutters {background: #000000; border-right: 0px;} +.cm-s-isotope .CodeMirror-linenumber {color: #808080;} +.cm-s-isotope .CodeMirror-cursor {border-left: 1px solid #c0c0c0 !important;} + +.cm-s-isotope span.cm-comment {color: #3300ff;} +.cm-s-isotope span.cm-atom {color: #cc00ff;} +.cm-s-isotope span.cm-number {color: #cc00ff;} + +.cm-s-isotope span.cm-property, .cm-s-isotope span.cm-attribute {color: #33ff00;} +.cm-s-isotope span.cm-keyword {color: #ff0000;} +.cm-s-isotope span.cm-string {color: #ff0099;} + +.cm-s-isotope span.cm-variable {color: #33ff00;} +.cm-s-isotope span.cm-variable-2 {color: #0066ff;} +.cm-s-isotope span.cm-def {color: #ff9900;} +.cm-s-isotope span.cm-error {background: #ff0000; color: #c0c0c0;} +.cm-s-isotope span.cm-bracket {color: #e0e0e0;} +.cm-s-isotope span.cm-tag {color: #ff0000;} +.cm-s-isotope span.cm-link {color: #cc00ff;} + +.cm-s-isotope .CodeMirror-matchingbracket { text-decoration: underline; color: white !important;} +.cm-s-isotope .CodeMirror-activeline-background { background: #202020; } diff --git a/docs/js/node_modules/codemirror/theme/lesser-dark.css b/docs/js/node_modules/codemirror/theme/lesser-dark.css new file mode 100644 index 000000000..f96bf430c --- /dev/null +++ b/docs/js/node_modules/codemirror/theme/lesser-dark.css @@ -0,0 +1,47 @@ +/* +http://lesscss.org/ dark theme +Ported to CodeMirror by Peter Kroon +*/ +.cm-s-lesser-dark { + line-height: 1.3em; +} +.cm-s-lesser-dark.CodeMirror { background: #262626; color: #EBEFE7; text-shadow: 0 -1px 1px #262626; } +.cm-s-lesser-dark div.CodeMirror-selected { background: #45443B; } /* 33322B*/ +.cm-s-lesser-dark .CodeMirror-line::selection, .cm-s-lesser-dark .CodeMirror-line > span::selection, .cm-s-lesser-dark .CodeMirror-line > span > span::selection { background: rgba(69, 68, 59, .99); } +.cm-s-lesser-dark .CodeMirror-line::-moz-selection, .cm-s-lesser-dark .CodeMirror-line > span::-moz-selection, .cm-s-lesser-dark .CodeMirror-line > span > span::-moz-selection { background: rgba(69, 68, 59, .99); } +.cm-s-lesser-dark .CodeMirror-cursor { border-left: 1px solid white; } +.cm-s-lesser-dark pre { padding: 0 8px; }/*editable code holder*/ + +.cm-s-lesser-dark.CodeMirror span.CodeMirror-matchingbracket { color: #7EFC7E; }/*65FC65*/ + +.cm-s-lesser-dark .CodeMirror-gutters { background: #262626; border-right:1px solid #aaa; } +.cm-s-lesser-dark .CodeMirror-guttermarker { color: #599eff; } +.cm-s-lesser-dark .CodeMirror-guttermarker-subtle { color: #777; } +.cm-s-lesser-dark .CodeMirror-linenumber { color: #777; } + +.cm-s-lesser-dark span.cm-header { color: #a0a; } +.cm-s-lesser-dark span.cm-quote { color: #090; } +.cm-s-lesser-dark span.cm-keyword { color: #599eff; } +.cm-s-lesser-dark span.cm-atom { color: #C2B470; } +.cm-s-lesser-dark span.cm-number { color: #B35E4D; } +.cm-s-lesser-dark span.cm-def { color: white; } +.cm-s-lesser-dark span.cm-variable { color:#D9BF8C; } +.cm-s-lesser-dark span.cm-variable-2 { color: #669199; } +.cm-s-lesser-dark span.cm-variable-3, .cm-s-lesser-dark span.cm-type { color: white; } +.cm-s-lesser-dark span.cm-property { color: #92A75C; } +.cm-s-lesser-dark span.cm-operator { color: #92A75C; } +.cm-s-lesser-dark span.cm-comment { color: #666; } +.cm-s-lesser-dark span.cm-string { color: #BCD279; } +.cm-s-lesser-dark span.cm-string-2 { color: #f50; } +.cm-s-lesser-dark span.cm-meta { color: #738C73; } +.cm-s-lesser-dark span.cm-qualifier { color: #555; } +.cm-s-lesser-dark span.cm-builtin { color: #ff9e59; } +.cm-s-lesser-dark span.cm-bracket { color: #EBEFE7; } +.cm-s-lesser-dark span.cm-tag { color: #669199; } +.cm-s-lesser-dark span.cm-attribute { color: #81a4d5; } +.cm-s-lesser-dark span.cm-hr { color: #999; } +.cm-s-lesser-dark span.cm-link { color: #7070E6; } +.cm-s-lesser-dark span.cm-error { color: #9d1e15; } + +.cm-s-lesser-dark .CodeMirror-activeline-background { background: #3C3A3A; } +.cm-s-lesser-dark .CodeMirror-matchingbracket { outline:1px solid grey; color:white !important; } diff --git a/docs/js/node_modules/codemirror/theme/liquibyte.css b/docs/js/node_modules/codemirror/theme/liquibyte.css new file mode 100644 index 000000000..393825e02 --- /dev/null +++ b/docs/js/node_modules/codemirror/theme/liquibyte.css @@ -0,0 +1,95 @@ +.cm-s-liquibyte.CodeMirror { + background-color: #000; + color: #fff; + line-height: 1.2em; + font-size: 1em; +} +.cm-s-liquibyte .CodeMirror-focused .cm-matchhighlight { + text-decoration: underline; + text-decoration-color: #0f0; + text-decoration-style: wavy; +} +.cm-s-liquibyte .cm-trailingspace { + text-decoration: line-through; + text-decoration-color: #f00; + text-decoration-style: dotted; +} +.cm-s-liquibyte .cm-tab { + text-decoration: line-through; + text-decoration-color: #404040; + text-decoration-style: dotted; +} +.cm-s-liquibyte .CodeMirror-gutters { background-color: #262626; border-right: 1px solid #505050; padding-right: 0.8em; } +.cm-s-liquibyte .CodeMirror-gutter-elt div { font-size: 1.2em; } +.cm-s-liquibyte .CodeMirror-guttermarker { } +.cm-s-liquibyte .CodeMirror-guttermarker-subtle { } +.cm-s-liquibyte .CodeMirror-linenumber { color: #606060; padding-left: 0; } +.cm-s-liquibyte .CodeMirror-cursor { border-left: 1px solid #eee; } + +.cm-s-liquibyte span.cm-comment { color: #008000; } +.cm-s-liquibyte span.cm-def { color: #ffaf40; font-weight: bold; } +.cm-s-liquibyte span.cm-keyword { color: #c080ff; font-weight: bold; } +.cm-s-liquibyte span.cm-builtin { color: #ffaf40; font-weight: bold; } +.cm-s-liquibyte span.cm-variable { color: #5967ff; font-weight: bold; } +.cm-s-liquibyte span.cm-string { color: #ff8000; } +.cm-s-liquibyte span.cm-number { color: #0f0; font-weight: bold; } +.cm-s-liquibyte span.cm-atom { color: #bf3030; font-weight: bold; } + +.cm-s-liquibyte span.cm-variable-2 { color: #007f7f; font-weight: bold; } +.cm-s-liquibyte span.cm-variable-3, .cm-s-liquibyte span.cm-type { color: #c080ff; font-weight: bold; } +.cm-s-liquibyte span.cm-property { color: #999; font-weight: bold; } +.cm-s-liquibyte span.cm-operator { color: #fff; } + +.cm-s-liquibyte span.cm-meta { color: #0f0; } +.cm-s-liquibyte span.cm-qualifier { color: #fff700; font-weight: bold; } +.cm-s-liquibyte span.cm-bracket { color: #cc7; } +.cm-s-liquibyte span.cm-tag { color: #ff0; font-weight: bold; } +.cm-s-liquibyte span.cm-attribute { color: #c080ff; font-weight: bold; } +.cm-s-liquibyte span.cm-error { color: #f00; } + +.cm-s-liquibyte div.CodeMirror-selected { background-color: rgba(255, 0, 0, 0.25); } + +.cm-s-liquibyte span.cm-compilation { background-color: rgba(255, 255, 255, 0.12); } + +.cm-s-liquibyte .CodeMirror-activeline-background { background-color: rgba(0, 255, 0, 0.15); } + +/* Default styles for common addons */ +.cm-s-liquibyte .CodeMirror span.CodeMirror-matchingbracket { color: #0f0; font-weight: bold; } +.cm-s-liquibyte .CodeMirror span.CodeMirror-nonmatchingbracket { color: #f00; font-weight: bold; } +.CodeMirror-matchingtag { background-color: rgba(150, 255, 0, .3); } +/* Scrollbars */ +/* Simple */ +.cm-s-liquibyte div.CodeMirror-simplescroll-horizontal div:hover, .cm-s-liquibyte div.CodeMirror-simplescroll-vertical div:hover { + background-color: rgba(80, 80, 80, .7); +} +.cm-s-liquibyte div.CodeMirror-simplescroll-horizontal div, .cm-s-liquibyte div.CodeMirror-simplescroll-vertical div { + background-color: rgba(80, 80, 80, .3); + border: 1px solid #404040; + border-radius: 5px; +} +.cm-s-liquibyte div.CodeMirror-simplescroll-vertical div { + border-top: 1px solid #404040; + border-bottom: 1px solid #404040; +} +.cm-s-liquibyte div.CodeMirror-simplescroll-horizontal div { + border-left: 1px solid #404040; + border-right: 1px solid #404040; +} +.cm-s-liquibyte div.CodeMirror-simplescroll-vertical { + background-color: #262626; +} +.cm-s-liquibyte div.CodeMirror-simplescroll-horizontal { + background-color: #262626; + border-top: 1px solid #404040; +} +/* Overlay */ +.cm-s-liquibyte div.CodeMirror-overlayscroll-horizontal div, div.CodeMirror-overlayscroll-vertical div { + background-color: #404040; + border-radius: 5px; +} +.cm-s-liquibyte div.CodeMirror-overlayscroll-vertical div { + border: 1px solid #404040; +} +.cm-s-liquibyte div.CodeMirror-overlayscroll-horizontal div { + border: 1px solid #404040; +} diff --git a/docs/js/node_modules/codemirror/theme/lucario.css b/docs/js/node_modules/codemirror/theme/lucario.css new file mode 100644 index 000000000..17a155103 --- /dev/null +++ b/docs/js/node_modules/codemirror/theme/lucario.css @@ -0,0 +1,37 @@ +/* + Name: lucario + Author: Raphael Amorim + + Original Lucario color scheme (https://github.com/raphamorim/lucario) +*/ + +.cm-s-lucario.CodeMirror, .cm-s-lucario .CodeMirror-gutters { + background-color: #2b3e50 !important; + color: #f8f8f2 !important; + border: none; +} +.cm-s-lucario .CodeMirror-gutters { color: #2b3e50; } +.cm-s-lucario .CodeMirror-cursor { border-left: solid thin #E6C845; } +.cm-s-lucario .CodeMirror-linenumber { color: #f8f8f2; } +.cm-s-lucario .CodeMirror-selected { background: #243443; } +.cm-s-lucario .CodeMirror-line::selection, .cm-s-lucario .CodeMirror-line > span::selection, .cm-s-lucario .CodeMirror-line > span > span::selection { background: #243443; } +.cm-s-lucario .CodeMirror-line::-moz-selection, .cm-s-lucario .CodeMirror-line > span::-moz-selection, .cm-s-lucario .CodeMirror-line > span > span::-moz-selection { background: #243443; } +.cm-s-lucario span.cm-comment { color: #5c98cd; } +.cm-s-lucario span.cm-string, .cm-s-lucario span.cm-string-2 { color: #E6DB74; } +.cm-s-lucario span.cm-number { color: #ca94ff; } +.cm-s-lucario span.cm-variable { color: #f8f8f2; } +.cm-s-lucario span.cm-variable-2 { color: #f8f8f2; } +.cm-s-lucario span.cm-def { color: #72C05D; } +.cm-s-lucario span.cm-operator { color: #66D9EF; } +.cm-s-lucario span.cm-keyword { color: #ff6541; } +.cm-s-lucario span.cm-atom { color: #bd93f9; } +.cm-s-lucario span.cm-meta { color: #f8f8f2; } +.cm-s-lucario span.cm-tag { color: #ff6541; } +.cm-s-lucario span.cm-attribute { color: #66D9EF; } +.cm-s-lucario span.cm-qualifier { color: #72C05D; } +.cm-s-lucario span.cm-property { color: #f8f8f2; } +.cm-s-lucario span.cm-builtin { color: #72C05D; } +.cm-s-lucario span.cm-variable-3, .cm-s-lucario span.cm-type { color: #ffb86c; } + +.cm-s-lucario .CodeMirror-activeline-background { background: #243443; } +.cm-s-lucario .CodeMirror-matchingbracket { text-decoration: underline; color: white !important; } diff --git a/docs/js/node_modules/codemirror/theme/material-darker.css b/docs/js/node_modules/codemirror/theme/material-darker.css new file mode 100644 index 000000000..45b64efb2 --- /dev/null +++ b/docs/js/node_modules/codemirror/theme/material-darker.css @@ -0,0 +1,135 @@ +/* + Name: material + Author: Mattia Astorino (http://github.com/equinusocio) + Website: https://material-theme.site/ +*/ + +.cm-s-material-darker.CodeMirror { + background-color: #212121; + color: #EEFFFF; +} + +.cm-s-material-darker .CodeMirror-gutters { + background: #212121; + color: #545454; + border: none; +} + +.cm-s-material-darker .CodeMirror-guttermarker, +.cm-s-material-darker .CodeMirror-guttermarker-subtle, +.cm-s-material-darker .CodeMirror-linenumber { + color: #545454; +} + +.cm-s-material-darker .CodeMirror-cursor { + border-left: 1px solid #FFCC00; +} + +.cm-s-material-darker div.CodeMirror-selected { + background: rgba(97, 97, 97, 0.2); +} + +.cm-s-material-darker.CodeMirror-focused div.CodeMirror-selected { + background: rgba(97, 97, 97, 0.2); +} + +.cm-s-material-darker .CodeMirror-line::selection, +.cm-s-material-darker .CodeMirror-line>span::selection, +.cm-s-material-darker .CodeMirror-line>span>span::selection { + background: rgba(128, 203, 196, 0.2); +} + +.cm-s-material-darker .CodeMirror-line::-moz-selection, +.cm-s-material-darker .CodeMirror-line>span::-moz-selection, +.cm-s-material-darker .CodeMirror-line>span>span::-moz-selection { + background: rgba(128, 203, 196, 0.2); +} + +.cm-s-material-darker .CodeMirror-activeline-background { + background: rgba(0, 0, 0, 0.5); +} + +.cm-s-material-darker .cm-keyword { + color: #C792EA; +} + +.cm-s-material-darker .cm-operator { + color: #89DDFF; +} + +.cm-s-material-darker .cm-variable-2 { + color: #EEFFFF; +} + +.cm-s-material-darker .cm-variable-3, +.cm-s-material-darker .cm-type { + color: #f07178; +} + +.cm-s-material-darker .cm-builtin { + color: #FFCB6B; +} + +.cm-s-material-darker .cm-atom { + color: #F78C6C; +} + +.cm-s-material-darker .cm-number { + color: #FF5370; +} + +.cm-s-material-darker .cm-def { + color: #82AAFF; +} + +.cm-s-material-darker .cm-string { + color: #C3E88D; +} + +.cm-s-material-darker .cm-string-2 { + color: #f07178; +} + +.cm-s-material-darker .cm-comment { + color: #545454; +} + +.cm-s-material-darker .cm-variable { + color: #f07178; +} + +.cm-s-material-darker .cm-tag { + color: #FF5370; +} + +.cm-s-material-darker .cm-meta { + color: #FFCB6B; +} + +.cm-s-material-darker .cm-attribute { + color: #C792EA; +} + +.cm-s-material-darker .cm-property { + color: #C792EA; +} + +.cm-s-material-darker .cm-qualifier { + color: #DECB6B; +} + +.cm-s-material-darker .cm-variable-3, +.cm-s-material-darker .cm-type { + color: #DECB6B; +} + + +.cm-s-material-darker .cm-error { + color: rgba(255, 255, 255, 1.0); + background-color: #FF5370; +} + +.cm-s-material-darker .CodeMirror-matchingbracket { + text-decoration: underline; + color: white !important; +} \ No newline at end of file diff --git a/docs/js/node_modules/codemirror/theme/material-ocean.css b/docs/js/node_modules/codemirror/theme/material-ocean.css new file mode 100644 index 000000000..86a6f3cd5 --- /dev/null +++ b/docs/js/node_modules/codemirror/theme/material-ocean.css @@ -0,0 +1,135 @@ +/* + Name: material + Author: Mattia Astorino (http://github.com/equinusocio) + Website: https://material-theme.site/ +*/ + +.cm-s-material-ocean.CodeMirror { + background-color: #0F111A; + color: #8F93A2; +} + +.cm-s-material-ocean .CodeMirror-gutters { + background: #0F111A; + color: #464B5D; + border: none; +} + +.cm-s-material-ocean .CodeMirror-guttermarker, +.cm-s-material-ocean .CodeMirror-guttermarker-subtle, +.cm-s-material-ocean .CodeMirror-linenumber { + color: #464B5D; +} + +.cm-s-material-ocean .CodeMirror-cursor { + border-left: 1px solid #FFCC00; +} + +.cm-s-material-ocean div.CodeMirror-selected { + background: rgba(113, 124, 180, 0.2); +} + +.cm-s-material-ocean.CodeMirror-focused div.CodeMirror-selected { + background: rgba(113, 124, 180, 0.2); +} + +.cm-s-material-ocean .CodeMirror-line::selection, +.cm-s-material-ocean .CodeMirror-line>span::selection, +.cm-s-material-ocean .CodeMirror-line>span>span::selection { + background: rgba(128, 203, 196, 0.2); +} + +.cm-s-material-ocean .CodeMirror-line::-moz-selection, +.cm-s-material-ocean .CodeMirror-line>span::-moz-selection, +.cm-s-material-ocean .CodeMirror-line>span>span::-moz-selection { + background: rgba(128, 203, 196, 0.2); +} + +.cm-s-material-ocean .CodeMirror-activeline-background { + background: rgba(0, 0, 0, 0.5); +} + +.cm-s-material-ocean .cm-keyword { + color: #C792EA; +} + +.cm-s-material-ocean .cm-operator { + color: #89DDFF; +} + +.cm-s-material-ocean .cm-variable-2 { + color: #EEFFFF; +} + +.cm-s-material-ocean .cm-variable-3, +.cm-s-material-ocean .cm-type { + color: #f07178; +} + +.cm-s-material-ocean .cm-builtin { + color: #FFCB6B; +} + +.cm-s-material-ocean .cm-atom { + color: #F78C6C; +} + +.cm-s-material-ocean .cm-number { + color: #FF5370; +} + +.cm-s-material-ocean .cm-def { + color: #82AAFF; +} + +.cm-s-material-ocean .cm-string { + color: #C3E88D; +} + +.cm-s-material-ocean .cm-string-2 { + color: #f07178; +} + +.cm-s-material-ocean .cm-comment { + color: #464B5D; +} + +.cm-s-material-ocean .cm-variable { + color: #f07178; +} + +.cm-s-material-ocean .cm-tag { + color: #FF5370; +} + +.cm-s-material-ocean .cm-meta { + color: #FFCB6B; +} + +.cm-s-material-ocean .cm-attribute { + color: #C792EA; +} + +.cm-s-material-ocean .cm-property { + color: #C792EA; +} + +.cm-s-material-ocean .cm-qualifier { + color: #DECB6B; +} + +.cm-s-material-ocean .cm-variable-3, +.cm-s-material-ocean .cm-type { + color: #DECB6B; +} + + +.cm-s-material-ocean .cm-error { + color: rgba(255, 255, 255, 1.0); + background-color: #FF5370; +} + +.cm-s-material-ocean .CodeMirror-matchingbracket { + text-decoration: underline; + color: white !important; +} \ No newline at end of file diff --git a/docs/js/node_modules/codemirror/theme/material-palenight.css b/docs/js/node_modules/codemirror/theme/material-palenight.css new file mode 100644 index 000000000..66d53dd39 --- /dev/null +++ b/docs/js/node_modules/codemirror/theme/material-palenight.css @@ -0,0 +1,135 @@ +/* + Name: material + Author: Mattia Astorino (http://github.com/equinusocio) + Website: https://material-theme.site/ +*/ + +.cm-s-material-palenight.CodeMirror { + background-color: #292D3E; + color: #A6ACCD; +} + +.cm-s-material-palenight .CodeMirror-gutters { + background: #292D3E; + color: #676E95; + border: none; +} + +.cm-s-material-palenight .CodeMirror-guttermarker, +.cm-s-material-palenight .CodeMirror-guttermarker-subtle, +.cm-s-material-palenight .CodeMirror-linenumber { + color: #676E95; +} + +.cm-s-material-palenight .CodeMirror-cursor { + border-left: 1px solid #FFCC00; +} + +.cm-s-material-palenight div.CodeMirror-selected { + background: rgba(113, 124, 180, 0.2); +} + +.cm-s-material-palenight.CodeMirror-focused div.CodeMirror-selected { + background: rgba(113, 124, 180, 0.2); +} + +.cm-s-material-palenight .CodeMirror-line::selection, +.cm-s-material-palenight .CodeMirror-line>span::selection, +.cm-s-material-palenight .CodeMirror-line>span>span::selection { + background: rgba(128, 203, 196, 0.2); +} + +.cm-s-material-palenight .CodeMirror-line::-moz-selection, +.cm-s-material-palenight .CodeMirror-line>span::-moz-selection, +.cm-s-material-palenight .CodeMirror-line>span>span::-moz-selection { + background: rgba(128, 203, 196, 0.2); +} + +.cm-s-material-palenight .CodeMirror-activeline-background { + background: rgba(0, 0, 0, 0.5); +} + +.cm-s-material-palenight .cm-keyword { + color: #C792EA; +} + +.cm-s-material-palenight .cm-operator { + color: #89DDFF; +} + +.cm-s-material-palenight .cm-variable-2 { + color: #EEFFFF; +} + +.cm-s-material-palenight .cm-variable-3, +.cm-s-material-palenight .cm-type { + color: #f07178; +} + +.cm-s-material-palenight .cm-builtin { + color: #FFCB6B; +} + +.cm-s-material-palenight .cm-atom { + color: #F78C6C; +} + +.cm-s-material-palenight .cm-number { + color: #FF5370; +} + +.cm-s-material-palenight .cm-def { + color: #82AAFF; +} + +.cm-s-material-palenight .cm-string { + color: #C3E88D; +} + +.cm-s-material-palenight .cm-string-2 { + color: #f07178; +} + +.cm-s-material-palenight .cm-comment { + color: #676E95; +} + +.cm-s-material-palenight .cm-variable { + color: #f07178; +} + +.cm-s-material-palenight .cm-tag { + color: #FF5370; +} + +.cm-s-material-palenight .cm-meta { + color: #FFCB6B; +} + +.cm-s-material-palenight .cm-attribute { + color: #C792EA; +} + +.cm-s-material-palenight .cm-property { + color: #C792EA; +} + +.cm-s-material-palenight .cm-qualifier { + color: #DECB6B; +} + +.cm-s-material-palenight .cm-variable-3, +.cm-s-material-palenight .cm-type { + color: #DECB6B; +} + + +.cm-s-material-palenight .cm-error { + color: rgba(255, 255, 255, 1.0); + background-color: #FF5370; +} + +.cm-s-material-palenight .CodeMirror-matchingbracket { + text-decoration: underline; + color: white !important; +} \ No newline at end of file diff --git a/docs/js/node_modules/codemirror/theme/material.css b/docs/js/node_modules/codemirror/theme/material.css new file mode 100644 index 000000000..9ac17a367 --- /dev/null +++ b/docs/js/node_modules/codemirror/theme/material.css @@ -0,0 +1,135 @@ +/* + Name: material + Author: Mattia Astorino (http://github.com/equinusocio) + Website: https://material-theme.site/ +*/ + +.cm-s-material.CodeMirror { + background-color: #263238; + color: #EEFFFF; +} + +.cm-s-material .CodeMirror-gutters { + background: #263238; + color: #546E7A; + border: none; +} + +.cm-s-material .CodeMirror-guttermarker, +.cm-s-material .CodeMirror-guttermarker-subtle, +.cm-s-material .CodeMirror-linenumber { + color: #546E7A; +} + +.cm-s-material .CodeMirror-cursor { + border-left: 1px solid #FFCC00; +} + +.cm-s-material div.CodeMirror-selected { + background: rgba(128, 203, 196, 0.2); +} + +.cm-s-material.CodeMirror-focused div.CodeMirror-selected { + background: rgba(128, 203, 196, 0.2); +} + +.cm-s-material .CodeMirror-line::selection, +.cm-s-material .CodeMirror-line>span::selection, +.cm-s-material .CodeMirror-line>span>span::selection { + background: rgba(128, 203, 196, 0.2); +} + +.cm-s-material .CodeMirror-line::-moz-selection, +.cm-s-material .CodeMirror-line>span::-moz-selection, +.cm-s-material .CodeMirror-line>span>span::-moz-selection { + background: rgba(128, 203, 196, 0.2); +} + +.cm-s-material .CodeMirror-activeline-background { + background: rgba(0, 0, 0, 0.5); +} + +.cm-s-material .cm-keyword { + color: #C792EA; +} + +.cm-s-material .cm-operator { + color: #89DDFF; +} + +.cm-s-material .cm-variable-2 { + color: #EEFFFF; +} + +.cm-s-material .cm-variable-3, +.cm-s-material .cm-type { + color: #f07178; +} + +.cm-s-material .cm-builtin { + color: #FFCB6B; +} + +.cm-s-material .cm-atom { + color: #F78C6C; +} + +.cm-s-material .cm-number { + color: #FF5370; +} + +.cm-s-material .cm-def { + color: #82AAFF; +} + +.cm-s-material .cm-string { + color: #C3E88D; +} + +.cm-s-material .cm-string-2 { + color: #f07178; +} + +.cm-s-material .cm-comment { + color: #546E7A; +} + +.cm-s-material .cm-variable { + color: #f07178; +} + +.cm-s-material .cm-tag { + color: #FF5370; +} + +.cm-s-material .cm-meta { + color: #FFCB6B; +} + +.cm-s-material .cm-attribute { + color: #C792EA; +} + +.cm-s-material .cm-property { + color: #C792EA; +} + +.cm-s-material .cm-qualifier { + color: #DECB6B; +} + +.cm-s-material .cm-variable-3, +.cm-s-material .cm-type { + color: #DECB6B; +} + + +.cm-s-material .cm-error { + color: rgba(255, 255, 255, 1.0); + background-color: #FF5370; +} + +.cm-s-material .CodeMirror-matchingbracket { + text-decoration: underline; + color: white !important; +} \ No newline at end of file diff --git a/docs/js/node_modules/codemirror/theme/mbo.css b/docs/js/node_modules/codemirror/theme/mbo.css new file mode 100644 index 000000000..e164fcf42 --- /dev/null +++ b/docs/js/node_modules/codemirror/theme/mbo.css @@ -0,0 +1,37 @@ +/****************************************************************/ +/* Based on mbonaci's Brackets mbo theme */ +/* https://github.com/mbonaci/global/blob/master/Mbo.tmTheme */ +/* Create your own: http://tmtheme-editor.herokuapp.com */ +/****************************************************************/ + +.cm-s-mbo.CodeMirror { background: #2c2c2c; color: #ffffec; } +.cm-s-mbo div.CodeMirror-selected { background: #716C62; } +.cm-s-mbo .CodeMirror-line::selection, .cm-s-mbo .CodeMirror-line > span::selection, .cm-s-mbo .CodeMirror-line > span > span::selection { background: rgba(113, 108, 98, .99); } +.cm-s-mbo .CodeMirror-line::-moz-selection, .cm-s-mbo .CodeMirror-line > span::-moz-selection, .cm-s-mbo .CodeMirror-line > span > span::-moz-selection { background: rgba(113, 108, 98, .99); } +.cm-s-mbo .CodeMirror-gutters { background: #4e4e4e; border-right: 0px; } +.cm-s-mbo .CodeMirror-guttermarker { color: white; } +.cm-s-mbo .CodeMirror-guttermarker-subtle { color: grey; } +.cm-s-mbo .CodeMirror-linenumber { color: #dadada; } +.cm-s-mbo .CodeMirror-cursor { border-left: 1px solid #ffffec; } + +.cm-s-mbo span.cm-comment { color: #95958a; } +.cm-s-mbo span.cm-atom { color: #00a8c6; } +.cm-s-mbo span.cm-number { color: #00a8c6; } + +.cm-s-mbo span.cm-property, .cm-s-mbo span.cm-attribute { color: #9ddfe9; } +.cm-s-mbo span.cm-keyword { color: #ffb928; } +.cm-s-mbo span.cm-string { color: #ffcf6c; } +.cm-s-mbo span.cm-string.cm-property { color: #ffffec; } + +.cm-s-mbo span.cm-variable { color: #ffffec; } +.cm-s-mbo span.cm-variable-2 { color: #00a8c6; } +.cm-s-mbo span.cm-def { color: #ffffec; } +.cm-s-mbo span.cm-bracket { color: #fffffc; font-weight: bold; } +.cm-s-mbo span.cm-tag { color: #9ddfe9; } +.cm-s-mbo span.cm-link { color: #f54b07; } +.cm-s-mbo span.cm-error { border-bottom: #636363; color: #ffffec; } +.cm-s-mbo span.cm-qualifier { color: #ffffec; } + +.cm-s-mbo .CodeMirror-activeline-background { background: #494b41; } +.cm-s-mbo .CodeMirror-matchingbracket { color: #ffb928 !important; } +.cm-s-mbo .CodeMirror-matchingtag { background: rgba(255, 255, 255, .37); } diff --git a/docs/js/node_modules/codemirror/theme/mdn-like.css b/docs/js/node_modules/codemirror/theme/mdn-like.css new file mode 100644 index 000000000..622ed3efb --- /dev/null +++ b/docs/js/node_modules/codemirror/theme/mdn-like.css @@ -0,0 +1,46 @@ +/* + MDN-LIKE Theme - Mozilla + Ported to CodeMirror by Peter Kroon + Report bugs/issues here: https://github.com/codemirror/CodeMirror/issues + GitHub: @peterkroon + + The mdn-like theme is inspired on the displayed code examples at: https://developer.mozilla.org/en-US/docs/Web/CSS/animation + +*/ +.cm-s-mdn-like.CodeMirror { color: #999; background-color: #fff; } +.cm-s-mdn-like div.CodeMirror-selected { background: #cfc; } +.cm-s-mdn-like .CodeMirror-line::selection, .cm-s-mdn-like .CodeMirror-line > span::selection, .cm-s-mdn-like .CodeMirror-line > span > span::selection { background: #cfc; } +.cm-s-mdn-like .CodeMirror-line::-moz-selection, .cm-s-mdn-like .CodeMirror-line > span::-moz-selection, .cm-s-mdn-like .CodeMirror-line > span > span::-moz-selection { background: #cfc; } + +.cm-s-mdn-like .CodeMirror-gutters { background: #f8f8f8; border-left: 6px solid rgba(0,83,159,0.65); color: #333; } +.cm-s-mdn-like .CodeMirror-linenumber { color: #aaa; padding-left: 8px; } +.cm-s-mdn-like .CodeMirror-cursor { border-left: 2px solid #222; } + +.cm-s-mdn-like .cm-keyword { color: #6262FF; } +.cm-s-mdn-like .cm-atom { color: #F90; } +.cm-s-mdn-like .cm-number { color: #ca7841; } +.cm-s-mdn-like .cm-def { color: #8DA6CE; } +.cm-s-mdn-like span.cm-variable-2, .cm-s-mdn-like span.cm-tag { color: #690; } +.cm-s-mdn-like span.cm-variable-3, .cm-s-mdn-like span.cm-def, .cm-s-mdn-like span.cm-type { color: #07a; } + +.cm-s-mdn-like .cm-variable { color: #07a; } +.cm-s-mdn-like .cm-property { color: #905; } +.cm-s-mdn-like .cm-qualifier { color: #690; } + +.cm-s-mdn-like .cm-operator { color: #cda869; } +.cm-s-mdn-like .cm-comment { color:#777; font-weight:normal; } +.cm-s-mdn-like .cm-string { color:#07a; font-style:italic; } +.cm-s-mdn-like .cm-string-2 { color:#bd6b18; } /*?*/ +.cm-s-mdn-like .cm-meta { color: #000; } /*?*/ +.cm-s-mdn-like .cm-builtin { color: #9B7536; } /*?*/ +.cm-s-mdn-like .cm-tag { color: #997643; } +.cm-s-mdn-like .cm-attribute { color: #d6bb6d; } /*?*/ +.cm-s-mdn-like .cm-header { color: #FF6400; } +.cm-s-mdn-like .cm-hr { color: #AEAEAE; } +.cm-s-mdn-like .cm-link { color:#ad9361; font-style:italic; text-decoration:none; } +.cm-s-mdn-like .cm-error { border-bottom: 1px solid red; } + +div.cm-s-mdn-like .CodeMirror-activeline-background { background: #efefff; } +div.cm-s-mdn-like span.CodeMirror-matchingbracket { outline:1px solid grey; color: inherit; } + +.cm-s-mdn-like.CodeMirror { background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFcAAAAyCAYAAAAp8UeFAAAHvklEQVR42s2b63bcNgyEQZCSHCdt2vd/0tWF7I+Q6XgMXiTtuvU5Pl57ZQKkKHzEAOtF5KeIJBGJ8uvL599FRFREZhFx8DeXv8trn68RuGaC8TRfo3SNp9dlDDHedyLyTUTeRWStXKPZrjtpZxaRw5hPqozRs1N8/enzIiQRWcCgy4MUA0f+XWliDhyL8Lfyvx7ei/Ae3iQFHyw7U/59pQVIMEEPEz0G7XiwdRjzSfC3UTtz9vchIntxvry5iMgfIhJoEflOz2CQr3F5h/HfeFe+GTdLaKcu9L8LTeQb/R/7GgbsfKedyNdoHsN31uRPWrfZ5wsj/NzzRQHuToIdU3ahwnsKPxXCjJITuOsi7XLc7SG/v5GdALs7wf8JjTFiB5+QvTEfRyGOfX3Lrx8wxyQi3sNq46O7QahQiCsRFgqddjBouVEHOKDgXAQHD9gJCr5sMKkEdjwsarG/ww3BMHBU7OBjXnzdyY7SfCxf5/z6ATccrwlKuwC/jhznnPF4CgVzhhVf4xp2EixcBActO75iZ8/fM9zAs2OMzKdslgXWJ9XG8PQoOAMA5fGcsvORgv0doBXyHrCwfLJAOwo71QLNkb8n2Pl6EWiR7OCibtkPaz4Kc/0NNAze2gju3zOwekALDaCFPI5vjPFmgGY5AZqyGEvH1x7QfIb8YtxMnA/b+QQ0aQDAwc6JMFg8CbQZ4qoYEEHbRwNojuK3EHwd7VALSgq+MNDKzfT58T8qdpADrgW0GmgcAS1lhzztJmkAzcPNOQbsWEALBDSlMKUG0Eq4CLAQWvEVQ9WU57gZJwZtgPO3r9oBTQ9WO8TjqXINx8R0EYpiZEUWOF3FxkbJkgU9B2f41YBrIj5ZfsQa0M5kTgiAAqM3ShXLgu8XMqcrQBvJ0CL5pnTsfMB13oB8athpAq2XOQmcGmoACCLydx7nToa23ATaSIY2ichfOdPTGxlasXMLaL0MLZAOwAKIM+y8CmicobGdCcbbK9DzN+yYGVoNNI5iUKTMyYOjPse4A8SM1MmcXgU0toOq1yO/v8FOxlASyc7TgeYaAMBJHcY1CcCwGI/TK4AmDbDyKYBBtFUkRwto8gygiQEaByFgJ00BH2M8JWwQS1nafDXQCidWyOI8AcjDCSjCLk8ngObuAm3JAHAdubAmOaK06V8MNEsKPJOhobSprwQa6gD7DclRQdqcwL4zxqgBrQcabUiBLclRDKAlWp+etPkBaNMA0AKlrHwTdEByZAA4GM+SNluSY6wAzcMNewxmgig5Ks0nkrSpBvSaQHMdKTBAnLojOdYyGpQ254602ZILPdTD1hdlggdIm74jbTp8vDwF5ZYUeLWGJpWsh6XNyXgcYwVoJQTEhhTYkxzZjiU5npU2TaB979TQehlaAVq4kaGpiPwwwLkYUuBbQwocyQTv1tA0+1UFWoJF3iv1oq+qoSk8EQdJmwHkziIF7oOZk14EGitibAdjLYYK78H5vZOhtWpoI0ATGHs0Q8OMb4Ey+2bU2UYztCtA0wFAs7TplGLRVQCcqaFdGSPCeTI1QNIC52iWNzof6Uib7xjEp07mNNoUYmVosVItHrHzRlLgBn9LFyRHaQCtVUMbtTNhoXWiTOO9k/V8BdAc1Oq0ArSQs6/5SU0hckNy9NnXqQY0PGYo5dWJ7nINaN6o958FWin27aBaWRka1r5myvLOAm0j30eBJqCxHLReVclxhxOEN2JfDWjxBtAC7MIH1fVaGdoOp4qJYDgKtKPSFNID2gSnGldrCqkFZ+5UeQXQBIRrSwocbdZYQT/2LwRahBPBXoHrB8nxaGROST62DKUbQOMMzZIC9abkuELfQzQALWTnDNAm8KHWFOJgJ5+SHIvTPcmx1xQyZRhNL5Qci689aXMEaN/uNIWkEwDAvFpOZmgsBaaGnbs1NPa1Jm32gBZAIh1pCtG7TSH4aE0y1uVY4uqoFPisGlpP2rSA5qTecWn5agK6BzSpgAyD+wFaqhnYoSZ1Vwr8CmlTQbrcO3ZaX0NAEyMbYaAlyquFoLKK3SPby9CeVUPThrSJmkCAE0CrKUQadi4DrdSlWhmah0YL9z9vClH59YGbHx1J8VZTyAjQepJjmXwAKTDQI3omc3p1U4gDUf6RfcdYfrUp5ClAi2J3Ba6UOXGo+K+bQrjjssitG2SJzshaLwMtXgRagUNpYYoVkMSBLM+9GGiJZMvduG6DRZ4qc04DMPtQQxOjEtACmhO7K1AbNbQDEggZyJwscFpAGwENhoBeUwh3bWolhe8BTYVKxQEWrSUn/uhcM5KhvUu/+eQu0Lzhi+VrK0PrZZNDQKs9cpYUuFYgMVpD4/NxenJTiMCNqdUEUf1qZWjppLT5qSkkUZbCwkbZMSuVnu80hfSkzRbQeqCZSAh6huR4VtoM2gHAlLf72smuWgE+VV7XpE25Ab2WFDgyhnSuKbs4GuGzCjR+tIoUuMFg3kgcWKLTwRqanJQ2W00hAsenfaApRC42hbCvK1SlE0HtE9BGgneJO+ELamitD1YjjOYnNYVcraGhtKkW0EqVVeDx733I2NH581k1NNxNLG0i0IJ8/NjVaOZ0tYZ2Vtr0Xv7tPV3hkWp9EFkgS/J0vosngTaSoaG06WHi+xObQkaAdlbanP8B2+2l0f90LmUAAAAASUVORK5CYII=); } diff --git a/docs/js/node_modules/codemirror/theme/midnight.css b/docs/js/node_modules/codemirror/theme/midnight.css new file mode 100644 index 000000000..fc26474a4 --- /dev/null +++ b/docs/js/node_modules/codemirror/theme/midnight.css @@ -0,0 +1,39 @@ +/* Based on the theme at http://bonsaiden.github.com/JavaScript-Garden */ + +/**/ +.cm-s-midnight .CodeMirror-activeline-background { background: #253540; } + +.cm-s-midnight.CodeMirror { + background: #0F192A; + color: #D1EDFF; +} + +.cm-s-midnight div.CodeMirror-selected { background: #314D67; } +.cm-s-midnight .CodeMirror-line::selection, .cm-s-midnight .CodeMirror-line > span::selection, .cm-s-midnight .CodeMirror-line > span > span::selection { background: rgba(49, 77, 103, .99); } +.cm-s-midnight .CodeMirror-line::-moz-selection, .cm-s-midnight .CodeMirror-line > span::-moz-selection, .cm-s-midnight .CodeMirror-line > span > span::-moz-selection { background: rgba(49, 77, 103, .99); } +.cm-s-midnight .CodeMirror-gutters { background: #0F192A; border-right: 1px solid; } +.cm-s-midnight .CodeMirror-guttermarker { color: white; } +.cm-s-midnight .CodeMirror-guttermarker-subtle { color: #d0d0d0; } +.cm-s-midnight .CodeMirror-linenumber { color: #D0D0D0; } +.cm-s-midnight .CodeMirror-cursor { border-left: 1px solid #F8F8F0; } + +.cm-s-midnight span.cm-comment { color: #428BDD; } +.cm-s-midnight span.cm-atom { color: #AE81FF; } +.cm-s-midnight span.cm-number { color: #D1EDFF; } + +.cm-s-midnight span.cm-property, .cm-s-midnight span.cm-attribute { color: #A6E22E; } +.cm-s-midnight span.cm-keyword { color: #E83737; } +.cm-s-midnight span.cm-string { color: #1DC116; } + +.cm-s-midnight span.cm-variable { color: #FFAA3E; } +.cm-s-midnight span.cm-variable-2 { color: #FFAA3E; } +.cm-s-midnight span.cm-def { color: #4DD; } +.cm-s-midnight span.cm-bracket { color: #D1EDFF; } +.cm-s-midnight span.cm-tag { color: #449; } +.cm-s-midnight span.cm-link { color: #AE81FF; } +.cm-s-midnight span.cm-error { background: #F92672; color: #F8F8F0; } + +.cm-s-midnight .CodeMirror-matchingbracket { + text-decoration: underline; + color: white !important; +} diff --git a/docs/js/node_modules/codemirror/theme/monokai.css b/docs/js/node_modules/codemirror/theme/monokai.css new file mode 100644 index 000000000..cd4cd5572 --- /dev/null +++ b/docs/js/node_modules/codemirror/theme/monokai.css @@ -0,0 +1,41 @@ +/* Based on Sublime Text's Monokai theme */ + +.cm-s-monokai.CodeMirror { background: #272822; color: #f8f8f2; } +.cm-s-monokai div.CodeMirror-selected { background: #49483E; } +.cm-s-monokai .CodeMirror-line::selection, .cm-s-monokai .CodeMirror-line > span::selection, .cm-s-monokai .CodeMirror-line > span > span::selection { background: rgba(73, 72, 62, .99); } +.cm-s-monokai .CodeMirror-line::-moz-selection, .cm-s-monokai .CodeMirror-line > span::-moz-selection, .cm-s-monokai .CodeMirror-line > span > span::-moz-selection { background: rgba(73, 72, 62, .99); } +.cm-s-monokai .CodeMirror-gutters { background: #272822; border-right: 0px; } +.cm-s-monokai .CodeMirror-guttermarker { color: white; } +.cm-s-monokai .CodeMirror-guttermarker-subtle { color: #d0d0d0; } +.cm-s-monokai .CodeMirror-linenumber { color: #d0d0d0; } +.cm-s-monokai .CodeMirror-cursor { border-left: 1px solid #f8f8f0; } + +.cm-s-monokai span.cm-comment { color: #75715e; } +.cm-s-monokai span.cm-atom { color: #ae81ff; } +.cm-s-monokai span.cm-number { color: #ae81ff; } + +.cm-s-monokai span.cm-comment.cm-attribute { color: #97b757; } +.cm-s-monokai span.cm-comment.cm-def { color: #bc9262; } +.cm-s-monokai span.cm-comment.cm-tag { color: #bc6283; } +.cm-s-monokai span.cm-comment.cm-type { color: #5998a6; } + +.cm-s-monokai span.cm-property, .cm-s-monokai span.cm-attribute { color: #a6e22e; } +.cm-s-monokai span.cm-keyword { color: #f92672; } +.cm-s-monokai span.cm-builtin { color: #66d9ef; } +.cm-s-monokai span.cm-string { color: #e6db74; } + +.cm-s-monokai span.cm-variable { color: #f8f8f2; } +.cm-s-monokai span.cm-variable-2 { color: #9effff; } +.cm-s-monokai span.cm-variable-3, .cm-s-monokai span.cm-type { color: #66d9ef; } +.cm-s-monokai span.cm-def { color: #fd971f; } +.cm-s-monokai span.cm-bracket { color: #f8f8f2; } +.cm-s-monokai span.cm-tag { color: #f92672; } +.cm-s-monokai span.cm-header { color: #ae81ff; } +.cm-s-monokai span.cm-link { color: #ae81ff; } +.cm-s-monokai span.cm-error { background: #f92672; color: #f8f8f0; } + +.cm-s-monokai .CodeMirror-activeline-background { background: #373831; } +.cm-s-monokai .CodeMirror-matchingbracket { + text-decoration: underline; + color: white !important; +} diff --git a/docs/js/node_modules/codemirror/theme/moxer.css b/docs/js/node_modules/codemirror/theme/moxer.css new file mode 100644 index 000000000..b3ca35e38 --- /dev/null +++ b/docs/js/node_modules/codemirror/theme/moxer.css @@ -0,0 +1,143 @@ +/* + Name: Moxer Theme + Author: Mattia Astorino (http://github.com/equinusocio) + Website: https://github.com/moxer-theme/moxer-code +*/ + +.cm-s-moxer.CodeMirror { + background-color: #090A0F; + color: #8E95B4; + line-height: 1.8; +} + +.cm-s-moxer .CodeMirror-gutters { + background: #090A0F; + color: #35394B; + border: none; +} + +.cm-s-moxer .CodeMirror-guttermarker, +.cm-s-moxer .CodeMirror-guttermarker-subtle, +.cm-s-moxer .CodeMirror-linenumber { + color: #35394B; +} + + +.cm-s-moxer .CodeMirror-cursor { + border-left: 1px solid #FFCC00; +} + +.cm-s-moxer div.CodeMirror-selected { + background: rgba(128, 203, 196, 0.2); +} + +.cm-s-moxer.CodeMirror-focused div.CodeMirror-selected { + background: #212431; +} + +.cm-s-moxer .CodeMirror-line::selection, +.cm-s-moxer .CodeMirror-line>span::selection, +.cm-s-moxer .CodeMirror-line>span>span::selection { + background: #212431; +} + +.cm-s-moxer .CodeMirror-line::-moz-selection, +.cm-s-moxer .CodeMirror-line>span::-moz-selection, +.cm-s-moxer .CodeMirror-line>span>span::-moz-selection { + background: #212431; +} + +.cm-s-moxer .CodeMirror-activeline-background, +.cm-s-moxer .CodeMirror-activeline-gutter .CodeMirror-linenumber { + background: rgba(33, 36, 49, 0.5); +} + +.cm-s-moxer .cm-keyword { + color: #D46C6C; +} + +.cm-s-moxer .cm-operator { + color: #D46C6C; +} + +.cm-s-moxer .cm-variable-2 { + color: #81C5DA; +} + + +.cm-s-moxer .cm-variable-3, +.cm-s-moxer .cm-type { + color: #f07178; +} + +.cm-s-moxer .cm-builtin { + color: #FFCB6B; +} + +.cm-s-moxer .cm-atom { + color: #A99BE2; +} + +.cm-s-moxer .cm-number { + color: #7CA4C0; +} + +.cm-s-moxer .cm-def { + color: #F5DFA5; +} + +.cm-s-moxer .CodeMirror-line .cm-def ~ .cm-def { + color: #81C5DA; +} + +.cm-s-moxer .cm-string { + color: #B2E4AE; +} + +.cm-s-moxer .cm-string-2 { + color: #f07178; +} + +.cm-s-moxer .cm-comment { + color: #3F445A; +} + +.cm-s-moxer .cm-variable { + color: #8E95B4; +} + +.cm-s-moxer .cm-tag { + color: #FF5370; +} + +.cm-s-moxer .cm-meta { + color: #FFCB6B; +} + +.cm-s-moxer .cm-attribute { + color: #C792EA; +} + +.cm-s-moxer .cm-property { + color: #81C5DA; +} + +.cm-s-moxer .cm-qualifier { + color: #DECB6B; +} + +.cm-s-moxer .cm-variable-3, +.cm-s-moxer .cm-type { + color: #DECB6B; +} + + +.cm-s-moxer .cm-error { + color: rgba(255, 255, 255, 1.0); + background-color: #FF5370; +} + +.cm-s-moxer .CodeMirror-matchingbracket { + text-decoration: underline; + color: white !important; +} \ No newline at end of file diff --git a/docs/js/node_modules/codemirror/theme/neat.css b/docs/js/node_modules/codemirror/theme/neat.css new file mode 100644 index 000000000..4267b1a37 --- /dev/null +++ b/docs/js/node_modules/codemirror/theme/neat.css @@ -0,0 +1,12 @@ +.cm-s-neat span.cm-comment { color: #a86; } +.cm-s-neat span.cm-keyword { line-height: 1em; font-weight: bold; color: blue; } +.cm-s-neat span.cm-string { color: #a22; } +.cm-s-neat span.cm-builtin { line-height: 1em; font-weight: bold; color: #077; } +.cm-s-neat span.cm-special { line-height: 1em; font-weight: bold; color: #0aa; } +.cm-s-neat span.cm-variable { color: black; } +.cm-s-neat span.cm-number, .cm-s-neat span.cm-atom { color: #3a3; } +.cm-s-neat span.cm-meta { color: #555; } +.cm-s-neat span.cm-link { color: #3a3; } + +.cm-s-neat .CodeMirror-activeline-background { background: #e8f2ff; } +.cm-s-neat .CodeMirror-matchingbracket { outline:1px solid grey; color:black !important; } diff --git a/docs/js/node_modules/codemirror/theme/neo.css b/docs/js/node_modules/codemirror/theme/neo.css new file mode 100644 index 000000000..b28d5c65f --- /dev/null +++ b/docs/js/node_modules/codemirror/theme/neo.css @@ -0,0 +1,43 @@ +/* neo theme for codemirror */ + +/* Color scheme */ + +.cm-s-neo.CodeMirror { + background-color:#ffffff; + color:#2e383c; + line-height:1.4375; +} +.cm-s-neo .cm-comment { color:#75787b; } +.cm-s-neo .cm-keyword, .cm-s-neo .cm-property { color:#1d75b3; } +.cm-s-neo .cm-atom,.cm-s-neo .cm-number { color:#75438a; } +.cm-s-neo .cm-node,.cm-s-neo .cm-tag { color:#9c3328; } +.cm-s-neo .cm-string { color:#b35e14; } +.cm-s-neo .cm-variable,.cm-s-neo .cm-qualifier { color:#047d65; } + + +/* Editor styling */ + +.cm-s-neo pre { + padding:0; +} + +.cm-s-neo .CodeMirror-gutters { + border:none; + border-right:10px solid transparent; + background-color:transparent; +} + +.cm-s-neo .CodeMirror-linenumber { + padding:0; + color:#e0e2e5; +} + +.cm-s-neo .CodeMirror-guttermarker { color: #1d75b3; } +.cm-s-neo .CodeMirror-guttermarker-subtle { color: #e0e2e5; } + +.cm-s-neo .CodeMirror-cursor { + width: auto; + border: 0; + background: rgba(155,157,162,0.37); + z-index: 1; +} diff --git a/docs/js/node_modules/codemirror/theme/night.css b/docs/js/node_modules/codemirror/theme/night.css new file mode 100644 index 000000000..f631bf42c --- /dev/null +++ b/docs/js/node_modules/codemirror/theme/night.css @@ -0,0 +1,27 @@ +/* Loosely based on the Midnight Textmate theme */ + +.cm-s-night.CodeMirror { background: #0a001f; color: #f8f8f8; } +.cm-s-night div.CodeMirror-selected { background: #447; } +.cm-s-night .CodeMirror-line::selection, .cm-s-night .CodeMirror-line > span::selection, .cm-s-night .CodeMirror-line > span > span::selection { background: rgba(68, 68, 119, .99); } +.cm-s-night .CodeMirror-line::-moz-selection, .cm-s-night .CodeMirror-line > span::-moz-selection, .cm-s-night .CodeMirror-line > span > span::-moz-selection { background: rgba(68, 68, 119, .99); } +.cm-s-night .CodeMirror-gutters { background: #0a001f; border-right: 1px solid #aaa; } +.cm-s-night .CodeMirror-guttermarker { color: white; } +.cm-s-night .CodeMirror-guttermarker-subtle { color: #bbb; } +.cm-s-night .CodeMirror-linenumber { color: #f8f8f8; } +.cm-s-night .CodeMirror-cursor { border-left: 1px solid white; } + +.cm-s-night span.cm-comment { color: #8900d1; } +.cm-s-night span.cm-atom { color: #845dc4; } +.cm-s-night span.cm-number, .cm-s-night span.cm-attribute { color: #ffd500; } +.cm-s-night span.cm-keyword { color: #599eff; } +.cm-s-night span.cm-string { color: #37f14a; } +.cm-s-night span.cm-meta { color: #7678e2; } +.cm-s-night span.cm-variable-2, .cm-s-night span.cm-tag { color: #99b2ff; } +.cm-s-night span.cm-variable-3, .cm-s-night span.cm-def, .cm-s-night span.cm-type { color: white; } +.cm-s-night span.cm-bracket { color: #8da6ce; } +.cm-s-night span.cm-builtin, .cm-s-night span.cm-special { color: #ff9e59; } +.cm-s-night span.cm-link { color: #845dc4; } +.cm-s-night span.cm-error { color: #9d1e15; } + +.cm-s-night .CodeMirror-activeline-background { background: #1C005A; } +.cm-s-night .CodeMirror-matchingbracket { outline:1px solid grey; color:white !important; } diff --git a/docs/js/node_modules/codemirror/theme/nord.css b/docs/js/node_modules/codemirror/theme/nord.css new file mode 100644 index 000000000..41a8ad778 --- /dev/null +++ b/docs/js/node_modules/codemirror/theme/nord.css @@ -0,0 +1,42 @@ +/* Based on arcticicestudio's Nord theme */ +/* https://github.com/arcticicestudio/nord */ + +.cm-s-nord.CodeMirror { background: #2e3440; color: #d8dee9; } +.cm-s-nord div.CodeMirror-selected { background: #434c5e; } +.cm-s-nord .CodeMirror-line::selection, .cm-s-nord .CodeMirror-line > span::selection, .cm-s-nord .CodeMirror-line > span > span::selection { background: #3b4252; } +.cm-s-nord .CodeMirror-line::-moz-selection, .cm-s-nord .CodeMirror-line > span::-moz-selection, .cm-s-nord .CodeMirror-line > span > span::-moz-selection { background: #3b4252; } +.cm-s-nord .CodeMirror-gutters { background: #2e3440; border-right: 0px; } +.cm-s-nord .CodeMirror-guttermarker { color: #4c566a; } +.cm-s-nord .CodeMirror-guttermarker-subtle { color: #4c566a; } +.cm-s-nord .CodeMirror-linenumber { color: #4c566a; } +.cm-s-nord .CodeMirror-cursor { border-left: 1px solid #f8f8f0; } + +.cm-s-nord span.cm-comment { color: #4c566a; } +.cm-s-nord span.cm-atom { color: #b48ead; } +.cm-s-nord span.cm-number { color: #b48ead; } + +.cm-s-nord span.cm-comment.cm-attribute { color: #97b757; } +.cm-s-nord span.cm-comment.cm-def { color: #bc9262; } +.cm-s-nord span.cm-comment.cm-tag { color: #bc6283; } +.cm-s-nord span.cm-comment.cm-type { color: #5998a6; } + +.cm-s-nord span.cm-property, .cm-s-nord span.cm-attribute { color: #8FBCBB; } +.cm-s-nord span.cm-keyword { color: #81A1C1; } +.cm-s-nord span.cm-builtin { color: #81A1C1; } +.cm-s-nord span.cm-string { color: #A3BE8C; } + +.cm-s-nord span.cm-variable { color: #d8dee9; } +.cm-s-nord span.cm-variable-2 { color: #d8dee9; } +.cm-s-nord span.cm-variable-3, .cm-s-nord span.cm-type { color: #d8dee9; } +.cm-s-nord span.cm-def { color: #8FBCBB; } +.cm-s-nord span.cm-bracket { color: #81A1C1; } +.cm-s-nord span.cm-tag { color: #bf616a; } +.cm-s-nord span.cm-header { color: #b48ead; } +.cm-s-nord span.cm-link { color: #b48ead; } +.cm-s-nord span.cm-error { background: #bf616a; color: #f8f8f0; } + +.cm-s-nord .CodeMirror-activeline-background { background: #3b4252; } +.cm-s-nord .CodeMirror-matchingbracket { + text-decoration: underline; + color: white !important; +} diff --git a/docs/js/node_modules/codemirror/theme/oceanic-next.css b/docs/js/node_modules/codemirror/theme/oceanic-next.css new file mode 100644 index 000000000..296277ba0 --- /dev/null +++ b/docs/js/node_modules/codemirror/theme/oceanic-next.css @@ -0,0 +1,44 @@ +/* + + Name: oceanic-next + Author: Filype Pereira (https://github.com/fpereira1) + + Original oceanic-next color scheme by Dmitri Voronianski (https://github.com/voronianski/oceanic-next-color-scheme) + +*/ + +.cm-s-oceanic-next.CodeMirror { background: #304148; color: #f8f8f2; } +.cm-s-oceanic-next div.CodeMirror-selected { background: rgba(101, 115, 126, 0.33); } +.cm-s-oceanic-next .CodeMirror-line::selection, .cm-s-oceanic-next .CodeMirror-line > span::selection, .cm-s-oceanic-next .CodeMirror-line > span > span::selection { background: rgba(101, 115, 126, 0.33); } +.cm-s-oceanic-next .CodeMirror-line::-moz-selection, .cm-s-oceanic-next .CodeMirror-line > span::-moz-selection, .cm-s-oceanic-next .CodeMirror-line > span > span::-moz-selection { background: rgba(101, 115, 126, 0.33); } +.cm-s-oceanic-next .CodeMirror-gutters { background: #304148; border-right: 10px; } +.cm-s-oceanic-next .CodeMirror-guttermarker { color: white; } +.cm-s-oceanic-next .CodeMirror-guttermarker-subtle { color: #d0d0d0; } +.cm-s-oceanic-next .CodeMirror-linenumber { color: #d0d0d0; } +.cm-s-oceanic-next .CodeMirror-cursor { border-left: 1px solid #f8f8f0; } + +.cm-s-oceanic-next span.cm-comment { color: #65737E; } +.cm-s-oceanic-next span.cm-atom { color: #C594C5; } +.cm-s-oceanic-next span.cm-number { color: #F99157; } + +.cm-s-oceanic-next span.cm-property { color: #99C794; } +.cm-s-oceanic-next span.cm-attribute, +.cm-s-oceanic-next span.cm-keyword { color: #C594C5; } +.cm-s-oceanic-next span.cm-builtin { color: #66d9ef; } +.cm-s-oceanic-next span.cm-string { color: #99C794; } + +.cm-s-oceanic-next span.cm-variable, +.cm-s-oceanic-next span.cm-variable-2, +.cm-s-oceanic-next span.cm-variable-3 { color: #f8f8f2; } +.cm-s-oceanic-next span.cm-def { color: #6699CC; } +.cm-s-oceanic-next span.cm-bracket { color: #5FB3B3; } +.cm-s-oceanic-next span.cm-tag { color: #C594C5; } +.cm-s-oceanic-next span.cm-header { color: #C594C5; } +.cm-s-oceanic-next span.cm-link { color: #C594C5; } +.cm-s-oceanic-next span.cm-error { background: #C594C5; color: #f8f8f0; } + +.cm-s-oceanic-next .CodeMirror-activeline-background { background: rgba(101, 115, 126, 0.33); } +.cm-s-oceanic-next .CodeMirror-matchingbracket { + text-decoration: underline; + color: white !important; +} diff --git a/docs/js/node_modules/codemirror/theme/panda-syntax.css b/docs/js/node_modules/codemirror/theme/panda-syntax.css new file mode 100644 index 000000000..de14e9112 --- /dev/null +++ b/docs/js/node_modules/codemirror/theme/panda-syntax.css @@ -0,0 +1,85 @@ +/* + Name: Panda Syntax + Author: Siamak Mokhtari (http://github.com/siamak/) + CodeMirror template by Siamak Mokhtari (https://github.com/siamak/atom-panda-syntax) +*/ +.cm-s-panda-syntax { + background: #292A2B; + color: #E6E6E6; + line-height: 1.5; + font-family: 'Operator Mono', 'Source Code Pro', Menlo, Monaco, Consolas, Courier New, monospace; +} +.cm-s-panda-syntax .CodeMirror-cursor { border-color: #ff2c6d; } +.cm-s-panda-syntax .CodeMirror-activeline-background { + background: rgba(99, 123, 156, 0.1); +} +.cm-s-panda-syntax .CodeMirror-selected { + background: #FFF; +} +.cm-s-panda-syntax .cm-comment { + font-style: italic; + color: #676B79; +} +.cm-s-panda-syntax .cm-operator { + color: #f3f3f3; +} +.cm-s-panda-syntax .cm-string { + color: #19F9D8; +} +.cm-s-panda-syntax .cm-string-2 { + color: #FFB86C; +} + +.cm-s-panda-syntax .cm-tag { + color: #ff2c6d; +} +.cm-s-panda-syntax .cm-meta { + color: #b084eb; +} + +.cm-s-panda-syntax .cm-number { + color: #FFB86C; +} +.cm-s-panda-syntax .cm-atom { + color: #ff2c6d; +} +.cm-s-panda-syntax .cm-keyword { + color: #FF75B5; +} +.cm-s-panda-syntax .cm-variable { + color: #ffb86c; +} +.cm-s-panda-syntax .cm-variable-2 { + color: #ff9ac1; +} +.cm-s-panda-syntax .cm-variable-3, .cm-s-panda-syntax .cm-type { + color: #ff9ac1; +} + +.cm-s-panda-syntax .cm-def { + color: #e6e6e6; +} +.cm-s-panda-syntax .cm-property { + color: #f3f3f3; +} +.cm-s-panda-syntax .cm-unit { + color: #ffb86c; +} + +.cm-s-panda-syntax .cm-attribute { + color: #ffb86c; +} + +.cm-s-panda-syntax .CodeMirror-matchingbracket { + border-bottom: 1px dotted #19F9D8; + padding-bottom: 2px; + color: #e6e6e6; +} +.cm-s-panda-syntax .CodeMirror-gutters { + background: #292a2b; + border-right-color: rgba(255, 255, 255, 0.1); +} +.cm-s-panda-syntax .CodeMirror-linenumber { + color: #e6e6e6; + opacity: 0.6; +} diff --git a/docs/js/node_modules/codemirror/theme/paraiso-dark.css b/docs/js/node_modules/codemirror/theme/paraiso-dark.css new file mode 100644 index 000000000..aa9d207e6 --- /dev/null +++ b/docs/js/node_modules/codemirror/theme/paraiso-dark.css @@ -0,0 +1,38 @@ +/* + + Name: Paraíso (Dark) + Author: Jan T. Sott + + Color scheme by Jan T. Sott (https://github.com/idleberg/Paraiso-CodeMirror) + Inspired by the art of Rubens LP (http://www.rubenslp.com.br) + +*/ + +.cm-s-paraiso-dark.CodeMirror { background: #2f1e2e; color: #b9b6b0; } +.cm-s-paraiso-dark div.CodeMirror-selected { background: #41323f; } +.cm-s-paraiso-dark .CodeMirror-line::selection, .cm-s-paraiso-dark .CodeMirror-line > span::selection, .cm-s-paraiso-dark .CodeMirror-line > span > span::selection { background: rgba(65, 50, 63, .99); } +.cm-s-paraiso-dark .CodeMirror-line::-moz-selection, .cm-s-paraiso-dark .CodeMirror-line > span::-moz-selection, .cm-s-paraiso-dark .CodeMirror-line > span > span::-moz-selection { background: rgba(65, 50, 63, .99); } +.cm-s-paraiso-dark .CodeMirror-gutters { background: #2f1e2e; border-right: 0px; } +.cm-s-paraiso-dark .CodeMirror-guttermarker { color: #ef6155; } +.cm-s-paraiso-dark .CodeMirror-guttermarker-subtle { color: #776e71; } +.cm-s-paraiso-dark .CodeMirror-linenumber { color: #776e71; } +.cm-s-paraiso-dark .CodeMirror-cursor { border-left: 1px solid #8d8687; } + +.cm-s-paraiso-dark span.cm-comment { color: #e96ba8; } +.cm-s-paraiso-dark span.cm-atom { color: #815ba4; } +.cm-s-paraiso-dark span.cm-number { color: #815ba4; } + +.cm-s-paraiso-dark span.cm-property, .cm-s-paraiso-dark span.cm-attribute { color: #48b685; } +.cm-s-paraiso-dark span.cm-keyword { color: #ef6155; } +.cm-s-paraiso-dark span.cm-string { color: #fec418; } + +.cm-s-paraiso-dark span.cm-variable { color: #48b685; } +.cm-s-paraiso-dark span.cm-variable-2 { color: #06b6ef; } +.cm-s-paraiso-dark span.cm-def { color: #f99b15; } +.cm-s-paraiso-dark span.cm-bracket { color: #b9b6b0; } +.cm-s-paraiso-dark span.cm-tag { color: #ef6155; } +.cm-s-paraiso-dark span.cm-link { color: #815ba4; } +.cm-s-paraiso-dark span.cm-error { background: #ef6155; color: #8d8687; } + +.cm-s-paraiso-dark .CodeMirror-activeline-background { background: #4D344A; } +.cm-s-paraiso-dark .CodeMirror-matchingbracket { text-decoration: underline; color: white !important; } diff --git a/docs/js/node_modules/codemirror/theme/paraiso-light.css b/docs/js/node_modules/codemirror/theme/paraiso-light.css new file mode 100644 index 000000000..ae0c755f8 --- /dev/null +++ b/docs/js/node_modules/codemirror/theme/paraiso-light.css @@ -0,0 +1,38 @@ +/* + + Name: Paraíso (Light) + Author: Jan T. Sott + + Color scheme by Jan T. Sott (https://github.com/idleberg/Paraiso-CodeMirror) + Inspired by the art of Rubens LP (http://www.rubenslp.com.br) + +*/ + +.cm-s-paraiso-light.CodeMirror { background: #e7e9db; color: #41323f; } +.cm-s-paraiso-light div.CodeMirror-selected { background: #b9b6b0; } +.cm-s-paraiso-light .CodeMirror-line::selection, .cm-s-paraiso-light .CodeMirror-line > span::selection, .cm-s-paraiso-light .CodeMirror-line > span > span::selection { background: #b9b6b0; } +.cm-s-paraiso-light .CodeMirror-line::-moz-selection, .cm-s-paraiso-light .CodeMirror-line > span::-moz-selection, .cm-s-paraiso-light .CodeMirror-line > span > span::-moz-selection { background: #b9b6b0; } +.cm-s-paraiso-light .CodeMirror-gutters { background: #e7e9db; border-right: 0px; } +.cm-s-paraiso-light .CodeMirror-guttermarker { color: black; } +.cm-s-paraiso-light .CodeMirror-guttermarker-subtle { color: #8d8687; } +.cm-s-paraiso-light .CodeMirror-linenumber { color: #8d8687; } +.cm-s-paraiso-light .CodeMirror-cursor { border-left: 1px solid #776e71; } + +.cm-s-paraiso-light span.cm-comment { color: #e96ba8; } +.cm-s-paraiso-light span.cm-atom { color: #815ba4; } +.cm-s-paraiso-light span.cm-number { color: #815ba4; } + +.cm-s-paraiso-light span.cm-property, .cm-s-paraiso-light span.cm-attribute { color: #48b685; } +.cm-s-paraiso-light span.cm-keyword { color: #ef6155; } +.cm-s-paraiso-light span.cm-string { color: #fec418; } + +.cm-s-paraiso-light span.cm-variable { color: #48b685; } +.cm-s-paraiso-light span.cm-variable-2 { color: #06b6ef; } +.cm-s-paraiso-light span.cm-def { color: #f99b15; } +.cm-s-paraiso-light span.cm-bracket { color: #41323f; } +.cm-s-paraiso-light span.cm-tag { color: #ef6155; } +.cm-s-paraiso-light span.cm-link { color: #815ba4; } +.cm-s-paraiso-light span.cm-error { background: #ef6155; color: #776e71; } + +.cm-s-paraiso-light .CodeMirror-activeline-background { background: #CFD1C4; } +.cm-s-paraiso-light .CodeMirror-matchingbracket { text-decoration: underline; color: white !important; } diff --git a/docs/js/node_modules/codemirror/theme/pastel-on-dark.css b/docs/js/node_modules/codemirror/theme/pastel-on-dark.css new file mode 100644 index 000000000..60435dd15 --- /dev/null +++ b/docs/js/node_modules/codemirror/theme/pastel-on-dark.css @@ -0,0 +1,52 @@ +/** + * Pastel On Dark theme ported from ACE editor + * @license MIT + * @copyright AtomicPages LLC 2014 + * @author Dennis Thompson, AtomicPages LLC + * @version 1.1 + * @source https://github.com/atomicpages/codemirror-pastel-on-dark-theme + */ + +.cm-s-pastel-on-dark.CodeMirror { + background: #2c2827; + color: #8F938F; + line-height: 1.5; +} +.cm-s-pastel-on-dark div.CodeMirror-selected { background: rgba(221,240,255,0.2); } +.cm-s-pastel-on-dark .CodeMirror-line::selection, .cm-s-pastel-on-dark .CodeMirror-line > span::selection, .cm-s-pastel-on-dark .CodeMirror-line > span > span::selection { background: rgba(221,240,255,0.2); } +.cm-s-pastel-on-dark .CodeMirror-line::-moz-selection, .cm-s-pastel-on-dark .CodeMirror-line > span::-moz-selection, .cm-s-pastel-on-dark .CodeMirror-line > span > span::-moz-selection { background: rgba(221,240,255,0.2); } + +.cm-s-pastel-on-dark .CodeMirror-gutters { + background: #34302f; + border-right: 0px; + padding: 0 3px; +} +.cm-s-pastel-on-dark .CodeMirror-guttermarker { color: white; } +.cm-s-pastel-on-dark .CodeMirror-guttermarker-subtle { color: #8F938F; } +.cm-s-pastel-on-dark .CodeMirror-linenumber { color: #8F938F; } +.cm-s-pastel-on-dark .CodeMirror-cursor { border-left: 1px solid #A7A7A7; } +.cm-s-pastel-on-dark span.cm-comment { color: #A6C6FF; } +.cm-s-pastel-on-dark span.cm-atom { color: #DE8E30; } +.cm-s-pastel-on-dark span.cm-number { color: #CCCCCC; } +.cm-s-pastel-on-dark span.cm-property { color: #8F938F; } +.cm-s-pastel-on-dark span.cm-attribute { color: #a6e22e; } +.cm-s-pastel-on-dark span.cm-keyword { color: #AEB2F8; } +.cm-s-pastel-on-dark span.cm-string { color: #66A968; } +.cm-s-pastel-on-dark span.cm-variable { color: #AEB2F8; } +.cm-s-pastel-on-dark span.cm-variable-2 { color: #BEBF55; } +.cm-s-pastel-on-dark span.cm-variable-3, .cm-s-pastel-on-dark span.cm-type { color: #DE8E30; } +.cm-s-pastel-on-dark span.cm-def { color: #757aD8; } +.cm-s-pastel-on-dark span.cm-bracket { color: #f8f8f2; } +.cm-s-pastel-on-dark span.cm-tag { color: #C1C144; } +.cm-s-pastel-on-dark span.cm-link { color: #ae81ff; } +.cm-s-pastel-on-dark span.cm-qualifier,.cm-s-pastel-on-dark span.cm-builtin { color: #C1C144; } +.cm-s-pastel-on-dark span.cm-error { + background: #757aD8; + color: #f8f8f0; +} +.cm-s-pastel-on-dark .CodeMirror-activeline-background { background: rgba(255, 255, 255, 0.031); } +.cm-s-pastel-on-dark .CodeMirror-matchingbracket { + border: 1px solid rgba(255,255,255,0.25); + color: #8F938F !important; + margin: -1px -1px 0 -1px; +} diff --git a/docs/js/node_modules/codemirror/theme/railscasts.css b/docs/js/node_modules/codemirror/theme/railscasts.css new file mode 100644 index 000000000..aeff0449d --- /dev/null +++ b/docs/js/node_modules/codemirror/theme/railscasts.css @@ -0,0 +1,34 @@ +/* + + Name: Railscasts + Author: Ryan Bates (http://railscasts.com) + + CodeMirror template by Jan T. Sott (https://github.com/idleberg/base16-codemirror) + Original Base16 color scheme by Chris Kempson (https://github.com/chriskempson/base16) + +*/ + +.cm-s-railscasts.CodeMirror {background: #2b2b2b; color: #f4f1ed;} +.cm-s-railscasts div.CodeMirror-selected {background: #272935 !important;} +.cm-s-railscasts .CodeMirror-gutters {background: #2b2b2b; border-right: 0px;} +.cm-s-railscasts .CodeMirror-linenumber {color: #5a647e;} +.cm-s-railscasts .CodeMirror-cursor {border-left: 1px solid #d4cfc9 !important;} + +.cm-s-railscasts span.cm-comment {color: #bc9458;} +.cm-s-railscasts span.cm-atom {color: #b6b3eb;} +.cm-s-railscasts span.cm-number {color: #b6b3eb;} + +.cm-s-railscasts span.cm-property, .cm-s-railscasts span.cm-attribute {color: #a5c261;} +.cm-s-railscasts span.cm-keyword {color: #da4939;} +.cm-s-railscasts span.cm-string {color: #ffc66d;} + +.cm-s-railscasts span.cm-variable {color: #a5c261;} +.cm-s-railscasts span.cm-variable-2 {color: #6d9cbe;} +.cm-s-railscasts span.cm-def {color: #cc7833;} +.cm-s-railscasts span.cm-error {background: #da4939; color: #d4cfc9;} +.cm-s-railscasts span.cm-bracket {color: #f4f1ed;} +.cm-s-railscasts span.cm-tag {color: #da4939;} +.cm-s-railscasts span.cm-link {color: #b6b3eb;} + +.cm-s-railscasts .CodeMirror-matchingbracket { text-decoration: underline; color: white !important;} +.cm-s-railscasts .CodeMirror-activeline-background { background: #303040; } diff --git a/docs/js/node_modules/codemirror/theme/rubyblue.css b/docs/js/node_modules/codemirror/theme/rubyblue.css new file mode 100644 index 000000000..1f181b06e --- /dev/null +++ b/docs/js/node_modules/codemirror/theme/rubyblue.css @@ -0,0 +1,25 @@ +.cm-s-rubyblue.CodeMirror { background: #112435; color: white; } +.cm-s-rubyblue div.CodeMirror-selected { background: #38566F; } +.cm-s-rubyblue .CodeMirror-line::selection, .cm-s-rubyblue .CodeMirror-line > span::selection, .cm-s-rubyblue .CodeMirror-line > span > span::selection { background: rgba(56, 86, 111, 0.99); } +.cm-s-rubyblue .CodeMirror-line::-moz-selection, .cm-s-rubyblue .CodeMirror-line > span::-moz-selection, .cm-s-rubyblue .CodeMirror-line > span > span::-moz-selection { background: rgba(56, 86, 111, 0.99); } +.cm-s-rubyblue .CodeMirror-gutters { background: #1F4661; border-right: 7px solid #3E7087; } +.cm-s-rubyblue .CodeMirror-guttermarker { color: white; } +.cm-s-rubyblue .CodeMirror-guttermarker-subtle { color: #3E7087; } +.cm-s-rubyblue .CodeMirror-linenumber { color: white; } +.cm-s-rubyblue .CodeMirror-cursor { border-left: 1px solid white; } + +.cm-s-rubyblue span.cm-comment { color: #999; font-style:italic; line-height: 1em; } +.cm-s-rubyblue span.cm-atom { color: #F4C20B; } +.cm-s-rubyblue span.cm-number, .cm-s-rubyblue span.cm-attribute { color: #82C6E0; } +.cm-s-rubyblue span.cm-keyword { color: #F0F; } +.cm-s-rubyblue span.cm-string { color: #F08047; } +.cm-s-rubyblue span.cm-meta { color: #F0F; } +.cm-s-rubyblue span.cm-variable-2, .cm-s-rubyblue span.cm-tag { color: #7BD827; } +.cm-s-rubyblue span.cm-variable-3, .cm-s-rubyblue span.cm-def, .cm-s-rubyblue span.cm-type { color: white; } +.cm-s-rubyblue span.cm-bracket { color: #F0F; } +.cm-s-rubyblue span.cm-link { color: #F4C20B; } +.cm-s-rubyblue span.CodeMirror-matchingbracket { color:#F0F !important; } +.cm-s-rubyblue span.cm-builtin, .cm-s-rubyblue span.cm-special { color: #FF9D00; } +.cm-s-rubyblue span.cm-error { color: #AF2018; } + +.cm-s-rubyblue .CodeMirror-activeline-background { background: #173047; } diff --git a/docs/js/node_modules/codemirror/theme/seti.css b/docs/js/node_modules/codemirror/theme/seti.css new file mode 100644 index 000000000..814f76f7d --- /dev/null +++ b/docs/js/node_modules/codemirror/theme/seti.css @@ -0,0 +1,44 @@ +/* + + Name: seti + Author: Michael Kaminsky (http://github.com/mkaminsky11) + + Original seti color scheme by Jesse Weed (https://github.com/jesseweed/seti-syntax) + +*/ + + +.cm-s-seti.CodeMirror { + background-color: #151718 !important; + color: #CFD2D1 !important; + border: none; +} +.cm-s-seti .CodeMirror-gutters { + color: #404b53; + background-color: #0E1112; + border: none; +} +.cm-s-seti .CodeMirror-cursor { border-left: solid thin #f8f8f0; } +.cm-s-seti .CodeMirror-linenumber { color: #6D8A88; } +.cm-s-seti.CodeMirror-focused div.CodeMirror-selected { background: rgba(255, 255, 255, 0.10); } +.cm-s-seti .CodeMirror-line::selection, .cm-s-seti .CodeMirror-line > span::selection, .cm-s-seti .CodeMirror-line > span > span::selection { background: rgba(255, 255, 255, 0.10); } +.cm-s-seti .CodeMirror-line::-moz-selection, .cm-s-seti .CodeMirror-line > span::-moz-selection, .cm-s-seti .CodeMirror-line > span > span::-moz-selection { background: rgba(255, 255, 255, 0.10); } +.cm-s-seti span.cm-comment { color: #41535b; } +.cm-s-seti span.cm-string, .cm-s-seti span.cm-string-2 { color: #55b5db; } +.cm-s-seti span.cm-number { color: #cd3f45; } +.cm-s-seti span.cm-variable { color: #55b5db; } +.cm-s-seti span.cm-variable-2 { color: #a074c4; } +.cm-s-seti span.cm-def { color: #55b5db; } +.cm-s-seti span.cm-keyword { color: #ff79c6; } +.cm-s-seti span.cm-operator { color: #9fca56; } +.cm-s-seti span.cm-keyword { color: #e6cd69; } +.cm-s-seti span.cm-atom { color: #cd3f45; } +.cm-s-seti span.cm-meta { color: #55b5db; } +.cm-s-seti span.cm-tag { color: #55b5db; } +.cm-s-seti span.cm-attribute { color: #9fca56; } +.cm-s-seti span.cm-qualifier { color: #9fca56; } +.cm-s-seti span.cm-property { color: #a074c4; } +.cm-s-seti span.cm-variable-3, .cm-s-seti span.cm-type { color: #9fca56; } +.cm-s-seti span.cm-builtin { color: #9fca56; } +.cm-s-seti .CodeMirror-activeline-background { background: #101213; } +.cm-s-seti .CodeMirror-matchingbracket { text-decoration: underline; color: white !important; } diff --git a/docs/js/node_modules/codemirror/theme/shadowfox.css b/docs/js/node_modules/codemirror/theme/shadowfox.css new file mode 100644 index 000000000..32d59b139 --- /dev/null +++ b/docs/js/node_modules/codemirror/theme/shadowfox.css @@ -0,0 +1,52 @@ +/* + + Name: shadowfox + Author: overdodactyl (http://github.com/overdodactyl) + + Original shadowfox color scheme by Firefox + +*/ + +.cm-s-shadowfox.CodeMirror { background: #2a2a2e; color: #b1b1b3; } +.cm-s-shadowfox div.CodeMirror-selected { background: #353B48; } +.cm-s-shadowfox .CodeMirror-line::selection, .cm-s-shadowfox .CodeMirror-line > span::selection, .cm-s-shadowfox .CodeMirror-line > span > span::selection { background: #353B48; } +.cm-s-shadowfox .CodeMirror-line::-moz-selection, .cm-s-shadowfox .CodeMirror-line > span::-moz-selection, .cm-s-shadowfox .CodeMirror-line > span > span::-moz-selection { background: #353B48; } +.cm-s-shadowfox .CodeMirror-gutters { background: #0c0c0d ; border-right: 1px solid #0c0c0d; } +.cm-s-shadowfox .CodeMirror-guttermarker { color: #555; } +.cm-s-shadowfox .CodeMirror-linenumber { color: #939393; } +.cm-s-shadowfox .CodeMirror-cursor { border-left: 1px solid #fff; } + +.cm-s-shadowfox span.cm-comment { color: #939393; } +.cm-s-shadowfox span.cm-atom { color: #FF7DE9; } +.cm-s-shadowfox span.cm-quote { color: #FF7DE9; } +.cm-s-shadowfox span.cm-builtin { color: #FF7DE9; } +.cm-s-shadowfox span.cm-attribute { color: #FF7DE9; } +.cm-s-shadowfox span.cm-keyword { color: #FF7DE9; } +.cm-s-shadowfox span.cm-error { color: #FF7DE9; } + +.cm-s-shadowfox span.cm-number { color: #6B89FF; } +.cm-s-shadowfox span.cm-string { color: #6B89FF; } +.cm-s-shadowfox span.cm-string-2 { color: #6B89FF; } + +.cm-s-shadowfox span.cm-meta { color: #939393; } +.cm-s-shadowfox span.cm-hr { color: #939393; } + +.cm-s-shadowfox span.cm-header { color: #75BFFF; } +.cm-s-shadowfox span.cm-qualifier { color: #75BFFF; } +.cm-s-shadowfox span.cm-variable-2 { color: #75BFFF; } + +.cm-s-shadowfox span.cm-property { color: #86DE74; } + +.cm-s-shadowfox span.cm-def { color: #75BFFF; } +.cm-s-shadowfox span.cm-bracket { color: #75BFFF; } +.cm-s-shadowfox span.cm-tag { color: #75BFFF; } +.cm-s-shadowfox span.cm-link:visited { color: #75BFFF; } + +.cm-s-shadowfox span.cm-variable { color: #B98EFF; } +.cm-s-shadowfox span.cm-variable-3 { color: #d7d7db; } +.cm-s-shadowfox span.cm-link { color: #737373; } +.cm-s-shadowfox span.cm-operator { color: #b1b1b3; } +.cm-s-shadowfox span.cm-special { color: #d7d7db; } + +.cm-s-shadowfox .CodeMirror-activeline-background { background: rgba(185, 215, 253, .15) } +.cm-s-shadowfox .CodeMirror-matchingbracket { outline: solid 1px rgba(255, 255, 255, .25); color: white !important; } diff --git a/docs/js/node_modules/codemirror/theme/solarized.css b/docs/js/node_modules/codemirror/theme/solarized.css new file mode 100644 index 000000000..fcd1d70de --- /dev/null +++ b/docs/js/node_modules/codemirror/theme/solarized.css @@ -0,0 +1,168 @@ +/* +Solarized theme for code-mirror +http://ethanschoonover.com/solarized +*/ + +/* +Solarized color palette +http://ethanschoonover.com/solarized/img/solarized-palette.png +*/ + +.solarized.base03 { color: #002b36; } +.solarized.base02 { color: #073642; } +.solarized.base01 { color: #586e75; } +.solarized.base00 { color: #657b83; } +.solarized.base0 { color: #839496; } +.solarized.base1 { color: #93a1a1; } +.solarized.base2 { color: #eee8d5; } +.solarized.base3 { color: #fdf6e3; } +.solarized.solar-yellow { color: #b58900; } +.solarized.solar-orange { color: #cb4b16; } +.solarized.solar-red { color: #dc322f; } +.solarized.solar-magenta { color: #d33682; } +.solarized.solar-violet { color: #6c71c4; } +.solarized.solar-blue { color: #268bd2; } +.solarized.solar-cyan { color: #2aa198; } +.solarized.solar-green { color: #859900; } + +/* Color scheme for code-mirror */ + +.cm-s-solarized { + line-height: 1.45em; + color-profile: sRGB; + rendering-intent: auto; +} +.cm-s-solarized.cm-s-dark { + color: #839496; + background-color: #002b36; + text-shadow: #002b36 0 1px; +} +.cm-s-solarized.cm-s-light { + background-color: #fdf6e3; + color: #657b83; + text-shadow: #eee8d5 0 1px; +} + +.cm-s-solarized .CodeMirror-widget { + text-shadow: none; +} + +.cm-s-solarized .cm-header { color: #586e75; } +.cm-s-solarized .cm-quote { color: #93a1a1; } + +.cm-s-solarized .cm-keyword { color: #cb4b16; } +.cm-s-solarized .cm-atom { color: #d33682; } +.cm-s-solarized .cm-number { color: #d33682; } +.cm-s-solarized .cm-def { color: #2aa198; } + +.cm-s-solarized .cm-variable { color: #839496; } +.cm-s-solarized .cm-variable-2 { color: #b58900; } +.cm-s-solarized .cm-variable-3, .cm-s-solarized .cm-type { color: #6c71c4; } + +.cm-s-solarized .cm-property { color: #2aa198; } +.cm-s-solarized .cm-operator { color: #6c71c4; } + +.cm-s-solarized .cm-comment { color: #586e75; font-style:italic; } + +.cm-s-solarized .cm-string { color: #859900; } +.cm-s-solarized .cm-string-2 { color: #b58900; } + +.cm-s-solarized .cm-meta { color: #859900; } +.cm-s-solarized .cm-qualifier { color: #b58900; } +.cm-s-solarized .cm-builtin { color: #d33682; } +.cm-s-solarized .cm-bracket { color: #cb4b16; } +.cm-s-solarized .CodeMirror-matchingbracket { color: #859900; } +.cm-s-solarized .CodeMirror-nonmatchingbracket { color: #dc322f; } +.cm-s-solarized .cm-tag { color: #93a1a1; } +.cm-s-solarized .cm-attribute { color: #2aa198; } +.cm-s-solarized .cm-hr { + color: transparent; + border-top: 1px solid #586e75; + display: block; +} +.cm-s-solarized .cm-link { color: #93a1a1; cursor: pointer; } +.cm-s-solarized .cm-special { color: #6c71c4; } +.cm-s-solarized .cm-em { + color: #999; + text-decoration: underline; + text-decoration-style: dotted; +} +.cm-s-solarized .cm-error, +.cm-s-solarized .cm-invalidchar { + color: #586e75; + border-bottom: 1px dotted #dc322f; +} + +.cm-s-solarized.cm-s-dark div.CodeMirror-selected { background: #073642; } +.cm-s-solarized.cm-s-dark.CodeMirror ::selection { background: rgba(7, 54, 66, 0.99); } +.cm-s-solarized.cm-s-dark .CodeMirror-line::-moz-selection, .cm-s-dark .CodeMirror-line > span::-moz-selection, .cm-s-dark .CodeMirror-line > span > span::-moz-selection { background: rgba(7, 54, 66, 0.99); } + +.cm-s-solarized.cm-s-light div.CodeMirror-selected { background: #eee8d5; } +.cm-s-solarized.cm-s-light .CodeMirror-line::selection, .cm-s-light .CodeMirror-line > span::selection, .cm-s-light .CodeMirror-line > span > span::selection { background: #eee8d5; } +.cm-s-solarized.cm-s-light .CodeMirror-line::-moz-selection, .cm-s-ligh .CodeMirror-line > span::-moz-selection, .cm-s-ligh .CodeMirror-line > span > span::-moz-selection { background: #eee8d5; } + +/* Editor styling */ + + + +/* Little shadow on the view-port of the buffer view */ +.cm-s-solarized.CodeMirror { + -moz-box-shadow: inset 7px 0 12px -6px #000; + -webkit-box-shadow: inset 7px 0 12px -6px #000; + box-shadow: inset 7px 0 12px -6px #000; +} + +/* Remove gutter border */ +.cm-s-solarized .CodeMirror-gutters { + border-right: 0; +} + +/* Gutter colors and line number styling based of color scheme (dark / light) */ + +/* Dark */ +.cm-s-solarized.cm-s-dark .CodeMirror-gutters { + background-color: #073642; +} + +.cm-s-solarized.cm-s-dark .CodeMirror-linenumber { + color: #586e75; + text-shadow: #021014 0 -1px; +} + +/* Light */ +.cm-s-solarized.cm-s-light .CodeMirror-gutters { + background-color: #eee8d5; +} + +.cm-s-solarized.cm-s-light .CodeMirror-linenumber { + color: #839496; +} + +/* Common */ +.cm-s-solarized .CodeMirror-linenumber { + padding: 0 5px; +} +.cm-s-solarized .CodeMirror-guttermarker-subtle { color: #586e75; } +.cm-s-solarized.cm-s-dark .CodeMirror-guttermarker { color: #ddd; } +.cm-s-solarized.cm-s-light .CodeMirror-guttermarker { color: #cb4b16; } + +.cm-s-solarized .CodeMirror-gutter .CodeMirror-gutter-text { + color: #586e75; +} + +/* Cursor */ +.cm-s-solarized .CodeMirror-cursor { border-left: 1px solid #819090; } + +/* Fat cursor */ +.cm-s-solarized.cm-s-light.cm-fat-cursor .CodeMirror-cursor { background: #77ee77; } +.cm-s-solarized.cm-s-light .cm-animate-fat-cursor { background-color: #77ee77; } +.cm-s-solarized.cm-s-dark.cm-fat-cursor .CodeMirror-cursor { background: #586e75; } +.cm-s-solarized.cm-s-dark .cm-animate-fat-cursor { background-color: #586e75; } + +/* Active line */ +.cm-s-solarized.cm-s-dark .CodeMirror-activeline-background { + background: rgba(255, 255, 255, 0.06); +} +.cm-s-solarized.cm-s-light .CodeMirror-activeline-background { + background: rgba(0, 0, 0, 0.06); +} diff --git a/docs/js/node_modules/codemirror/theme/ssms.css b/docs/js/node_modules/codemirror/theme/ssms.css new file mode 100644 index 000000000..9494c14c2 --- /dev/null +++ b/docs/js/node_modules/codemirror/theme/ssms.css @@ -0,0 +1,16 @@ +.cm-s-ssms span.cm-keyword { color: blue; } +.cm-s-ssms span.cm-comment { color: darkgreen; } +.cm-s-ssms span.cm-string { color: red; } +.cm-s-ssms span.cm-def { color: black; } +.cm-s-ssms span.cm-variable { color: black; } +.cm-s-ssms span.cm-variable-2 { color: black; } +.cm-s-ssms span.cm-atom { color: darkgray; } +.cm-s-ssms .CodeMirror-linenumber { color: teal; } +.cm-s-ssms .CodeMirror-activeline-background { background: #ffffff; } +.cm-s-ssms span.cm-string-2 { color: #FF00FF; } +.cm-s-ssms span.cm-operator, +.cm-s-ssms span.cm-bracket, +.cm-s-ssms span.cm-punctuation { color: darkgray; } +.cm-s-ssms .CodeMirror-gutters { border-right: 3px solid #ffee62; background-color: #ffffff; } +.cm-s-ssms div.CodeMirror-selected { background: #ADD6FF; } + diff --git a/docs/js/node_modules/codemirror/theme/the-matrix.css b/docs/js/node_modules/codemirror/theme/the-matrix.css new file mode 100644 index 000000000..c4c93c11e --- /dev/null +++ b/docs/js/node_modules/codemirror/theme/the-matrix.css @@ -0,0 +1,30 @@ +.cm-s-the-matrix.CodeMirror { background: #000000; color: #00FF00; } +.cm-s-the-matrix div.CodeMirror-selected { background: #2D2D2D; } +.cm-s-the-matrix .CodeMirror-line::selection, .cm-s-the-matrix .CodeMirror-line > span::selection, .cm-s-the-matrix .CodeMirror-line > span > span::selection { background: rgba(45, 45, 45, 0.99); } +.cm-s-the-matrix .CodeMirror-line::-moz-selection, .cm-s-the-matrix .CodeMirror-line > span::-moz-selection, .cm-s-the-matrix .CodeMirror-line > span > span::-moz-selection { background: rgba(45, 45, 45, 0.99); } +.cm-s-the-matrix .CodeMirror-gutters { background: #060; border-right: 2px solid #00FF00; } +.cm-s-the-matrix .CodeMirror-guttermarker { color: #0f0; } +.cm-s-the-matrix .CodeMirror-guttermarker-subtle { color: white; } +.cm-s-the-matrix .CodeMirror-linenumber { color: #FFFFFF; } +.cm-s-the-matrix .CodeMirror-cursor { border-left: 1px solid #00FF00; } + +.cm-s-the-matrix span.cm-keyword { color: #008803; font-weight: bold; } +.cm-s-the-matrix span.cm-atom { color: #3FF; } +.cm-s-the-matrix span.cm-number { color: #FFB94F; } +.cm-s-the-matrix span.cm-def { color: #99C; } +.cm-s-the-matrix span.cm-variable { color: #F6C; } +.cm-s-the-matrix span.cm-variable-2 { color: #C6F; } +.cm-s-the-matrix span.cm-variable-3, .cm-s-the-matrix span.cm-type { color: #96F; } +.cm-s-the-matrix span.cm-property { color: #62FFA0; } +.cm-s-the-matrix span.cm-operator { color: #999; } +.cm-s-the-matrix span.cm-comment { color: #CCCCCC; } +.cm-s-the-matrix span.cm-string { color: #39C; } +.cm-s-the-matrix span.cm-meta { color: #C9F; } +.cm-s-the-matrix span.cm-qualifier { color: #FFF700; } +.cm-s-the-matrix span.cm-builtin { color: #30a; } +.cm-s-the-matrix span.cm-bracket { color: #cc7; } +.cm-s-the-matrix span.cm-tag { color: #FFBD40; } +.cm-s-the-matrix span.cm-attribute { color: #FFF700; } +.cm-s-the-matrix span.cm-error { color: #FF0000; } + +.cm-s-the-matrix .CodeMirror-activeline-background { background: #040; } diff --git a/docs/js/node_modules/codemirror/theme/tomorrow-night-bright.css b/docs/js/node_modules/codemirror/theme/tomorrow-night-bright.css new file mode 100644 index 000000000..b6dd4a927 --- /dev/null +++ b/docs/js/node_modules/codemirror/theme/tomorrow-night-bright.css @@ -0,0 +1,35 @@ +/* + + Name: Tomorrow Night - Bright + Author: Chris Kempson + + Port done by Gerard Braad + +*/ + +.cm-s-tomorrow-night-bright.CodeMirror { background: #000000; color: #eaeaea; } +.cm-s-tomorrow-night-bright div.CodeMirror-selected { background: #424242; } +.cm-s-tomorrow-night-bright .CodeMirror-gutters { background: #000000; border-right: 0px; } +.cm-s-tomorrow-night-bright .CodeMirror-guttermarker { color: #e78c45; } +.cm-s-tomorrow-night-bright .CodeMirror-guttermarker-subtle { color: #777; } +.cm-s-tomorrow-night-bright .CodeMirror-linenumber { color: #424242; } +.cm-s-tomorrow-night-bright .CodeMirror-cursor { border-left: 1px solid #6A6A6A; } + +.cm-s-tomorrow-night-bright span.cm-comment { color: #d27b53; } +.cm-s-tomorrow-night-bright span.cm-atom { color: #a16a94; } +.cm-s-tomorrow-night-bright span.cm-number { color: #a16a94; } + +.cm-s-tomorrow-night-bright span.cm-property, .cm-s-tomorrow-night-bright span.cm-attribute { color: #99cc99; } +.cm-s-tomorrow-night-bright span.cm-keyword { color: #d54e53; } +.cm-s-tomorrow-night-bright span.cm-string { color: #e7c547; } + +.cm-s-tomorrow-night-bright span.cm-variable { color: #b9ca4a; } +.cm-s-tomorrow-night-bright span.cm-variable-2 { color: #7aa6da; } +.cm-s-tomorrow-night-bright span.cm-def { color: #e78c45; } +.cm-s-tomorrow-night-bright span.cm-bracket { color: #eaeaea; } +.cm-s-tomorrow-night-bright span.cm-tag { color: #d54e53; } +.cm-s-tomorrow-night-bright span.cm-link { color: #a16a94; } +.cm-s-tomorrow-night-bright span.cm-error { background: #d54e53; color: #6A6A6A; } + +.cm-s-tomorrow-night-bright .CodeMirror-activeline-background { background: #2a2a2a; } +.cm-s-tomorrow-night-bright .CodeMirror-matchingbracket { text-decoration: underline; color: white !important; } diff --git a/docs/js/node_modules/codemirror/theme/tomorrow-night-eighties.css b/docs/js/node_modules/codemirror/theme/tomorrow-night-eighties.css new file mode 100644 index 000000000..2a9debc32 --- /dev/null +++ b/docs/js/node_modules/codemirror/theme/tomorrow-night-eighties.css @@ -0,0 +1,38 @@ +/* + + Name: Tomorrow Night - Eighties + Author: Chris Kempson + + CodeMirror template by Jan T. Sott (https://github.com/idleberg/base16-codemirror) + Original Base16 color scheme by Chris Kempson (https://github.com/chriskempson/base16) + +*/ + +.cm-s-tomorrow-night-eighties.CodeMirror { background: #000000; color: #CCCCCC; } +.cm-s-tomorrow-night-eighties div.CodeMirror-selected { background: #2D2D2D; } +.cm-s-tomorrow-night-eighties .CodeMirror-line::selection, .cm-s-tomorrow-night-eighties .CodeMirror-line > span::selection, .cm-s-tomorrow-night-eighties .CodeMirror-line > span > span::selection { background: rgba(45, 45, 45, 0.99); } +.cm-s-tomorrow-night-eighties .CodeMirror-line::-moz-selection, .cm-s-tomorrow-night-eighties .CodeMirror-line > span::-moz-selection, .cm-s-tomorrow-night-eighties .CodeMirror-line > span > span::-moz-selection { background: rgba(45, 45, 45, 0.99); } +.cm-s-tomorrow-night-eighties .CodeMirror-gutters { background: #000000; border-right: 0px; } +.cm-s-tomorrow-night-eighties .CodeMirror-guttermarker { color: #f2777a; } +.cm-s-tomorrow-night-eighties .CodeMirror-guttermarker-subtle { color: #777; } +.cm-s-tomorrow-night-eighties .CodeMirror-linenumber { color: #515151; } +.cm-s-tomorrow-night-eighties .CodeMirror-cursor { border-left: 1px solid #6A6A6A; } + +.cm-s-tomorrow-night-eighties span.cm-comment { color: #d27b53; } +.cm-s-tomorrow-night-eighties span.cm-atom { color: #a16a94; } +.cm-s-tomorrow-night-eighties span.cm-number { color: #a16a94; } + +.cm-s-tomorrow-night-eighties span.cm-property, .cm-s-tomorrow-night-eighties span.cm-attribute { color: #99cc99; } +.cm-s-tomorrow-night-eighties span.cm-keyword { color: #f2777a; } +.cm-s-tomorrow-night-eighties span.cm-string { color: #ffcc66; } + +.cm-s-tomorrow-night-eighties span.cm-variable { color: #99cc99; } +.cm-s-tomorrow-night-eighties span.cm-variable-2 { color: #6699cc; } +.cm-s-tomorrow-night-eighties span.cm-def { color: #f99157; } +.cm-s-tomorrow-night-eighties span.cm-bracket { color: #CCCCCC; } +.cm-s-tomorrow-night-eighties span.cm-tag { color: #f2777a; } +.cm-s-tomorrow-night-eighties span.cm-link { color: #a16a94; } +.cm-s-tomorrow-night-eighties span.cm-error { background: #f2777a; color: #6A6A6A; } + +.cm-s-tomorrow-night-eighties .CodeMirror-activeline-background { background: #343600; } +.cm-s-tomorrow-night-eighties .CodeMirror-matchingbracket { text-decoration: underline; color: white !important; } diff --git a/docs/js/node_modules/codemirror/theme/ttcn.css b/docs/js/node_modules/codemirror/theme/ttcn.css new file mode 100644 index 000000000..0b14ac35d --- /dev/null +++ b/docs/js/node_modules/codemirror/theme/ttcn.css @@ -0,0 +1,64 @@ +.cm-s-ttcn .cm-quote { color: #090; } +.cm-s-ttcn .cm-negative { color: #d44; } +.cm-s-ttcn .cm-positive { color: #292; } +.cm-s-ttcn .cm-header, .cm-strong { font-weight: bold; } +.cm-s-ttcn .cm-em { font-style: italic; } +.cm-s-ttcn .cm-link { text-decoration: underline; } +.cm-s-ttcn .cm-strikethrough { text-decoration: line-through; } +.cm-s-ttcn .cm-header { color: #00f; font-weight: bold; } + +.cm-s-ttcn .cm-atom { color: #219; } +.cm-s-ttcn .cm-attribute { color: #00c; } +.cm-s-ttcn .cm-bracket { color: #997; } +.cm-s-ttcn .cm-comment { color: #333333; } +.cm-s-ttcn .cm-def { color: #00f; } +.cm-s-ttcn .cm-em { font-style: italic; } +.cm-s-ttcn .cm-error { color: #f00; } +.cm-s-ttcn .cm-hr { color: #999; } +.cm-s-ttcn .cm-invalidchar { color: #f00; } +.cm-s-ttcn .cm-keyword { font-weight:bold; } +.cm-s-ttcn .cm-link { color: #00c; text-decoration: underline; } +.cm-s-ttcn .cm-meta { color: #555; } +.cm-s-ttcn .cm-negative { color: #d44; } +.cm-s-ttcn .cm-positive { color: #292; } +.cm-s-ttcn .cm-qualifier { color: #555; } +.cm-s-ttcn .cm-strikethrough { text-decoration: line-through; } +.cm-s-ttcn .cm-string { color: #006400; } +.cm-s-ttcn .cm-string-2 { color: #f50; } +.cm-s-ttcn .cm-strong { font-weight: bold; } +.cm-s-ttcn .cm-tag { color: #170; } +.cm-s-ttcn .cm-variable { color: #8B2252; } +.cm-s-ttcn .cm-variable-2 { color: #05a; } +.cm-s-ttcn .cm-variable-3, .cm-s-ttcn .cm-type { color: #085; } + +.cm-s-ttcn .cm-invalidchar { color: #f00; } + +/* ASN */ +.cm-s-ttcn .cm-accessTypes, +.cm-s-ttcn .cm-compareTypes { color: #27408B; } +.cm-s-ttcn .cm-cmipVerbs { color: #8B2252; } +.cm-s-ttcn .cm-modifier { color:#D2691E; } +.cm-s-ttcn .cm-status { color:#8B4545; } +.cm-s-ttcn .cm-storage { color:#A020F0; } +.cm-s-ttcn .cm-tags { color:#006400; } + +/* CFG */ +.cm-s-ttcn .cm-externalCommands { color: #8B4545; font-weight:bold; } +.cm-s-ttcn .cm-fileNCtrlMaskOptions, +.cm-s-ttcn .cm-sectionTitle { color: #2E8B57; font-weight:bold; } + +/* TTCN */ +.cm-s-ttcn .cm-booleanConsts, +.cm-s-ttcn .cm-otherConsts, +.cm-s-ttcn .cm-verdictConsts { color: #006400; } +.cm-s-ttcn .cm-configOps, +.cm-s-ttcn .cm-functionOps, +.cm-s-ttcn .cm-portOps, +.cm-s-ttcn .cm-sutOps, +.cm-s-ttcn .cm-timerOps, +.cm-s-ttcn .cm-verdictOps { color: #0000FF; } +.cm-s-ttcn .cm-preprocessor, +.cm-s-ttcn .cm-templateMatch, +.cm-s-ttcn .cm-ttcn3Macros { color: #27408B; } +.cm-s-ttcn .cm-types { color: #A52A2A; font-weight:bold; } +.cm-s-ttcn .cm-visibilityModifiers { font-weight:bold; } diff --git a/docs/js/node_modules/codemirror/theme/twilight.css b/docs/js/node_modules/codemirror/theme/twilight.css new file mode 100644 index 000000000..b2b1b2aa9 --- /dev/null +++ b/docs/js/node_modules/codemirror/theme/twilight.css @@ -0,0 +1,32 @@ +.cm-s-twilight.CodeMirror { background: #141414; color: #f7f7f7; } /**/ +.cm-s-twilight div.CodeMirror-selected { background: #323232; } /**/ +.cm-s-twilight .CodeMirror-line::selection, .cm-s-twilight .CodeMirror-line > span::selection, .cm-s-twilight .CodeMirror-line > span > span::selection { background: rgba(50, 50, 50, 0.99); } +.cm-s-twilight .CodeMirror-line::-moz-selection, .cm-s-twilight .CodeMirror-line > span::-moz-selection, .cm-s-twilight .CodeMirror-line > span > span::-moz-selection { background: rgba(50, 50, 50, 0.99); } + +.cm-s-twilight .CodeMirror-gutters { background: #222; border-right: 1px solid #aaa; } +.cm-s-twilight .CodeMirror-guttermarker { color: white; } +.cm-s-twilight .CodeMirror-guttermarker-subtle { color: #aaa; } +.cm-s-twilight .CodeMirror-linenumber { color: #aaa; } +.cm-s-twilight .CodeMirror-cursor { border-left: 1px solid white; } + +.cm-s-twilight .cm-keyword { color: #f9ee98; } /**/ +.cm-s-twilight .cm-atom { color: #FC0; } +.cm-s-twilight .cm-number { color: #ca7841; } /**/ +.cm-s-twilight .cm-def { color: #8DA6CE; } +.cm-s-twilight span.cm-variable-2, .cm-s-twilight span.cm-tag { color: #607392; } /**/ +.cm-s-twilight span.cm-variable-3, .cm-s-twilight span.cm-def, .cm-s-twilight span.cm-type { color: #607392; } /**/ +.cm-s-twilight .cm-operator { color: #cda869; } /**/ +.cm-s-twilight .cm-comment { color:#777; font-style:italic; font-weight:normal; } /**/ +.cm-s-twilight .cm-string { color:#8f9d6a; font-style:italic; } /**/ +.cm-s-twilight .cm-string-2 { color:#bd6b18; } /*?*/ +.cm-s-twilight .cm-meta { background-color:#141414; color:#f7f7f7; } /*?*/ +.cm-s-twilight .cm-builtin { color: #cda869; } /*?*/ +.cm-s-twilight .cm-tag { color: #997643; } /**/ +.cm-s-twilight .cm-attribute { color: #d6bb6d; } /*?*/ +.cm-s-twilight .cm-header { color: #FF6400; } +.cm-s-twilight .cm-hr { color: #AEAEAE; } +.cm-s-twilight .cm-link { color:#ad9361; font-style:italic; text-decoration:none; } /**/ +.cm-s-twilight .cm-error { border-bottom: 1px solid red; } + +.cm-s-twilight .CodeMirror-activeline-background { background: #27282E; } +.cm-s-twilight .CodeMirror-matchingbracket { outline:1px solid grey; color:white !important; } diff --git a/docs/js/node_modules/codemirror/theme/vibrant-ink.css b/docs/js/node_modules/codemirror/theme/vibrant-ink.css new file mode 100644 index 000000000..6358ad365 --- /dev/null +++ b/docs/js/node_modules/codemirror/theme/vibrant-ink.css @@ -0,0 +1,34 @@ +/* Taken from the popular Visual Studio Vibrant Ink Schema */ + +.cm-s-vibrant-ink.CodeMirror { background: black; color: white; } +.cm-s-vibrant-ink div.CodeMirror-selected { background: #35493c; } +.cm-s-vibrant-ink .CodeMirror-line::selection, .cm-s-vibrant-ink .CodeMirror-line > span::selection, .cm-s-vibrant-ink .CodeMirror-line > span > span::selection { background: rgba(53, 73, 60, 0.99); } +.cm-s-vibrant-ink .CodeMirror-line::-moz-selection, .cm-s-vibrant-ink .CodeMirror-line > span::-moz-selection, .cm-s-vibrant-ink .CodeMirror-line > span > span::-moz-selection { background: rgba(53, 73, 60, 0.99); } + +.cm-s-vibrant-ink .CodeMirror-gutters { background: #002240; border-right: 1px solid #aaa; } +.cm-s-vibrant-ink .CodeMirror-guttermarker { color: white; } +.cm-s-vibrant-ink .CodeMirror-guttermarker-subtle { color: #d0d0d0; } +.cm-s-vibrant-ink .CodeMirror-linenumber { color: #d0d0d0; } +.cm-s-vibrant-ink .CodeMirror-cursor { border-left: 1px solid white; } + +.cm-s-vibrant-ink .cm-keyword { color: #CC7832; } +.cm-s-vibrant-ink .cm-atom { color: #FC0; } +.cm-s-vibrant-ink .cm-number { color: #FFEE98; } +.cm-s-vibrant-ink .cm-def { color: #8DA6CE; } +.cm-s-vibrant-ink span.cm-variable-2, .cm-s-vibrant span.cm-tag { color: #FFC66D; } +.cm-s-vibrant-ink span.cm-variable-3, .cm-s-vibrant span.cm-def, .cm-s-vibrant span.cm-type { color: #FFC66D; } +.cm-s-vibrant-ink .cm-operator { color: #888; } +.cm-s-vibrant-ink .cm-comment { color: gray; font-weight: bold; } +.cm-s-vibrant-ink .cm-string { color: #A5C25C; } +.cm-s-vibrant-ink .cm-string-2 { color: red; } +.cm-s-vibrant-ink .cm-meta { color: #D8FA3C; } +.cm-s-vibrant-ink .cm-builtin { color: #8DA6CE; } +.cm-s-vibrant-ink .cm-tag { color: #8DA6CE; } +.cm-s-vibrant-ink .cm-attribute { color: #8DA6CE; } +.cm-s-vibrant-ink .cm-header { color: #FF6400; } +.cm-s-vibrant-ink .cm-hr { color: #AEAEAE; } +.cm-s-vibrant-ink .cm-link { color: #5656F3; } +.cm-s-vibrant-ink .cm-error { border-bottom: 1px solid red; } + +.cm-s-vibrant-ink .CodeMirror-activeline-background { background: #27282E; } +.cm-s-vibrant-ink .CodeMirror-matchingbracket { outline:1px solid grey; color:white !important; } diff --git a/docs/js/node_modules/codemirror/theme/xq-dark.css b/docs/js/node_modules/codemirror/theme/xq-dark.css new file mode 100644 index 000000000..7da1a0f70 --- /dev/null +++ b/docs/js/node_modules/codemirror/theme/xq-dark.css @@ -0,0 +1,53 @@ +/* +Copyright (C) 2011 by MarkLogic Corporation +Author: Mike Brevoort + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ +.cm-s-xq-dark.CodeMirror { background: #0a001f; color: #f8f8f8; } +.cm-s-xq-dark div.CodeMirror-selected { background: #27007A; } +.cm-s-xq-dark .CodeMirror-line::selection, .cm-s-xq-dark .CodeMirror-line > span::selection, .cm-s-xq-dark .CodeMirror-line > span > span::selection { background: rgba(39, 0, 122, 0.99); } +.cm-s-xq-dark .CodeMirror-line::-moz-selection, .cm-s-xq-dark .CodeMirror-line > span::-moz-selection, .cm-s-xq-dark .CodeMirror-line > span > span::-moz-selection { background: rgba(39, 0, 122, 0.99); } +.cm-s-xq-dark .CodeMirror-gutters { background: #0a001f; border-right: 1px solid #aaa; } +.cm-s-xq-dark .CodeMirror-guttermarker { color: #FFBD40; } +.cm-s-xq-dark .CodeMirror-guttermarker-subtle { color: #f8f8f8; } +.cm-s-xq-dark .CodeMirror-linenumber { color: #f8f8f8; } +.cm-s-xq-dark .CodeMirror-cursor { border-left: 1px solid white; } + +.cm-s-xq-dark span.cm-keyword { color: #FFBD40; } +.cm-s-xq-dark span.cm-atom { color: #6C8CD5; } +.cm-s-xq-dark span.cm-number { color: #164; } +.cm-s-xq-dark span.cm-def { color: #FFF; text-decoration:underline; } +.cm-s-xq-dark span.cm-variable { color: #FFF; } +.cm-s-xq-dark span.cm-variable-2 { color: #EEE; } +.cm-s-xq-dark span.cm-variable-3, .cm-s-xq-dark span.cm-type { color: #DDD; } +.cm-s-xq-dark span.cm-property {} +.cm-s-xq-dark span.cm-operator {} +.cm-s-xq-dark span.cm-comment { color: gray; } +.cm-s-xq-dark span.cm-string { color: #9FEE00; } +.cm-s-xq-dark span.cm-meta { color: yellow; } +.cm-s-xq-dark span.cm-qualifier { color: #FFF700; } +.cm-s-xq-dark span.cm-builtin { color: #30a; } +.cm-s-xq-dark span.cm-bracket { color: #cc7; } +.cm-s-xq-dark span.cm-tag { color: #FFBD40; } +.cm-s-xq-dark span.cm-attribute { color: #FFF700; } +.cm-s-xq-dark span.cm-error { color: #f00; } + +.cm-s-xq-dark .CodeMirror-activeline-background { background: #27282E; } +.cm-s-xq-dark .CodeMirror-matchingbracket { outline:1px solid grey; color:white !important; } diff --git a/docs/js/node_modules/codemirror/theme/xq-light.css b/docs/js/node_modules/codemirror/theme/xq-light.css new file mode 100644 index 000000000..7b182ea99 --- /dev/null +++ b/docs/js/node_modules/codemirror/theme/xq-light.css @@ -0,0 +1,43 @@ +/* +Copyright (C) 2011 by MarkLogic Corporation +Author: Mike Brevoort + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ +.cm-s-xq-light span.cm-keyword { line-height: 1em; font-weight: bold; color: #5A5CAD; } +.cm-s-xq-light span.cm-atom { color: #6C8CD5; } +.cm-s-xq-light span.cm-number { color: #164; } +.cm-s-xq-light span.cm-def { text-decoration:underline; } +.cm-s-xq-light span.cm-variable { color: black; } +.cm-s-xq-light span.cm-variable-2 { color:black; } +.cm-s-xq-light span.cm-variable-3, .cm-s-xq-light span.cm-type { color: black; } +.cm-s-xq-light span.cm-property {} +.cm-s-xq-light span.cm-operator {} +.cm-s-xq-light span.cm-comment { color: #0080FF; font-style: italic; } +.cm-s-xq-light span.cm-string { color: red; } +.cm-s-xq-light span.cm-meta { color: yellow; } +.cm-s-xq-light span.cm-qualifier { color: grey; } +.cm-s-xq-light span.cm-builtin { color: #7EA656; } +.cm-s-xq-light span.cm-bracket { color: #cc7; } +.cm-s-xq-light span.cm-tag { color: #3F7F7F; } +.cm-s-xq-light span.cm-attribute { color: #7F007F; } +.cm-s-xq-light span.cm-error { color: #f00; } + +.cm-s-xq-light .CodeMirror-activeline-background { background: #e8f2ff; } +.cm-s-xq-light .CodeMirror-matchingbracket { outline:1px solid grey;color:black !important;background:yellow; } diff --git a/docs/js/node_modules/codemirror/theme/yeti.css b/docs/js/node_modules/codemirror/theme/yeti.css new file mode 100644 index 000000000..d085f7249 --- /dev/null +++ b/docs/js/node_modules/codemirror/theme/yeti.css @@ -0,0 +1,44 @@ +/* + + Name: yeti + Author: Michael Kaminsky (http://github.com/mkaminsky11) + + Original yeti color scheme by Jesse Weed (https://github.com/jesseweed/yeti-syntax) + +*/ + + +.cm-s-yeti.CodeMirror { + background-color: #ECEAE8 !important; + color: #d1c9c0 !important; + border: none; +} + +.cm-s-yeti .CodeMirror-gutters { + color: #adaba6; + background-color: #E5E1DB; + border: none; +} +.cm-s-yeti .CodeMirror-cursor { border-left: solid thin #d1c9c0; } +.cm-s-yeti .CodeMirror-linenumber { color: #adaba6; } +.cm-s-yeti.CodeMirror-focused div.CodeMirror-selected { background: #DCD8D2; } +.cm-s-yeti .CodeMirror-line::selection, .cm-s-yeti .CodeMirror-line > span::selection, .cm-s-yeti .CodeMirror-line > span > span::selection { background: #DCD8D2; } +.cm-s-yeti .CodeMirror-line::-moz-selection, .cm-s-yeti .CodeMirror-line > span::-moz-selection, .cm-s-yeti .CodeMirror-line > span > span::-moz-selection { background: #DCD8D2; } +.cm-s-yeti span.cm-comment { color: #d4c8be; } +.cm-s-yeti span.cm-string, .cm-s-yeti span.cm-string-2 { color: #96c0d8; } +.cm-s-yeti span.cm-number { color: #a074c4; } +.cm-s-yeti span.cm-variable { color: #55b5db; } +.cm-s-yeti span.cm-variable-2 { color: #a074c4; } +.cm-s-yeti span.cm-def { color: #55b5db; } +.cm-s-yeti span.cm-operator { color: #9fb96e; } +.cm-s-yeti span.cm-keyword { color: #9fb96e; } +.cm-s-yeti span.cm-atom { color: #a074c4; } +.cm-s-yeti span.cm-meta { color: #96c0d8; } +.cm-s-yeti span.cm-tag { color: #96c0d8; } +.cm-s-yeti span.cm-attribute { color: #9fb96e; } +.cm-s-yeti span.cm-qualifier { color: #96c0d8; } +.cm-s-yeti span.cm-property { color: #a074c4; } +.cm-s-yeti span.cm-builtin { color: #a074c4; } +.cm-s-yeti span.cm-variable-3, .cm-s-yeti span.cm-type { color: #96c0d8; } +.cm-s-yeti .CodeMirror-activeline-background { background: #E7E4E0; } +.cm-s-yeti .CodeMirror-matchingbracket { text-decoration: underline; } diff --git a/docs/js/node_modules/codemirror/theme/yonce.css b/docs/js/node_modules/codemirror/theme/yonce.css new file mode 100644 index 000000000..975f0788a --- /dev/null +++ b/docs/js/node_modules/codemirror/theme/yonce.css @@ -0,0 +1,59 @@ +/* + + Name: yoncé + Author: Thomas MacLean (http://github.com/thomasmaclean) + + Original yoncé color scheme by Mina Markham (https://github.com/minamarkham) + +*/ + +.cm-s-yonce.CodeMirror { background: #1C1C1C; color: #d4d4d4; } /**/ +.cm-s-yonce div.CodeMirror-selected { background: rgba(252, 69, 133, 0.478); } /**/ +.cm-s-yonce .CodeMirror-selectedtext, +.cm-s-yonce .CodeMirror-selected, +.cm-s-yonce .CodeMirror-line::selection, +.cm-s-yonce .CodeMirror-line > span::selection, +.cm-s-yonce .CodeMirror-line > span > span::selection, +.cm-s-yonce .CodeMirror-line::-moz-selection, +.cm-s-yonce .CodeMirror-line > span::-moz-selection, +.cm-s-yonce .CodeMirror-line > span > span::-moz-selection { background: rgba(252, 67, 132, 0.47); } + +.cm-s-yonce.CodeMirror pre { padding-left: 0px; } +.cm-s-yonce .CodeMirror-gutters {background: #1C1C1C; border-right: 0px;} +.cm-s-yonce .CodeMirror-linenumber {color: #777777; padding-right: 10px; } +.cm-s-yonce .CodeMirror-activeline .CodeMirror-linenumber.CodeMirror-gutter-elt { background: #1C1C1C; color: #fc4384; } +.cm-s-yonce .CodeMirror-linenumber { color: #777; } +.cm-s-yonce .CodeMirror-cursor { border-left: 2px solid #FC4384; } +.cm-s-yonce .cm-searching { background: rgba(243, 155, 53, .3) !important; outline: 1px solid #F39B35; } +.cm-s-yonce .cm-searching.CodeMirror-selectedtext { background: rgba(243, 155, 53, .7) !important; color: white; } + +.cm-s-yonce .cm-keyword { color: #00A7AA; } /**/ +.cm-s-yonce .cm-atom { color: #F39B35; } +.cm-s-yonce .cm-number, .cm-s-yonce span.cm-type { color: #A06FCA; } /**/ +.cm-s-yonce .cm-def { color: #98E342; } +.cm-s-yonce .cm-property, +.cm-s-yonce span.cm-variable { color: #D4D4D4; font-style: italic; } +.cm-s-yonce span.cm-variable-2 { color: #da7dae; font-style: italic; } +.cm-s-yonce span.cm-variable-3 { color: #A06FCA; } +.cm-s-yonce .cm-type.cm-def { color: #FC4384; font-style: normal; text-decoration: underline; } +.cm-s-yonce .cm-property.cm-def { color: #FC4384; font-style: normal; } +.cm-s-yonce .cm-callee { color: #FC4384; font-style: normal; } +.cm-s-yonce .cm-operator { color: #FC4384; } /**/ +.cm-s-yonce .cm-qualifier, +.cm-s-yonce .cm-tag { color: #FC4384; } +.cm-s-yonce .cm-tag.cm-bracket { color: #D4D4D4; } +.cm-s-yonce .cm-attribute { color: #A06FCA; } +.cm-s-yonce .cm-comment { color:#696d70; font-style:italic; font-weight:normal; } /**/ +.cm-s-yonce .cm-comment.cm-tag { color: #FC4384 } +.cm-s-yonce .cm-comment.cm-attribute { color: #D4D4D4; } +.cm-s-yonce .cm-string { color:#E6DB74; } /**/ +.cm-s-yonce .cm-string-2 { color:#F39B35; } /*?*/ +.cm-s-yonce .cm-meta { color: #D4D4D4; background: inherit; } +.cm-s-yonce .cm-builtin { color: #FC4384; } /*?*/ +.cm-s-yonce .cm-header { color: #da7dae; } +.cm-s-yonce .cm-hr { color: #98E342; } +.cm-s-yonce .cm-link { color:#696d70; font-style:italic; text-decoration:none; } /**/ +.cm-s-yonce .cm-error { border-bottom: 1px solid #C42412; } + +.cm-s-yonce .CodeMirror-activeline-background { background: #272727; } +.cm-s-yonce .CodeMirror-matchingbracket { outline:1px solid grey; color:#D4D4D4 !important; } diff --git a/docs/js/node_modules/codemirror/theme/zenburn.css b/docs/js/node_modules/codemirror/theme/zenburn.css new file mode 100644 index 000000000..781c40aca --- /dev/null +++ b/docs/js/node_modules/codemirror/theme/zenburn.css @@ -0,0 +1,37 @@ +/** + * " + * Using Zenburn color palette from the Emacs Zenburn Theme + * https://github.com/bbatsov/zenburn-emacs/blob/master/zenburn-theme.el + * + * Also using parts of https://github.com/xavi/coderay-lighttable-theme + * " + * From: https://github.com/wisenomad/zenburn-lighttable-theme/blob/master/zenburn.css + */ + +.cm-s-zenburn .CodeMirror-gutters { background: #3f3f3f !important; } +.cm-s-zenburn .CodeMirror-foldgutter-open, .CodeMirror-foldgutter-folded { color: #999; } +.cm-s-zenburn .CodeMirror-cursor { border-left: 1px solid white; } +.cm-s-zenburn { background-color: #3f3f3f; color: #dcdccc; } +.cm-s-zenburn span.cm-builtin { color: #dcdccc; font-weight: bold; } +.cm-s-zenburn span.cm-comment { color: #7f9f7f; } +.cm-s-zenburn span.cm-keyword { color: #f0dfaf; font-weight: bold; } +.cm-s-zenburn span.cm-atom { color: #bfebbf; } +.cm-s-zenburn span.cm-def { color: #dcdccc; } +.cm-s-zenburn span.cm-variable { color: #dfaf8f; } +.cm-s-zenburn span.cm-variable-2 { color: #dcdccc; } +.cm-s-zenburn span.cm-string { color: #cc9393; } +.cm-s-zenburn span.cm-string-2 { color: #cc9393; } +.cm-s-zenburn span.cm-number { color: #dcdccc; } +.cm-s-zenburn span.cm-tag { color: #93e0e3; } +.cm-s-zenburn span.cm-property { color: #dfaf8f; } +.cm-s-zenburn span.cm-attribute { color: #dfaf8f; } +.cm-s-zenburn span.cm-qualifier { color: #7cb8bb; } +.cm-s-zenburn span.cm-meta { color: #f0dfaf; } +.cm-s-zenburn span.cm-header { color: #f0efd0; } +.cm-s-zenburn span.cm-operator { color: #f0efd0; } +.cm-s-zenburn span.CodeMirror-matchingbracket { box-sizing: border-box; background: transparent; border-bottom: 1px solid; } +.cm-s-zenburn span.CodeMirror-nonmatchingbracket { border-bottom: 1px solid; background: none; } +.cm-s-zenburn .CodeMirror-activeline { background: #000000; } +.cm-s-zenburn .CodeMirror-activeline-background { background: #000000; } +.cm-s-zenburn div.CodeMirror-selected { background: #545454; } +.cm-s-zenburn .CodeMirror-focused div.CodeMirror-selected { background: #4f4f4f; } diff --git a/docs/js/node_modules/combined-stream/License b/docs/js/node_modules/combined-stream/License new file mode 100644 index 000000000..4804b7ab4 --- /dev/null +++ b/docs/js/node_modules/combined-stream/License @@ -0,0 +1,19 @@ +Copyright (c) 2011 Debuggable Limited + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/docs/js/node_modules/combined-stream/Readme.md b/docs/js/node_modules/combined-stream/Readme.md new file mode 100644 index 000000000..9e367b5bc --- /dev/null +++ b/docs/js/node_modules/combined-stream/Readme.md @@ -0,0 +1,138 @@ +# combined-stream + +A stream that emits multiple other streams one after another. + +**NB** Currently `combined-stream` works with streams version 1 only. There is ongoing effort to switch this library to streams version 2. Any help is welcome. :) Meanwhile you can explore other libraries that provide streams2 support with more or less compatibility with `combined-stream`. + +- [combined-stream2](https://www.npmjs.com/package/combined-stream2): A drop-in streams2-compatible replacement for the combined-stream module. + +- [multistream](https://www.npmjs.com/package/multistream): A stream that emits multiple other streams one after another. + +## Installation + +``` bash +npm install combined-stream +``` + +## Usage + +Here is a simple example that shows how you can use combined-stream to combine +two files into one: + +``` javascript +var CombinedStream = require('combined-stream'); +var fs = require('fs'); + +var combinedStream = CombinedStream.create(); +combinedStream.append(fs.createReadStream('file1.txt')); +combinedStream.append(fs.createReadStream('file2.txt')); + +combinedStream.pipe(fs.createWriteStream('combined.txt')); +``` + +While the example above works great, it will pause all source streams until +they are needed. If you don't want that to happen, you can set `pauseStreams` +to `false`: + +``` javascript +var CombinedStream = require('combined-stream'); +var fs = require('fs'); + +var combinedStream = CombinedStream.create({pauseStreams: false}); +combinedStream.append(fs.createReadStream('file1.txt')); +combinedStream.append(fs.createReadStream('file2.txt')); + +combinedStream.pipe(fs.createWriteStream('combined.txt')); +``` + +However, what if you don't have all the source streams yet, or you don't want +to allocate the resources (file descriptors, memory, etc.) for them right away? +Well, in that case you can simply provide a callback that supplies the stream +by calling a `next()` function: + +``` javascript +var CombinedStream = require('combined-stream'); +var fs = require('fs'); + +var combinedStream = CombinedStream.create(); +combinedStream.append(function(next) { + next(fs.createReadStream('file1.txt')); +}); +combinedStream.append(function(next) { + next(fs.createReadStream('file2.txt')); +}); + +combinedStream.pipe(fs.createWriteStream('combined.txt')); +``` + +## API + +### CombinedStream.create([options]) + +Returns a new combined stream object. Available options are: + +* `maxDataSize` +* `pauseStreams` + +The effect of those options is described below. + +### combinedStream.pauseStreams = `true` + +Whether to apply back pressure to the underlaying streams. If set to `false`, +the underlaying streams will never be paused. If set to `true`, the +underlaying streams will be paused right after being appended, as well as when +`delayedStream.pipe()` wants to throttle. + +### combinedStream.maxDataSize = `2 * 1024 * 1024` + +The maximum amount of bytes (or characters) to buffer for all source streams. +If this value is exceeded, `combinedStream` emits an `'error'` event. + +### combinedStream.dataSize = `0` + +The amount of bytes (or characters) currently buffered by `combinedStream`. + +### combinedStream.append(stream) + +Appends the given `stream` to the combinedStream object. If `pauseStreams` is +set to `true, this stream will also be paused right away. + +`streams` can also be a function that takes one parameter called `next`. `next` +is a function that must be invoked in order to provide the `next` stream, see +example above. + +Regardless of how the `stream` is appended, combined-stream always attaches an +`'error'` listener to it, so you don't have to do that manually. + +Special case: `stream` can also be a String or Buffer. + +### combinedStream.write(data) + +You should not call this, `combinedStream` takes care of piping the appended +streams into itself for you. + +### combinedStream.resume() + +Causes `combinedStream` to start drain the streams it manages. The function is +idempotent, and also emits a `'resume'` event each time which usually goes to +the stream that is currently being drained. + +### combinedStream.pause(); + +If `combinedStream.pauseStreams` is set to `false`, this does nothing. +Otherwise a `'pause'` event is emitted, this goes to the stream that is +currently being drained, so you can use it to apply back pressure. + +### combinedStream.end(); + +Sets `combinedStream.writable` to false, emits an `'end'` event, and removes +all streams from the queue. + +### combinedStream.destroy(); + +Same as `combinedStream.end()`, except it emits a `'close'` event instead of +`'end'`. + +## License + +combined-stream is licensed under the MIT license. diff --git a/docs/js/node_modules/combined-stream/lib/combined_stream.js b/docs/js/node_modules/combined-stream/lib/combined_stream.js new file mode 100644 index 000000000..125f097f3 --- /dev/null +++ b/docs/js/node_modules/combined-stream/lib/combined_stream.js @@ -0,0 +1,208 @@ +var util = require('util'); +var Stream = require('stream').Stream; +var DelayedStream = require('delayed-stream'); + +module.exports = CombinedStream; +function CombinedStream() { + this.writable = false; + this.readable = true; + this.dataSize = 0; + this.maxDataSize = 2 * 1024 * 1024; + this.pauseStreams = true; + + this._released = false; + this._streams = []; + this._currentStream = null; + this._insideLoop = false; + this._pendingNext = false; +} +util.inherits(CombinedStream, Stream); + +CombinedStream.create = function(options) { + var combinedStream = new this(); + + options = options || {}; + for (var option in options) { + combinedStream[option] = options[option]; + } + + return combinedStream; +}; + +CombinedStream.isStreamLike = function(stream) { + return (typeof stream !== 'function') + && (typeof stream !== 'string') + && (typeof stream !== 'boolean') + && (typeof stream !== 'number') + && (!Buffer.isBuffer(stream)); +}; + +CombinedStream.prototype.append = function(stream) { + var isStreamLike = CombinedStream.isStreamLike(stream); + + if (isStreamLike) { + if (!(stream instanceof DelayedStream)) { + var newStream = DelayedStream.create(stream, { + maxDataSize: Infinity, + pauseStream: this.pauseStreams, + }); + stream.on('data', this._checkDataSize.bind(this)); + stream = newStream; + } + + this._handleErrors(stream); + + if (this.pauseStreams) { + stream.pause(); + } + } + + this._streams.push(stream); + return this; +}; + +CombinedStream.prototype.pipe = function(dest, options) { + Stream.prototype.pipe.call(this, dest, options); + this.resume(); + return dest; +}; + +CombinedStream.prototype._getNext = function() { + this._currentStream = null; + + if (this._insideLoop) { + this._pendingNext = true; + return; // defer call + } + + this._insideLoop = true; + try { + do { + this._pendingNext = false; + this._realGetNext(); + } while (this._pendingNext); + } finally { + this._insideLoop = false; + } +}; + +CombinedStream.prototype._realGetNext = function() { + var stream = this._streams.shift(); + + + if (typeof stream == 'undefined') { + this.end(); + return; + } + + if (typeof stream !== 'function') { + this._pipeNext(stream); + return; + } + + var getStream = stream; + getStream(function(stream) { + var isStreamLike = CombinedStream.isStreamLike(stream); + if (isStreamLike) { + stream.on('data', this._checkDataSize.bind(this)); + this._handleErrors(stream); + } + + this._pipeNext(stream); + }.bind(this)); +}; + +CombinedStream.prototype._pipeNext = function(stream) { + this._currentStream = stream; + + var isStreamLike = CombinedStream.isStreamLike(stream); + if (isStreamLike) { + stream.on('end', this._getNext.bind(this)); + stream.pipe(this, {end: false}); + return; + } + + var value = stream; + this.write(value); + this._getNext(); +}; + +CombinedStream.prototype._handleErrors = function(stream) { + var self = this; + stream.on('error', function(err) { + self._emitError(err); + }); +}; + +CombinedStream.prototype.write = function(data) { + this.emit('data', data); +}; + +CombinedStream.prototype.pause = function() { + if (!this.pauseStreams) { + return; + } + + if(this.pauseStreams && this._currentStream && typeof(this._currentStream.pause) == 'function') this._currentStream.pause(); + this.emit('pause'); +}; + +CombinedStream.prototype.resume = function() { + if (!this._released) { + this._released = true; + this.writable = true; + this._getNext(); + } + + if(this.pauseStreams && this._currentStream && typeof(this._currentStream.resume) == 'function') this._currentStream.resume(); + this.emit('resume'); +}; + +CombinedStream.prototype.end = function() { + this._reset(); + this.emit('end'); +}; + +CombinedStream.prototype.destroy = function() { + this._reset(); + this.emit('close'); +}; + +CombinedStream.prototype._reset = function() { + this.writable = false; + this._streams = []; + this._currentStream = null; +}; + +CombinedStream.prototype._checkDataSize = function() { + this._updateDataSize(); + if (this.dataSize <= this.maxDataSize) { + return; + } + + var message = + 'DelayedStream#maxDataSize of ' + this.maxDataSize + ' bytes exceeded.'; + this._emitError(new Error(message)); +}; + +CombinedStream.prototype._updateDataSize = function() { + this.dataSize = 0; + + var self = this; + this._streams.forEach(function(stream) { + if (!stream.dataSize) { + return; + } + + self.dataSize += stream.dataSize; + }); + + if (this._currentStream && this._currentStream.dataSize) { + this.dataSize += this._currentStream.dataSize; + } +}; + +CombinedStream.prototype._emitError = function(err) { + this._reset(); + this.emit('error', err); +}; diff --git a/docs/js/node_modules/combined-stream/package.json b/docs/js/node_modules/combined-stream/package.json new file mode 100644 index 000000000..2ce5cc883 --- /dev/null +++ b/docs/js/node_modules/combined-stream/package.json @@ -0,0 +1,58 @@ +{ + "_from": "combined-stream@~1.0.6", + "_id": "combined-stream@1.0.8", + "_inBundle": false, + "_integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "_location": "/combined-stream", + "_phantomChildren": {}, + "_requested": { + "type": "range", + "registry": true, + "raw": "combined-stream@~1.0.6", + "name": "combined-stream", + "escapedName": "combined-stream", + "rawSpec": "~1.0.6", + "saveSpec": null, + "fetchSpec": "~1.0.6" + }, + "_requiredBy": [ + "/form-data", + "/request" + ], + "_resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "_shasum": "c3d45a8b34fd730631a110a8a2520682b31d5a7f", + "_spec": "combined-stream@~1.0.6", + "_where": "/Users/hectorip/Education/Eloquent-JavaScript-ES/node_modules/request", + "author": { + "name": "Felix Geisendörfer", + "email": "felix@debuggable.com", + "url": "http://debuggable.com/" + }, + "bugs": { + "url": "https://github.com/felixge/node-combined-stream/issues" + }, + "bundleDependencies": false, + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "deprecated": false, + "description": "A stream that emits multiple other streams one after another.", + "devDependencies": { + "far": "~0.0.7" + }, + "engines": { + "node": ">= 0.8" + }, + "homepage": "https://github.com/felixge/node-combined-stream", + "license": "MIT", + "main": "./lib/combined_stream", + "name": "combined-stream", + "repository": { + "type": "git", + "url": "git://github.com/felixge/node-combined-stream.git" + }, + "scripts": { + "test": "node test/run.js" + }, + "version": "1.0.8" +} diff --git a/docs/js/node_modules/combined-stream/yarn.lock b/docs/js/node_modules/combined-stream/yarn.lock new file mode 100644 index 000000000..7edf41840 --- /dev/null +++ b/docs/js/node_modules/combined-stream/yarn.lock @@ -0,0 +1,17 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + +far@~0.0.7: + version "0.0.7" + resolved "https://registry.yarnpkg.com/far/-/far-0.0.7.tgz#01c1fd362bcd26ce9cf161af3938aa34619f79a7" + dependencies: + oop "0.0.3" + +oop@0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/oop/-/oop-0.0.3.tgz#70fa405a5650891a194fdc82ca68dad6dabf4401" diff --git a/docs/js/node_modules/core-util-is/LICENSE b/docs/js/node_modules/core-util-is/LICENSE new file mode 100644 index 000000000..d8d7f9437 --- /dev/null +++ b/docs/js/node_modules/core-util-is/LICENSE @@ -0,0 +1,19 @@ +Copyright Node.js contributors. All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +IN THE SOFTWARE. diff --git a/docs/js/node_modules/core-util-is/README.md b/docs/js/node_modules/core-util-is/README.md new file mode 100644 index 000000000..5a76b4149 --- /dev/null +++ b/docs/js/node_modules/core-util-is/README.md @@ -0,0 +1,3 @@ +# core-util-is + +The `util.is*` functions introduced in Node v0.12. diff --git a/docs/js/node_modules/core-util-is/float.patch b/docs/js/node_modules/core-util-is/float.patch new file mode 100644 index 000000000..a06d5c05f --- /dev/null +++ b/docs/js/node_modules/core-util-is/float.patch @@ -0,0 +1,604 @@ +diff --git a/lib/util.js b/lib/util.js +index a03e874..9074e8e 100644 +--- a/lib/util.js ++++ b/lib/util.js +@@ -19,430 +19,6 @@ + // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + // USE OR OTHER DEALINGS IN THE SOFTWARE. + +-var formatRegExp = /%[sdj%]/g; +-exports.format = function(f) { +- if (!isString(f)) { +- var objects = []; +- for (var i = 0; i < arguments.length; i++) { +- objects.push(inspect(arguments[i])); +- } +- return objects.join(' '); +- } +- +- var i = 1; +- var args = arguments; +- var len = args.length; +- var str = String(f).replace(formatRegExp, function(x) { +- if (x === '%%') return '%'; +- if (i >= len) return x; +- switch (x) { +- case '%s': return String(args[i++]); +- case '%d': return Number(args[i++]); +- case '%j': +- try { +- return JSON.stringify(args[i++]); +- } catch (_) { +- return '[Circular]'; +- } +- default: +- return x; +- } +- }); +- for (var x = args[i]; i < len; x = args[++i]) { +- if (isNull(x) || !isObject(x)) { +- str += ' ' + x; +- } else { +- str += ' ' + inspect(x); +- } +- } +- return str; +-}; +- +- +-// Mark that a method should not be used. +-// Returns a modified function which warns once by default. +-// If --no-deprecation is set, then it is a no-op. +-exports.deprecate = function(fn, msg) { +- // Allow for deprecating things in the process of starting up. +- if (isUndefined(global.process)) { +- return function() { +- return exports.deprecate(fn, msg).apply(this, arguments); +- }; +- } +- +- if (process.noDeprecation === true) { +- return fn; +- } +- +- var warned = false; +- function deprecated() { +- if (!warned) { +- if (process.throwDeprecation) { +- throw new Error(msg); +- } else if (process.traceDeprecation) { +- console.trace(msg); +- } else { +- console.error(msg); +- } +- warned = true; +- } +- return fn.apply(this, arguments); +- } +- +- return deprecated; +-}; +- +- +-var debugs = {}; +-var debugEnviron; +-exports.debuglog = function(set) { +- if (isUndefined(debugEnviron)) +- debugEnviron = process.env.NODE_DEBUG || ''; +- set = set.toUpperCase(); +- if (!debugs[set]) { +- if (new RegExp('\\b' + set + '\\b', 'i').test(debugEnviron)) { +- var pid = process.pid; +- debugs[set] = function() { +- var msg = exports.format.apply(exports, arguments); +- console.error('%s %d: %s', set, pid, msg); +- }; +- } else { +- debugs[set] = function() {}; +- } +- } +- return debugs[set]; +-}; +- +- +-/** +- * Echos the value of a value. Trys to print the value out +- * in the best way possible given the different types. +- * +- * @param {Object} obj The object to print out. +- * @param {Object} opts Optional options object that alters the output. +- */ +-/* legacy: obj, showHidden, depth, colors*/ +-function inspect(obj, opts) { +- // default options +- var ctx = { +- seen: [], +- stylize: stylizeNoColor +- }; +- // legacy... +- if (arguments.length >= 3) ctx.depth = arguments[2]; +- if (arguments.length >= 4) ctx.colors = arguments[3]; +- if (isBoolean(opts)) { +- // legacy... +- ctx.showHidden = opts; +- } else if (opts) { +- // got an "options" object +- exports._extend(ctx, opts); +- } +- // set default options +- if (isUndefined(ctx.showHidden)) ctx.showHidden = false; +- if (isUndefined(ctx.depth)) ctx.depth = 2; +- if (isUndefined(ctx.colors)) ctx.colors = false; +- if (isUndefined(ctx.customInspect)) ctx.customInspect = true; +- if (ctx.colors) ctx.stylize = stylizeWithColor; +- return formatValue(ctx, obj, ctx.depth); +-} +-exports.inspect = inspect; +- +- +-// http://en.wikipedia.org/wiki/ANSI_escape_code#graphics +-inspect.colors = { +- 'bold' : [1, 22], +- 'italic' : [3, 23], +- 'underline' : [4, 24], +- 'inverse' : [7, 27], +- 'white' : [37, 39], +- 'grey' : [90, 39], +- 'black' : [30, 39], +- 'blue' : [34, 39], +- 'cyan' : [36, 39], +- 'green' : [32, 39], +- 'magenta' : [35, 39], +- 'red' : [31, 39], +- 'yellow' : [33, 39] +-}; +- +-// Don't use 'blue' not visible on cmd.exe +-inspect.styles = { +- 'special': 'cyan', +- 'number': 'yellow', +- 'boolean': 'yellow', +- 'undefined': 'grey', +- 'null': 'bold', +- 'string': 'green', +- 'date': 'magenta', +- // "name": intentionally not styling +- 'regexp': 'red' +-}; +- +- +-function stylizeWithColor(str, styleType) { +- var style = inspect.styles[styleType]; +- +- if (style) { +- return '\u001b[' + inspect.colors[style][0] + 'm' + str + +- '\u001b[' + inspect.colors[style][1] + 'm'; +- } else { +- return str; +- } +-} +- +- +-function stylizeNoColor(str, styleType) { +- return str; +-} +- +- +-function arrayToHash(array) { +- var hash = {}; +- +- array.forEach(function(val, idx) { +- hash[val] = true; +- }); +- +- return hash; +-} +- +- +-function formatValue(ctx, value, recurseTimes) { +- // Provide a hook for user-specified inspect functions. +- // Check that value is an object with an inspect function on it +- if (ctx.customInspect && +- value && +- isFunction(value.inspect) && +- // Filter out the util module, it's inspect function is special +- value.inspect !== exports.inspect && +- // Also filter out any prototype objects using the circular check. +- !(value.constructor && value.constructor.prototype === value)) { +- var ret = value.inspect(recurseTimes, ctx); +- if (!isString(ret)) { +- ret = formatValue(ctx, ret, recurseTimes); +- } +- return ret; +- } +- +- // Primitive types cannot have properties +- var primitive = formatPrimitive(ctx, value); +- if (primitive) { +- return primitive; +- } +- +- // Look up the keys of the object. +- var keys = Object.keys(value); +- var visibleKeys = arrayToHash(keys); +- +- if (ctx.showHidden) { +- keys = Object.getOwnPropertyNames(value); +- } +- +- // Some type of object without properties can be shortcutted. +- if (keys.length === 0) { +- if (isFunction(value)) { +- var name = value.name ? ': ' + value.name : ''; +- return ctx.stylize('[Function' + name + ']', 'special'); +- } +- if (isRegExp(value)) { +- return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp'); +- } +- if (isDate(value)) { +- return ctx.stylize(Date.prototype.toString.call(value), 'date'); +- } +- if (isError(value)) { +- return formatError(value); +- } +- } +- +- var base = '', array = false, braces = ['{', '}']; +- +- // Make Array say that they are Array +- if (isArray(value)) { +- array = true; +- braces = ['[', ']']; +- } +- +- // Make functions say that they are functions +- if (isFunction(value)) { +- var n = value.name ? ': ' + value.name : ''; +- base = ' [Function' + n + ']'; +- } +- +- // Make RegExps say that they are RegExps +- if (isRegExp(value)) { +- base = ' ' + RegExp.prototype.toString.call(value); +- } +- +- // Make dates with properties first say the date +- if (isDate(value)) { +- base = ' ' + Date.prototype.toUTCString.call(value); +- } +- +- // Make error with message first say the error +- if (isError(value)) { +- base = ' ' + formatError(value); +- } +- +- if (keys.length === 0 && (!array || value.length == 0)) { +- return braces[0] + base + braces[1]; +- } +- +- if (recurseTimes < 0) { +- if (isRegExp(value)) { +- return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp'); +- } else { +- return ctx.stylize('[Object]', 'special'); +- } +- } +- +- ctx.seen.push(value); +- +- var output; +- if (array) { +- output = formatArray(ctx, value, recurseTimes, visibleKeys, keys); +- } else { +- output = keys.map(function(key) { +- return formatProperty(ctx, value, recurseTimes, visibleKeys, key, array); +- }); +- } +- +- ctx.seen.pop(); +- +- return reduceToSingleString(output, base, braces); +-} +- +- +-function formatPrimitive(ctx, value) { +- if (isUndefined(value)) +- return ctx.stylize('undefined', 'undefined'); +- if (isString(value)) { +- var simple = '\'' + JSON.stringify(value).replace(/^"|"$/g, '') +- .replace(/'/g, "\\'") +- .replace(/\\"/g, '"') + '\''; +- return ctx.stylize(simple, 'string'); +- } +- if (isNumber(value)) { +- // Format -0 as '-0'. Strict equality won't distinguish 0 from -0, +- // so instead we use the fact that 1 / -0 < 0 whereas 1 / 0 > 0 . +- if (value === 0 && 1 / value < 0) +- return ctx.stylize('-0', 'number'); +- return ctx.stylize('' + value, 'number'); +- } +- if (isBoolean(value)) +- return ctx.stylize('' + value, 'boolean'); +- // For some reason typeof null is "object", so special case here. +- if (isNull(value)) +- return ctx.stylize('null', 'null'); +-} +- +- +-function formatError(value) { +- return '[' + Error.prototype.toString.call(value) + ']'; +-} +- +- +-function formatArray(ctx, value, recurseTimes, visibleKeys, keys) { +- var output = []; +- for (var i = 0, l = value.length; i < l; ++i) { +- if (hasOwnProperty(value, String(i))) { +- output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, +- String(i), true)); +- } else { +- output.push(''); +- } +- } +- keys.forEach(function(key) { +- if (!key.match(/^\d+$/)) { +- output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, +- key, true)); +- } +- }); +- return output; +-} +- +- +-function formatProperty(ctx, value, recurseTimes, visibleKeys, key, array) { +- var name, str, desc; +- desc = Object.getOwnPropertyDescriptor(value, key) || { value: value[key] }; +- if (desc.get) { +- if (desc.set) { +- str = ctx.stylize('[Getter/Setter]', 'special'); +- } else { +- str = ctx.stylize('[Getter]', 'special'); +- } +- } else { +- if (desc.set) { +- str = ctx.stylize('[Setter]', 'special'); +- } +- } +- if (!hasOwnProperty(visibleKeys, key)) { +- name = '[' + key + ']'; +- } +- if (!str) { +- if (ctx.seen.indexOf(desc.value) < 0) { +- if (isNull(recurseTimes)) { +- str = formatValue(ctx, desc.value, null); +- } else { +- str = formatValue(ctx, desc.value, recurseTimes - 1); +- } +- if (str.indexOf('\n') > -1) { +- if (array) { +- str = str.split('\n').map(function(line) { +- return ' ' + line; +- }).join('\n').substr(2); +- } else { +- str = '\n' + str.split('\n').map(function(line) { +- return ' ' + line; +- }).join('\n'); +- } +- } +- } else { +- str = ctx.stylize('[Circular]', 'special'); +- } +- } +- if (isUndefined(name)) { +- if (array && key.match(/^\d+$/)) { +- return str; +- } +- name = JSON.stringify('' + key); +- if (name.match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)) { +- name = name.substr(1, name.length - 2); +- name = ctx.stylize(name, 'name'); +- } else { +- name = name.replace(/'/g, "\\'") +- .replace(/\\"/g, '"') +- .replace(/(^"|"$)/g, "'"); +- name = ctx.stylize(name, 'string'); +- } +- } +- +- return name + ': ' + str; +-} +- +- +-function reduceToSingleString(output, base, braces) { +- var numLinesEst = 0; +- var length = output.reduce(function(prev, cur) { +- numLinesEst++; +- if (cur.indexOf('\n') >= 0) numLinesEst++; +- return prev + cur.replace(/\u001b\[\d\d?m/g, '').length + 1; +- }, 0); +- +- if (length > 60) { +- return braces[0] + +- (base === '' ? '' : base + '\n ') + +- ' ' + +- output.join(',\n ') + +- ' ' + +- braces[1]; +- } +- +- return braces[0] + base + ' ' + output.join(', ') + ' ' + braces[1]; +-} +- +- + // NOTE: These type checking functions intentionally don't use `instanceof` + // because it is fragile and can be easily faked with `Object.create()`. + function isArray(ar) { +@@ -522,166 +98,10 @@ function isPrimitive(arg) { + exports.isPrimitive = isPrimitive; + + function isBuffer(arg) { +- return arg instanceof Buffer; ++ return Buffer.isBuffer(arg); + } + exports.isBuffer = isBuffer; + + function objectToString(o) { + return Object.prototype.toString.call(o); +-} +- +- +-function pad(n) { +- return n < 10 ? '0' + n.toString(10) : n.toString(10); +-} +- +- +-var months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', +- 'Oct', 'Nov', 'Dec']; +- +-// 26 Feb 16:19:34 +-function timestamp() { +- var d = new Date(); +- var time = [pad(d.getHours()), +- pad(d.getMinutes()), +- pad(d.getSeconds())].join(':'); +- return [d.getDate(), months[d.getMonth()], time].join(' '); +-} +- +- +-// log is just a thin wrapper to console.log that prepends a timestamp +-exports.log = function() { +- console.log('%s - %s', timestamp(), exports.format.apply(exports, arguments)); +-}; +- +- +-/** +- * Inherit the prototype methods from one constructor into another. +- * +- * The Function.prototype.inherits from lang.js rewritten as a standalone +- * function (not on Function.prototype). NOTE: If this file is to be loaded +- * during bootstrapping this function needs to be rewritten using some native +- * functions as prototype setup using normal JavaScript does not work as +- * expected during bootstrapping (see mirror.js in r114903). +- * +- * @param {function} ctor Constructor function which needs to inherit the +- * prototype. +- * @param {function} superCtor Constructor function to inherit prototype from. +- */ +-exports.inherits = function(ctor, superCtor) { +- ctor.super_ = superCtor; +- ctor.prototype = Object.create(superCtor.prototype, { +- constructor: { +- value: ctor, +- enumerable: false, +- writable: true, +- configurable: true +- } +- }); +-}; +- +-exports._extend = function(origin, add) { +- // Don't do anything if add isn't an object +- if (!add || !isObject(add)) return origin; +- +- var keys = Object.keys(add); +- var i = keys.length; +- while (i--) { +- origin[keys[i]] = add[keys[i]]; +- } +- return origin; +-}; +- +-function hasOwnProperty(obj, prop) { +- return Object.prototype.hasOwnProperty.call(obj, prop); +-} +- +- +-// Deprecated old stuff. +- +-exports.p = exports.deprecate(function() { +- for (var i = 0, len = arguments.length; i < len; ++i) { +- console.error(exports.inspect(arguments[i])); +- } +-}, 'util.p: Use console.error() instead'); +- +- +-exports.exec = exports.deprecate(function() { +- return require('child_process').exec.apply(this, arguments); +-}, 'util.exec is now called `child_process.exec`.'); +- +- +-exports.print = exports.deprecate(function() { +- for (var i = 0, len = arguments.length; i < len; ++i) { +- process.stdout.write(String(arguments[i])); +- } +-}, 'util.print: Use console.log instead'); +- +- +-exports.puts = exports.deprecate(function() { +- for (var i = 0, len = arguments.length; i < len; ++i) { +- process.stdout.write(arguments[i] + '\n'); +- } +-}, 'util.puts: Use console.log instead'); +- +- +-exports.debug = exports.deprecate(function(x) { +- process.stderr.write('DEBUG: ' + x + '\n'); +-}, 'util.debug: Use console.error instead'); +- +- +-exports.error = exports.deprecate(function(x) { +- for (var i = 0, len = arguments.length; i < len; ++i) { +- process.stderr.write(arguments[i] + '\n'); +- } +-}, 'util.error: Use console.error instead'); +- +- +-exports.pump = exports.deprecate(function(readStream, writeStream, callback) { +- var callbackCalled = false; +- +- function call(a, b, c) { +- if (callback && !callbackCalled) { +- callback(a, b, c); +- callbackCalled = true; +- } +- } +- +- readStream.addListener('data', function(chunk) { +- if (writeStream.write(chunk) === false) readStream.pause(); +- }); +- +- writeStream.addListener('drain', function() { +- readStream.resume(); +- }); +- +- readStream.addListener('end', function() { +- writeStream.end(); +- }); +- +- readStream.addListener('close', function() { +- call(); +- }); +- +- readStream.addListener('error', function(err) { +- writeStream.end(); +- call(err); +- }); +- +- writeStream.addListener('error', function(err) { +- readStream.destroy(); +- call(err); +- }); +-}, 'util.pump(): Use readableStream.pipe() instead'); +- +- +-var uv; +-exports._errnoException = function(err, syscall) { +- if (isUndefined(uv)) uv = process.binding('uv'); +- var errname = uv.errname(err); +- var e = new Error(syscall + ' ' + errname); +- e.code = errname; +- e.errno = errname; +- e.syscall = syscall; +- return e; +-}; ++} \ No newline at end of file diff --git a/docs/js/node_modules/core-util-is/lib/util.js b/docs/js/node_modules/core-util-is/lib/util.js new file mode 100644 index 000000000..ff4c851c0 --- /dev/null +++ b/docs/js/node_modules/core-util-is/lib/util.js @@ -0,0 +1,107 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +// NOTE: These type checking functions intentionally don't use `instanceof` +// because it is fragile and can be easily faked with `Object.create()`. + +function isArray(arg) { + if (Array.isArray) { + return Array.isArray(arg); + } + return objectToString(arg) === '[object Array]'; +} +exports.isArray = isArray; + +function isBoolean(arg) { + return typeof arg === 'boolean'; +} +exports.isBoolean = isBoolean; + +function isNull(arg) { + return arg === null; +} +exports.isNull = isNull; + +function isNullOrUndefined(arg) { + return arg == null; +} +exports.isNullOrUndefined = isNullOrUndefined; + +function isNumber(arg) { + return typeof arg === 'number'; +} +exports.isNumber = isNumber; + +function isString(arg) { + return typeof arg === 'string'; +} +exports.isString = isString; + +function isSymbol(arg) { + return typeof arg === 'symbol'; +} +exports.isSymbol = isSymbol; + +function isUndefined(arg) { + return arg === void 0; +} +exports.isUndefined = isUndefined; + +function isRegExp(re) { + return objectToString(re) === '[object RegExp]'; +} +exports.isRegExp = isRegExp; + +function isObject(arg) { + return typeof arg === 'object' && arg !== null; +} +exports.isObject = isObject; + +function isDate(d) { + return objectToString(d) === '[object Date]'; +} +exports.isDate = isDate; + +function isError(e) { + return (objectToString(e) === '[object Error]' || e instanceof Error); +} +exports.isError = isError; + +function isFunction(arg) { + return typeof arg === 'function'; +} +exports.isFunction = isFunction; + +function isPrimitive(arg) { + return arg === null || + typeof arg === 'boolean' || + typeof arg === 'number' || + typeof arg === 'string' || + typeof arg === 'symbol' || // ES6 symbol + typeof arg === 'undefined'; +} +exports.isPrimitive = isPrimitive; + +exports.isBuffer = Buffer.isBuffer; + +function objectToString(o) { + return Object.prototype.toString.call(o); +} diff --git a/docs/js/node_modules/core-util-is/package.json b/docs/js/node_modules/core-util-is/package.json new file mode 100644 index 000000000..afcb2b61d --- /dev/null +++ b/docs/js/node_modules/core-util-is/package.json @@ -0,0 +1,62 @@ +{ + "_from": "core-util-is@1.0.2", + "_id": "core-util-is@1.0.2", + "_inBundle": false, + "_integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "_location": "/core-util-is", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "core-util-is@1.0.2", + "name": "core-util-is", + "escapedName": "core-util-is", + "rawSpec": "1.0.2", + "saveSpec": null, + "fetchSpec": "1.0.2" + }, + "_requiredBy": [ + "/verror" + ], + "_resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "_shasum": "b5fd54220aa2bc5ab57aab7140c940754503c1a7", + "_spec": "core-util-is@1.0.2", + "_where": "/Users/hectorip/Education/Eloquent-JavaScript-ES/node_modules/verror", + "author": { + "name": "Isaac Z. Schlueter", + "email": "i@izs.me", + "url": "http://blog.izs.me/" + }, + "bugs": { + "url": "https://github.com/isaacs/core-util-is/issues" + }, + "bundleDependencies": false, + "deprecated": false, + "description": "The `util.is*` functions introduced in Node v0.12.", + "devDependencies": { + "tap": "^2.3.0" + }, + "homepage": "https://github.com/isaacs/core-util-is#readme", + "keywords": [ + "util", + "isBuffer", + "isArray", + "isNumber", + "isString", + "isRegExp", + "isThis", + "isThat", + "polyfill" + ], + "license": "MIT", + "main": "lib/util.js", + "name": "core-util-is", + "repository": { + "type": "git", + "url": "git://github.com/isaacs/core-util-is.git" + }, + "scripts": { + "test": "tap test.js" + }, + "version": "1.0.2" +} diff --git a/docs/js/node_modules/core-util-is/test.js b/docs/js/node_modules/core-util-is/test.js new file mode 100644 index 000000000..1a490c65a --- /dev/null +++ b/docs/js/node_modules/core-util-is/test.js @@ -0,0 +1,68 @@ +var assert = require('tap'); + +var t = require('./lib/util'); + +assert.equal(t.isArray([]), true); +assert.equal(t.isArray({}), false); + +assert.equal(t.isBoolean(null), false); +assert.equal(t.isBoolean(true), true); +assert.equal(t.isBoolean(false), true); + +assert.equal(t.isNull(null), true); +assert.equal(t.isNull(undefined), false); +assert.equal(t.isNull(false), false); +assert.equal(t.isNull(), false); + +assert.equal(t.isNullOrUndefined(null), true); +assert.equal(t.isNullOrUndefined(undefined), true); +assert.equal(t.isNullOrUndefined(false), false); +assert.equal(t.isNullOrUndefined(), true); + +assert.equal(t.isNumber(null), false); +assert.equal(t.isNumber('1'), false); +assert.equal(t.isNumber(1), true); + +assert.equal(t.isString(null), false); +assert.equal(t.isString('1'), true); +assert.equal(t.isString(1), false); + +assert.equal(t.isSymbol(null), false); +assert.equal(t.isSymbol('1'), false); +assert.equal(t.isSymbol(1), false); +assert.equal(t.isSymbol(Symbol()), true); + +assert.equal(t.isUndefined(null), false); +assert.equal(t.isUndefined(undefined), true); +assert.equal(t.isUndefined(false), false); +assert.equal(t.isUndefined(), true); + +assert.equal(t.isRegExp(null), false); +assert.equal(t.isRegExp('1'), false); +assert.equal(t.isRegExp(new RegExp()), true); + +assert.equal(t.isObject({}), true); +assert.equal(t.isObject([]), true); +assert.equal(t.isObject(new RegExp()), true); +assert.equal(t.isObject(new Date()), true); + +assert.equal(t.isDate(null), false); +assert.equal(t.isDate('1'), false); +assert.equal(t.isDate(new Date()), true); + +assert.equal(t.isError(null), false); +assert.equal(t.isError({ err: true }), false); +assert.equal(t.isError(new Error()), true); + +assert.equal(t.isFunction(null), false); +assert.equal(t.isFunction({ }), false); +assert.equal(t.isFunction(function() {}), true); + +assert.equal(t.isPrimitive(null), true); +assert.equal(t.isPrimitive(''), true); +assert.equal(t.isPrimitive(0), true); +assert.equal(t.isPrimitive(new Date()), false); + +assert.equal(t.isBuffer(null), false); +assert.equal(t.isBuffer({}), false); +assert.equal(t.isBuffer(new Buffer(0)), true); diff --git a/docs/js/node_modules/cssom/LICENSE.txt b/docs/js/node_modules/cssom/LICENSE.txt new file mode 100644 index 000000000..bc57aacdd --- /dev/null +++ b/docs/js/node_modules/cssom/LICENSE.txt @@ -0,0 +1,20 @@ +Copyright (c) Nikita Vasilyev + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/docs/js/node_modules/cssom/README.mdown b/docs/js/node_modules/cssom/README.mdown new file mode 100644 index 000000000..83af16baa --- /dev/null +++ b/docs/js/node_modules/cssom/README.mdown @@ -0,0 +1,67 @@ +# CSSOM + +CSSOM.js is a CSS parser written in pure JavaScript. It is also a partial implementation of [CSS Object Model](http://dev.w3.org/csswg/cssom/). + + CSSOM.parse("body {color: black}") + -> { + cssRules: [ + { + selectorText: "body", + style: { + 0: "color", + color: "black", + length: 1 + } + } + ] + } + + +## [Parser demo](http://nv.github.com/CSSOM/docs/parse.html) + +Works well in Google Chrome 6+, Safari 5+, Firefox 3.6+, Opera 10.63+. +Doesn't work in IE < 9 because of unsupported getters/setters. + +To use CSSOM.js in the browser you might want to build a one-file version that exposes a single `CSSOM` global variable: + + ➤ git clone https://github.com/NV/CSSOM.git + ➤ cd CSSOM + ➤ node build.js + build/CSSOM.js is done + +To use it with Node.js or any other CommonJS loader: + + ➤ npm install cssom + +## Don’t use it if... + +You parse CSS to mungle, minify or reformat code like this: + +```css +div { + background: gray; + background: linear-gradient(to bottom, white 0%, black 100%); +} +``` + +This pattern is often used to give browsers that don’t understand linear gradients a fallback solution (e.g. gray color in the example). +In CSSOM, `background: gray` [gets overwritten](http://nv.github.io/CSSOM/docs/parse.html#css=div%20%7B%0A%20%20%20%20%20%20background%3A%20gray%3B%0A%20%20%20%20background%3A%20linear-gradient(to%20bottom%2C%20white%200%25%2C%20black%20100%25)%3B%0A%7D). +It doesn't get preserved. + +If you do CSS mungling, minification, image inlining, and such, CSSOM.js is no good for you, considere using one of the following: + + * [postcss](https://github.com/postcss/postcss) + * [reworkcss/css](https://github.com/reworkcss/css) + * [csso](https://github.com/css/csso) + * [mensch](https://github.com/brettstimmerman/mensch) + + +## [Tests](http://nv.github.com/CSSOM/spec/) + +To run tests locally: + + ➤ git submodule init + ➤ git submodule update + + +## [Who uses CSSOM.js](https://github.com/NV/CSSOM/wiki/Who-uses-CSSOM.js) diff --git a/docs/js/node_modules/cssom/lib/CSSDocumentRule.js b/docs/js/node_modules/cssom/lib/CSSDocumentRule.js new file mode 100644 index 000000000..aec0776e2 --- /dev/null +++ b/docs/js/node_modules/cssom/lib/CSSDocumentRule.js @@ -0,0 +1,39 @@ +//.CommonJS +var CSSOM = { + CSSRule: require("./CSSRule").CSSRule, + MatcherList: require("./MatcherList").MatcherList +}; +///CommonJS + + +/** + * @constructor + * @see https://developer.mozilla.org/en/CSS/@-moz-document + */ +CSSOM.CSSDocumentRule = function CSSDocumentRule() { + CSSOM.CSSRule.call(this); + this.matcher = new CSSOM.MatcherList(); + this.cssRules = []; +}; + +CSSOM.CSSDocumentRule.prototype = new CSSOM.CSSRule(); +CSSOM.CSSDocumentRule.prototype.constructor = CSSOM.CSSDocumentRule; +CSSOM.CSSDocumentRule.prototype.type = 10; +//FIXME +//CSSOM.CSSDocumentRule.prototype.insertRule = CSSStyleSheet.prototype.insertRule; +//CSSOM.CSSDocumentRule.prototype.deleteRule = CSSStyleSheet.prototype.deleteRule; + +Object.defineProperty(CSSOM.CSSDocumentRule.prototype, "cssText", { + get: function() { + var cssTexts = []; + for (var i=0, length=this.cssRules.length; i < length; i++) { + cssTexts.push(this.cssRules[i].cssText); + } + return "@-moz-document " + this.matcher.matcherText + " {" + cssTexts.join("") + "}"; + } +}); + + +//.CommonJS +exports.CSSDocumentRule = CSSOM.CSSDocumentRule; +///CommonJS diff --git a/docs/js/node_modules/cssom/lib/CSSFontFaceRule.js b/docs/js/node_modules/cssom/lib/CSSFontFaceRule.js new file mode 100644 index 000000000..7a537fb0f --- /dev/null +++ b/docs/js/node_modules/cssom/lib/CSSFontFaceRule.js @@ -0,0 +1,36 @@ +//.CommonJS +var CSSOM = { + CSSStyleDeclaration: require("./CSSStyleDeclaration").CSSStyleDeclaration, + CSSRule: require("./CSSRule").CSSRule +}; +///CommonJS + + +/** + * @constructor + * @see http://dev.w3.org/csswg/cssom/#css-font-face-rule + */ +CSSOM.CSSFontFaceRule = function CSSFontFaceRule() { + CSSOM.CSSRule.call(this); + this.style = new CSSOM.CSSStyleDeclaration(); + this.style.parentRule = this; +}; + +CSSOM.CSSFontFaceRule.prototype = new CSSOM.CSSRule(); +CSSOM.CSSFontFaceRule.prototype.constructor = CSSOM.CSSFontFaceRule; +CSSOM.CSSFontFaceRule.prototype.type = 5; +//FIXME +//CSSOM.CSSFontFaceRule.prototype.insertRule = CSSStyleSheet.prototype.insertRule; +//CSSOM.CSSFontFaceRule.prototype.deleteRule = CSSStyleSheet.prototype.deleteRule; + +// http://www.opensource.apple.com/source/WebCore/WebCore-955.66.1/css/WebKitCSSFontFaceRule.cpp +Object.defineProperty(CSSOM.CSSFontFaceRule.prototype, "cssText", { + get: function() { + return "@font-face {" + this.style.cssText + "}"; + } +}); + + +//.CommonJS +exports.CSSFontFaceRule = CSSOM.CSSFontFaceRule; +///CommonJS diff --git a/docs/js/node_modules/cssom/lib/CSSHostRule.js b/docs/js/node_modules/cssom/lib/CSSHostRule.js new file mode 100644 index 000000000..365304fc9 --- /dev/null +++ b/docs/js/node_modules/cssom/lib/CSSHostRule.js @@ -0,0 +1,37 @@ +//.CommonJS +var CSSOM = { + CSSRule: require("./CSSRule").CSSRule +}; +///CommonJS + + +/** + * @constructor + * @see http://www.w3.org/TR/shadow-dom/#host-at-rule + */ +CSSOM.CSSHostRule = function CSSHostRule() { + CSSOM.CSSRule.call(this); + this.cssRules = []; +}; + +CSSOM.CSSHostRule.prototype = new CSSOM.CSSRule(); +CSSOM.CSSHostRule.prototype.constructor = CSSOM.CSSHostRule; +CSSOM.CSSHostRule.prototype.type = 1001; +//FIXME +//CSSOM.CSSHostRule.prototype.insertRule = CSSStyleSheet.prototype.insertRule; +//CSSOM.CSSHostRule.prototype.deleteRule = CSSStyleSheet.prototype.deleteRule; + +Object.defineProperty(CSSOM.CSSHostRule.prototype, "cssText", { + get: function() { + var cssTexts = []; + for (var i=0, length=this.cssRules.length; i < length; i++) { + cssTexts.push(this.cssRules[i].cssText); + } + return "@host {" + cssTexts.join("") + "}"; + } +}); + + +//.CommonJS +exports.CSSHostRule = CSSOM.CSSHostRule; +///CommonJS diff --git a/docs/js/node_modules/cssom/lib/CSSImportRule.js b/docs/js/node_modules/cssom/lib/CSSImportRule.js new file mode 100644 index 000000000..0398105a3 --- /dev/null +++ b/docs/js/node_modules/cssom/lib/CSSImportRule.js @@ -0,0 +1,132 @@ +//.CommonJS +var CSSOM = { + CSSRule: require("./CSSRule").CSSRule, + CSSStyleSheet: require("./CSSStyleSheet").CSSStyleSheet, + MediaList: require("./MediaList").MediaList +}; +///CommonJS + + +/** + * @constructor + * @see http://dev.w3.org/csswg/cssom/#cssimportrule + * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSImportRule + */ +CSSOM.CSSImportRule = function CSSImportRule() { + CSSOM.CSSRule.call(this); + this.href = ""; + this.media = new CSSOM.MediaList(); + this.styleSheet = new CSSOM.CSSStyleSheet(); +}; + +CSSOM.CSSImportRule.prototype = new CSSOM.CSSRule(); +CSSOM.CSSImportRule.prototype.constructor = CSSOM.CSSImportRule; +CSSOM.CSSImportRule.prototype.type = 3; + +Object.defineProperty(CSSOM.CSSImportRule.prototype, "cssText", { + get: function() { + var mediaText = this.media.mediaText; + return "@import url(" + this.href + ")" + (mediaText ? " " + mediaText : "") + ";"; + }, + set: function(cssText) { + var i = 0; + + /** + * @import url(partial.css) screen, handheld; + * || | + * after-import media + * | + * url + */ + var state = ''; + + var buffer = ''; + var index; + for (var character; (character = cssText.charAt(i)); i++) { + + switch (character) { + case ' ': + case '\t': + case '\r': + case '\n': + case '\f': + if (state === 'after-import') { + state = 'url'; + } else { + buffer += character; + } + break; + + case '@': + if (!state && cssText.indexOf('@import', i) === i) { + state = 'after-import'; + i += 'import'.length; + buffer = ''; + } + break; + + case 'u': + if (state === 'url' && cssText.indexOf('url(', i) === i) { + index = cssText.indexOf(')', i + 1); + if (index === -1) { + throw i + ': ")" not found'; + } + i += 'url('.length; + var url = cssText.slice(i, index); + if (url[0] === url[url.length - 1]) { + if (url[0] === '"' || url[0] === "'") { + url = url.slice(1, -1); + } + } + this.href = url; + i = index; + state = 'media'; + } + break; + + case '"': + if (state === 'url') { + index = cssText.indexOf('"', i + 1); + if (!index) { + throw i + ": '\"' not found"; + } + this.href = cssText.slice(i + 1, index); + i = index; + state = 'media'; + } + break; + + case "'": + if (state === 'url') { + index = cssText.indexOf("'", i + 1); + if (!index) { + throw i + ': "\'" not found'; + } + this.href = cssText.slice(i + 1, index); + i = index; + state = 'media'; + } + break; + + case ';': + if (state === 'media') { + if (buffer) { + this.media.mediaText = buffer.trim(); + } + } + break; + + default: + if (state === 'media') { + buffer += character; + } + break; + } + } + } +}); + + +//.CommonJS +exports.CSSImportRule = CSSOM.CSSImportRule; +///CommonJS diff --git a/docs/js/node_modules/cssom/lib/CSSKeyframeRule.js b/docs/js/node_modules/cssom/lib/CSSKeyframeRule.js new file mode 100644 index 000000000..c22f2f5e2 --- /dev/null +++ b/docs/js/node_modules/cssom/lib/CSSKeyframeRule.js @@ -0,0 +1,37 @@ +//.CommonJS +var CSSOM = { + CSSRule: require("./CSSRule").CSSRule, + CSSStyleDeclaration: require('./CSSStyleDeclaration').CSSStyleDeclaration +}; +///CommonJS + + +/** + * @constructor + * @see http://www.w3.org/TR/css3-animations/#DOM-CSSKeyframeRule + */ +CSSOM.CSSKeyframeRule = function CSSKeyframeRule() { + CSSOM.CSSRule.call(this); + this.keyText = ''; + this.style = new CSSOM.CSSStyleDeclaration(); + this.style.parentRule = this; +}; + +CSSOM.CSSKeyframeRule.prototype = new CSSOM.CSSRule(); +CSSOM.CSSKeyframeRule.prototype.constructor = CSSOM.CSSKeyframeRule; +CSSOM.CSSKeyframeRule.prototype.type = 9; +//FIXME +//CSSOM.CSSKeyframeRule.prototype.insertRule = CSSStyleSheet.prototype.insertRule; +//CSSOM.CSSKeyframeRule.prototype.deleteRule = CSSStyleSheet.prototype.deleteRule; + +// http://www.opensource.apple.com/source/WebCore/WebCore-955.66.1/css/WebKitCSSKeyframeRule.cpp +Object.defineProperty(CSSOM.CSSKeyframeRule.prototype, "cssText", { + get: function() { + return this.keyText + " {" + this.style.cssText + "} "; + } +}); + + +//.CommonJS +exports.CSSKeyframeRule = CSSOM.CSSKeyframeRule; +///CommonJS diff --git a/docs/js/node_modules/cssom/lib/CSSKeyframesRule.js b/docs/js/node_modules/cssom/lib/CSSKeyframesRule.js new file mode 100644 index 000000000..7e42717a1 --- /dev/null +++ b/docs/js/node_modules/cssom/lib/CSSKeyframesRule.js @@ -0,0 +1,39 @@ +//.CommonJS +var CSSOM = { + CSSRule: require("./CSSRule").CSSRule +}; +///CommonJS + + +/** + * @constructor + * @see http://www.w3.org/TR/css3-animations/#DOM-CSSKeyframesRule + */ +CSSOM.CSSKeyframesRule = function CSSKeyframesRule() { + CSSOM.CSSRule.call(this); + this.name = ''; + this.cssRules = []; +}; + +CSSOM.CSSKeyframesRule.prototype = new CSSOM.CSSRule(); +CSSOM.CSSKeyframesRule.prototype.constructor = CSSOM.CSSKeyframesRule; +CSSOM.CSSKeyframesRule.prototype.type = 8; +//FIXME +//CSSOM.CSSKeyframesRule.prototype.insertRule = CSSStyleSheet.prototype.insertRule; +//CSSOM.CSSKeyframesRule.prototype.deleteRule = CSSStyleSheet.prototype.deleteRule; + +// http://www.opensource.apple.com/source/WebCore/WebCore-955.66.1/css/WebKitCSSKeyframesRule.cpp +Object.defineProperty(CSSOM.CSSKeyframesRule.prototype, "cssText", { + get: function() { + var cssTexts = []; + for (var i=0, length=this.cssRules.length; i < length; i++) { + cssTexts.push(" " + this.cssRules[i].cssText); + } + return "@" + (this._vendorPrefix || '') + "keyframes " + this.name + " { \n" + cssTexts.join("\n") + "\n}"; + } +}); + + +//.CommonJS +exports.CSSKeyframesRule = CSSOM.CSSKeyframesRule; +///CommonJS diff --git a/docs/js/node_modules/cssom/lib/CSSMediaRule.js b/docs/js/node_modules/cssom/lib/CSSMediaRule.js new file mode 100644 index 000000000..367a35edf --- /dev/null +++ b/docs/js/node_modules/cssom/lib/CSSMediaRule.js @@ -0,0 +1,41 @@ +//.CommonJS +var CSSOM = { + CSSRule: require("./CSSRule").CSSRule, + MediaList: require("./MediaList").MediaList +}; +///CommonJS + + +/** + * @constructor + * @see http://dev.w3.org/csswg/cssom/#cssmediarule + * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSMediaRule + */ +CSSOM.CSSMediaRule = function CSSMediaRule() { + CSSOM.CSSRule.call(this); + this.media = new CSSOM.MediaList(); + this.cssRules = []; +}; + +CSSOM.CSSMediaRule.prototype = new CSSOM.CSSRule(); +CSSOM.CSSMediaRule.prototype.constructor = CSSOM.CSSMediaRule; +CSSOM.CSSMediaRule.prototype.type = 4; +//FIXME +//CSSOM.CSSMediaRule.prototype.insertRule = CSSStyleSheet.prototype.insertRule; +//CSSOM.CSSMediaRule.prototype.deleteRule = CSSStyleSheet.prototype.deleteRule; + +// http://opensource.apple.com/source/WebCore/WebCore-658.28/css/CSSMediaRule.cpp +Object.defineProperty(CSSOM.CSSMediaRule.prototype, "cssText", { + get: function() { + var cssTexts = []; + for (var i=0, length=this.cssRules.length; i < length; i++) { + cssTexts.push(this.cssRules[i].cssText); + } + return "@media " + this.media.mediaText + " {" + cssTexts.join("") + "}"; + } +}); + + +//.CommonJS +exports.CSSMediaRule = CSSOM.CSSMediaRule; +///CommonJS diff --git a/docs/js/node_modules/cssom/lib/CSSOM.js b/docs/js/node_modules/cssom/lib/CSSOM.js new file mode 100644 index 000000000..95f356303 --- /dev/null +++ b/docs/js/node_modules/cssom/lib/CSSOM.js @@ -0,0 +1,3 @@ +var CSSOM = {}; + + diff --git a/docs/js/node_modules/cssom/lib/CSSRule.js b/docs/js/node_modules/cssom/lib/CSSRule.js new file mode 100644 index 000000000..0b5e25b6f --- /dev/null +++ b/docs/js/node_modules/cssom/lib/CSSRule.js @@ -0,0 +1,43 @@ +//.CommonJS +var CSSOM = {}; +///CommonJS + + +/** + * @constructor + * @see http://dev.w3.org/csswg/cssom/#the-cssrule-interface + * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSRule + */ +CSSOM.CSSRule = function CSSRule() { + this.parentRule = null; + this.parentStyleSheet = null; +}; + +CSSOM.CSSRule.UNKNOWN_RULE = 0; // obsolete +CSSOM.CSSRule.STYLE_RULE = 1; +CSSOM.CSSRule.CHARSET_RULE = 2; // obsolete +CSSOM.CSSRule.IMPORT_RULE = 3; +CSSOM.CSSRule.MEDIA_RULE = 4; +CSSOM.CSSRule.FONT_FACE_RULE = 5; +CSSOM.CSSRule.PAGE_RULE = 6; +CSSOM.CSSRule.KEYFRAMES_RULE = 7; +CSSOM.CSSRule.KEYFRAME_RULE = 8; +CSSOM.CSSRule.MARGIN_RULE = 9; +CSSOM.CSSRule.NAMESPACE_RULE = 10; +CSSOM.CSSRule.COUNTER_STYLE_RULE = 11; +CSSOM.CSSRule.SUPPORTS_RULE = 12; +CSSOM.CSSRule.DOCUMENT_RULE = 13; +CSSOM.CSSRule.FONT_FEATURE_VALUES_RULE = 14; +CSSOM.CSSRule.VIEWPORT_RULE = 15; +CSSOM.CSSRule.REGION_STYLE_RULE = 16; + + +CSSOM.CSSRule.prototype = { + constructor: CSSOM.CSSRule + //FIXME +}; + + +//.CommonJS +exports.CSSRule = CSSOM.CSSRule; +///CommonJS diff --git a/docs/js/node_modules/cssom/lib/CSSStyleDeclaration.js b/docs/js/node_modules/cssom/lib/CSSStyleDeclaration.js new file mode 100644 index 000000000..b43b9afa9 --- /dev/null +++ b/docs/js/node_modules/cssom/lib/CSSStyleDeclaration.js @@ -0,0 +1,148 @@ +//.CommonJS +var CSSOM = {}; +///CommonJS + + +/** + * @constructor + * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSStyleDeclaration + */ +CSSOM.CSSStyleDeclaration = function CSSStyleDeclaration(){ + this.length = 0; + this.parentRule = null; + + // NON-STANDARD + this._importants = {}; +}; + + +CSSOM.CSSStyleDeclaration.prototype = { + + constructor: CSSOM.CSSStyleDeclaration, + + /** + * + * @param {string} name + * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSStyleDeclaration-getPropertyValue + * @return {string} the value of the property if it has been explicitly set for this declaration block. + * Returns the empty string if the property has not been set. + */ + getPropertyValue: function(name) { + return this[name] || ""; + }, + + /** + * + * @param {string} name + * @param {string} value + * @param {string} [priority=null] "important" or null + * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSStyleDeclaration-setProperty + */ + setProperty: function(name, value, priority) { + if (this[name]) { + // Property already exist. Overwrite it. + var index = Array.prototype.indexOf.call(this, name); + if (index < 0) { + this[this.length] = name; + this.length++; + } + } else { + // New property. + this[this.length] = name; + this.length++; + } + this[name] = value + ""; + this._importants[name] = priority; + }, + + /** + * + * @param {string} name + * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSStyleDeclaration-removeProperty + * @return {string} the value of the property if it has been explicitly set for this declaration block. + * Returns the empty string if the property has not been set or the property name does not correspond to a known CSS property. + */ + removeProperty: function(name) { + if (!(name in this)) { + return ""; + } + var index = Array.prototype.indexOf.call(this, name); + if (index < 0) { + return ""; + } + var prevValue = this[name]; + this[name] = ""; + + // That's what WebKit and Opera do + Array.prototype.splice.call(this, index, 1); + + // That's what Firefox does + //this[index] = "" + + return prevValue; + }, + + getPropertyCSSValue: function() { + //FIXME + }, + + /** + * + * @param {String} name + */ + getPropertyPriority: function(name) { + return this._importants[name] || ""; + }, + + + /** + * element.style.overflow = "auto" + * element.style.getPropertyShorthand("overflow-x") + * -> "overflow" + */ + getPropertyShorthand: function() { + //FIXME + }, + + isPropertyImplicit: function() { + //FIXME + }, + + // Doesn't work in IE < 9 + get cssText(){ + var properties = []; + for (var i=0, length=this.length; i < length; ++i) { + var name = this[i]; + var value = this.getPropertyValue(name); + var priority = this.getPropertyPriority(name); + if (priority) { + priority = " !" + priority; + } + properties[i] = name + ": " + value + priority + ";"; + } + return properties.join(" "); + }, + + set cssText(text){ + var i, name; + for (i = this.length; i--;) { + name = this[i]; + this[name] = ""; + } + Array.prototype.splice.call(this, 0, this.length); + this._importants = {}; + + var dummyRule = CSSOM.parse('#bogus{' + text + '}').cssRules[0].style; + var length = dummyRule.length; + for (i = 0; i < length; ++i) { + name = dummyRule[i]; + this.setProperty(dummyRule[i], dummyRule.getPropertyValue(name), dummyRule.getPropertyPriority(name)); + } + } +}; + + +//.CommonJS +exports.CSSStyleDeclaration = CSSOM.CSSStyleDeclaration; +CSSOM.parse = require('./parse').parse; // Cannot be included sooner due to the mutual dependency between parse.js and CSSStyleDeclaration.js +///CommonJS diff --git a/docs/js/node_modules/cssom/lib/CSSStyleRule.js b/docs/js/node_modules/cssom/lib/CSSStyleRule.js new file mode 100644 index 000000000..630b3f85c --- /dev/null +++ b/docs/js/node_modules/cssom/lib/CSSStyleRule.js @@ -0,0 +1,190 @@ +//.CommonJS +var CSSOM = { + CSSStyleDeclaration: require("./CSSStyleDeclaration").CSSStyleDeclaration, + CSSRule: require("./CSSRule").CSSRule +}; +///CommonJS + + +/** + * @constructor + * @see http://dev.w3.org/csswg/cssom/#cssstylerule + * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSStyleRule + */ +CSSOM.CSSStyleRule = function CSSStyleRule() { + CSSOM.CSSRule.call(this); + this.selectorText = ""; + this.style = new CSSOM.CSSStyleDeclaration(); + this.style.parentRule = this; +}; + +CSSOM.CSSStyleRule.prototype = new CSSOM.CSSRule(); +CSSOM.CSSStyleRule.prototype.constructor = CSSOM.CSSStyleRule; +CSSOM.CSSStyleRule.prototype.type = 1; + +Object.defineProperty(CSSOM.CSSStyleRule.prototype, "cssText", { + get: function() { + var text; + if (this.selectorText) { + text = this.selectorText + " {" + this.style.cssText + "}"; + } else { + text = ""; + } + return text; + }, + set: function(cssText) { + var rule = CSSOM.CSSStyleRule.parse(cssText); + this.style = rule.style; + this.selectorText = rule.selectorText; + } +}); + + +/** + * NON-STANDARD + * lightweight version of parse.js. + * @param {string} ruleText + * @return CSSStyleRule + */ +CSSOM.CSSStyleRule.parse = function(ruleText) { + var i = 0; + var state = "selector"; + var index; + var j = i; + var buffer = ""; + + var SIGNIFICANT_WHITESPACE = { + "selector": true, + "value": true + }; + + var styleRule = new CSSOM.CSSStyleRule(); + var name, priority=""; + + for (var character; (character = ruleText.charAt(i)); i++) { + + switch (character) { + + case " ": + case "\t": + case "\r": + case "\n": + case "\f": + if (SIGNIFICANT_WHITESPACE[state]) { + // Squash 2 or more white-spaces in the row into 1 + switch (ruleText.charAt(i - 1)) { + case " ": + case "\t": + case "\r": + case "\n": + case "\f": + break; + default: + buffer += " "; + break; + } + } + break; + + // String + case '"': + j = i + 1; + index = ruleText.indexOf('"', j) + 1; + if (!index) { + throw '" is missing'; + } + buffer += ruleText.slice(i, index); + i = index - 1; + break; + + case "'": + j = i + 1; + index = ruleText.indexOf("'", j) + 1; + if (!index) { + throw "' is missing"; + } + buffer += ruleText.slice(i, index); + i = index - 1; + break; + + // Comment + case "/": + if (ruleText.charAt(i + 1) === "*") { + i += 2; + index = ruleText.indexOf("*/", i); + if (index === -1) { + throw new SyntaxError("Missing */"); + } else { + i = index + 1; + } + } else { + buffer += character; + } + break; + + case "{": + if (state === "selector") { + styleRule.selectorText = buffer.trim(); + buffer = ""; + state = "name"; + } + break; + + case ":": + if (state === "name") { + name = buffer.trim(); + buffer = ""; + state = "value"; + } else { + buffer += character; + } + break; + + case "!": + if (state === "value" && ruleText.indexOf("!important", i) === i) { + priority = "important"; + i += "important".length; + } else { + buffer += character; + } + break; + + case ";": + if (state === "value") { + styleRule.style.setProperty(name, buffer.trim(), priority); + priority = ""; + buffer = ""; + state = "name"; + } else { + buffer += character; + } + break; + + case "}": + if (state === "value") { + styleRule.style.setProperty(name, buffer.trim(), priority); + priority = ""; + buffer = ""; + } else if (state === "name") { + break; + } else { + buffer += character; + } + state = "selector"; + break; + + default: + buffer += character; + break; + + } + } + + return styleRule; + +}; + + +//.CommonJS +exports.CSSStyleRule = CSSOM.CSSStyleRule; +///CommonJS diff --git a/docs/js/node_modules/cssom/lib/CSSStyleSheet.js b/docs/js/node_modules/cssom/lib/CSSStyleSheet.js new file mode 100644 index 000000000..f0e0dfc59 --- /dev/null +++ b/docs/js/node_modules/cssom/lib/CSSStyleSheet.js @@ -0,0 +1,88 @@ +//.CommonJS +var CSSOM = { + StyleSheet: require("./StyleSheet").StyleSheet, + CSSStyleRule: require("./CSSStyleRule").CSSStyleRule +}; +///CommonJS + + +/** + * @constructor + * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSStyleSheet + */ +CSSOM.CSSStyleSheet = function CSSStyleSheet() { + CSSOM.StyleSheet.call(this); + this.cssRules = []; +}; + + +CSSOM.CSSStyleSheet.prototype = new CSSOM.StyleSheet(); +CSSOM.CSSStyleSheet.prototype.constructor = CSSOM.CSSStyleSheet; + + +/** + * Used to insert a new rule into the style sheet. The new rule now becomes part of the cascade. + * + * sheet = new Sheet("body {margin: 0}") + * sheet.toString() + * -> "body{margin:0;}" + * sheet.insertRule("img {border: none}", 0) + * -> 0 + * sheet.toString() + * -> "img{border:none;}body{margin:0;}" + * + * @param {string} rule + * @param {number} index + * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSStyleSheet-insertRule + * @return {number} The index within the style sheet's rule collection of the newly inserted rule. + */ +CSSOM.CSSStyleSheet.prototype.insertRule = function(rule, index) { + if (index < 0 || index > this.cssRules.length) { + throw new RangeError("INDEX_SIZE_ERR"); + } + var cssRule = CSSOM.parse(rule).cssRules[0]; + cssRule.parentStyleSheet = this; + this.cssRules.splice(index, 0, cssRule); + return index; +}; + + +/** + * Used to delete a rule from the style sheet. + * + * sheet = new Sheet("img{border:none} body{margin:0}") + * sheet.toString() + * -> "img{border:none;}body{margin:0;}" + * sheet.deleteRule(0) + * sheet.toString() + * -> "body{margin:0;}" + * + * @param {number} index within the style sheet's rule list of the rule to remove. + * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSStyleSheet-deleteRule + */ +CSSOM.CSSStyleSheet.prototype.deleteRule = function(index) { + if (index < 0 || index >= this.cssRules.length) { + throw new RangeError("INDEX_SIZE_ERR"); + } + this.cssRules.splice(index, 1); +}; + + +/** + * NON-STANDARD + * @return {string} serialize stylesheet + */ +CSSOM.CSSStyleSheet.prototype.toString = function() { + var result = ""; + var rules = this.cssRules; + for (var i=0; i 1000 ? '1000px' : 'auto'); + * } + */ +CSSOM.CSSValueExpression.prototype.parse = function() { + var token = this._token, + idx = this._idx; + + var character = '', + expression = '', + error = '', + info, + paren = []; + + + for (; ; ++idx) { + character = token.charAt(idx); + + // end of token + if (character === '') { + error = 'css expression error: unfinished expression!'; + break; + } + + switch(character) { + case '(': + paren.push(character); + expression += character; + break; + + case ')': + paren.pop(character); + expression += character; + break; + + case '/': + if ((info = this._parseJSComment(token, idx))) { // comment? + if (info.error) { + error = 'css expression error: unfinished comment in expression!'; + } else { + idx = info.idx; + // ignore the comment + } + } else if ((info = this._parseJSRexExp(token, idx))) { // regexp + idx = info.idx; + expression += info.text; + } else { // other + expression += character; + } + break; + + case "'": + case '"': + info = this._parseJSString(token, idx, character); + if (info) { // string + idx = info.idx; + expression += info.text; + } else { + expression += character; + } + break; + + default: + expression += character; + break; + } + + if (error) { + break; + } + + // end of expression + if (paren.length === 0) { + break; + } + } + + var ret; + if (error) { + ret = { + error: error + }; + } else { + ret = { + idx: idx, + expression: expression + }; + } + + return ret; +}; + + +/** + * + * @return {Object|false} + * - idx: + * - text: + * or + * - error: + * or + * false + * + */ +CSSOM.CSSValueExpression.prototype._parseJSComment = function(token, idx) { + var nextChar = token.charAt(idx + 1), + text; + + if (nextChar === '/' || nextChar === '*') { + var startIdx = idx, + endIdx, + commentEndChar; + + if (nextChar === '/') { // line comment + commentEndChar = '\n'; + } else if (nextChar === '*') { // block comment + commentEndChar = '*/'; + } + + endIdx = token.indexOf(commentEndChar, startIdx + 1 + 1); + if (endIdx !== -1) { + endIdx = endIdx + commentEndChar.length - 1; + text = token.substring(idx, endIdx + 1); + return { + idx: endIdx, + text: text + }; + } else { + var error = 'css expression error: unfinished comment in expression!'; + return { + error: error + }; + } + } else { + return false; + } +}; + + +/** + * + * @return {Object|false} + * - idx: + * - text: + * or + * false + * + */ +CSSOM.CSSValueExpression.prototype._parseJSString = function(token, idx, sep) { + var endIdx = this._findMatchedIdx(token, idx, sep), + text; + + if (endIdx === -1) { + return false; + } else { + text = token.substring(idx, endIdx + sep.length); + + return { + idx: endIdx, + text: text + }; + } +}; + + +/** + * parse regexp in css expression + * + * @return {Object|false} + * - idx: + * - regExp: + * or + * false + */ + +/* + +all legal RegExp + +/a/ +(/a/) +[/a/] +[12, /a/] + +!/a/ + ++/a/ +-/a/ +* /a/ +/ /a/ +%/a/ + +===/a/ +!==/a/ +==/a/ +!=/a/ +>/a/ +>=/a/ +>/a/ +>>>/a/ + +&&/a/ +||/a/ +?/a/ +=/a/ +,/a/ + + delete /a/ + in /a/ +instanceof /a/ + new /a/ + typeof /a/ + void /a/ + +*/ +CSSOM.CSSValueExpression.prototype._parseJSRexExp = function(token, idx) { + var before = token.substring(0, idx).replace(/\s+$/, ""), + legalRegx = [ + /^$/, + /\($/, + /\[$/, + /\!$/, + /\+$/, + /\-$/, + /\*$/, + /\/\s+/, + /\%$/, + /\=$/, + /\>$/, + /<$/, + /\&$/, + /\|$/, + /\^$/, + /\~$/, + /\?$/, + /\,$/, + /delete$/, + /in$/, + /instanceof$/, + /new$/, + /typeof$/, + /void$/ + ]; + + var isLegal = legalRegx.some(function(reg) { + return reg.test(before); + }); + + if (!isLegal) { + return false; + } else { + var sep = '/'; + + // same logic as string + return this._parseJSString(token, idx, sep); + } +}; + + +/** + * + * find next sep(same line) index in `token` + * + * @return {Number} + * + */ +CSSOM.CSSValueExpression.prototype._findMatchedIdx = function(token, idx, sep) { + var startIdx = idx, + endIdx; + + var NOT_FOUND = -1; + + while(true) { + endIdx = token.indexOf(sep, startIdx + 1); + + if (endIdx === -1) { // not found + endIdx = NOT_FOUND; + break; + } else { + var text = token.substring(idx + 1, endIdx), + matched = text.match(/\\+$/); + if (!matched || matched[0] % 2 === 0) { // not escaped + break; + } else { + startIdx = endIdx; + } + } + } + + // boundary must be in the same line(js sting or regexp) + var nextNewLineIdx = token.indexOf('\n', idx + 1); + if (nextNewLineIdx < endIdx) { + endIdx = NOT_FOUND; + } + + + return endIdx; +}; + + + + +//.CommonJS +exports.CSSValueExpression = CSSOM.CSSValueExpression; +///CommonJS diff --git a/docs/js/node_modules/cssom/lib/MatcherList.js b/docs/js/node_modules/cssom/lib/MatcherList.js new file mode 100644 index 000000000..a79158518 --- /dev/null +++ b/docs/js/node_modules/cssom/lib/MatcherList.js @@ -0,0 +1,62 @@ +//.CommonJS +var CSSOM = {}; +///CommonJS + + +/** + * @constructor + * @see https://developer.mozilla.org/en/CSS/@-moz-document + */ +CSSOM.MatcherList = function MatcherList(){ + this.length = 0; +}; + +CSSOM.MatcherList.prototype = { + + constructor: CSSOM.MatcherList, + + /** + * @return {string} + */ + get matcherText() { + return Array.prototype.join.call(this, ", "); + }, + + /** + * @param {string} value + */ + set matcherText(value) { + // just a temporary solution, actually it may be wrong by just split the value with ',', because a url can include ','. + var values = value.split(","); + var length = this.length = values.length; + for (var i=0; i 0; + + while (ancestorRules.length > 0) { + parentRule = ancestorRules.pop(); + + if ( + parentRule.constructor.name === "CSSMediaRule" + || parentRule.constructor.name === "CSSSupportsRule" + ) { + prevScope = currentScope; + currentScope = parentRule; + currentScope.cssRules.push(prevScope); + break; + } + + if (ancestorRules.length === 0) { + hasAncestors = false; + } + } + + if (!hasAncestors) { + currentScope.__ends = i + 1; + styleSheet.cssRules.push(currentScope); + currentScope = styleSheet; + parentRule = null; + } + + buffer = ""; + state = "before-selector"; + break; + } + break; + + default: + switch (state) { + case "before-selector": + state = "selector"; + styleRule = new CSSOM.CSSStyleRule(); + styleRule.__starts = i; + break; + case "before-name": + state = "name"; + break; + case "before-value": + state = "value"; + break; + case "importRule-begin": + state = "importRule"; + break; + } + buffer += character; + break; + } + } + + return styleSheet; +}; + + +//.CommonJS +exports.parse = CSSOM.parse; +// The following modules cannot be included sooner due to the mutual dependency with parse.js +CSSOM.CSSStyleSheet = require("./CSSStyleSheet").CSSStyleSheet; +CSSOM.CSSStyleRule = require("./CSSStyleRule").CSSStyleRule; +CSSOM.CSSImportRule = require("./CSSImportRule").CSSImportRule; +CSSOM.CSSMediaRule = require("./CSSMediaRule").CSSMediaRule; +CSSOM.CSSSupportsRule = require("./CSSSupportsRule").CSSSupportsRule; +CSSOM.CSSFontFaceRule = require("./CSSFontFaceRule").CSSFontFaceRule; +CSSOM.CSSHostRule = require("./CSSHostRule").CSSHostRule; +CSSOM.CSSStyleDeclaration = require('./CSSStyleDeclaration').CSSStyleDeclaration; +CSSOM.CSSKeyframeRule = require('./CSSKeyframeRule').CSSKeyframeRule; +CSSOM.CSSKeyframesRule = require('./CSSKeyframesRule').CSSKeyframesRule; +CSSOM.CSSValueExpression = require('./CSSValueExpression').CSSValueExpression; +CSSOM.CSSDocumentRule = require('./CSSDocumentRule').CSSDocumentRule; +///CommonJS diff --git a/docs/js/node_modules/cssom/package.json b/docs/js/node_modules/cssom/package.json new file mode 100644 index 000000000..fd22917ac --- /dev/null +++ b/docs/js/node_modules/cssom/package.json @@ -0,0 +1,54 @@ +{ + "_from": "cssom@>= 0.3.2 < 0.4.0", + "_id": "cssom@0.3.8", + "_inBundle": false, + "_integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", + "_location": "/cssom", + "_phantomChildren": {}, + "_requested": { + "type": "range", + "registry": true, + "raw": "cssom@>= 0.3.2 < 0.4.0", + "name": "cssom", + "escapedName": "cssom", + "rawSpec": ">= 0.3.2 < 0.4.0", + "saveSpec": null, + "fetchSpec": ">= 0.3.2 < 0.4.0" + }, + "_requiredBy": [ + "/cssstyle", + "/jsdom" + ], + "_resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", + "_shasum": "9f1276f5b2b463f2114d3f2c75250af8c1a36f4a", + "_spec": "cssom@>= 0.3.2 < 0.4.0", + "_where": "/Users/hectorip/Education/Eloquent-JavaScript-ES/node_modules/jsdom", + "author": { + "name": "Nikita Vasilyev", + "email": "me@elv1s.ru" + }, + "bugs": { + "url": "https://github.com/NV/CSSOM/issues" + }, + "bundleDependencies": false, + "deprecated": false, + "description": "CSS Object Model implementation and CSS parser", + "files": [ + "lib/" + ], + "homepage": "https://github.com/NV/CSSOM#readme", + "keywords": [ + "CSS", + "CSSOM", + "parser", + "styleSheet" + ], + "license": "MIT", + "main": "./lib/index.js", + "name": "cssom", + "repository": { + "type": "git", + "url": "git+https://github.com/NV/CSSOM.git" + }, + "version": "0.3.8" +} diff --git a/docs/js/node_modules/cssstyle/.eslintignore b/docs/js/node_modules/cssstyle/.eslintignore new file mode 100644 index 000000000..f9e0b7329 --- /dev/null +++ b/docs/js/node_modules/cssstyle/.eslintignore @@ -0,0 +1,3 @@ +node_modules +lib/implementedProperties.js +lib/properties.js diff --git a/docs/js/node_modules/cssstyle/.eslintrc.js b/docs/js/node_modules/cssstyle/.eslintrc.js new file mode 100644 index 000000000..770d7996f --- /dev/null +++ b/docs/js/node_modules/cssstyle/.eslintrc.js @@ -0,0 +1,50 @@ +'use strict'; + +module.exports = { + root: true, + extends: ['eslint:recommended', 'prettier'], + parserOptions: { + ecmaVersion: 2018, + }, + env: { + es6: true, + }, + globals: { + exports: true, + module: true, + require: true, + window: true, + }, + plugins: ['prettier'], + rules: { + 'prettier/prettier': [ + 'warn', + { + printWidth: 100, + singleQuote: true, + trailingComma: 'es5', + }, + ], + strict: ['warn', 'global'], + }, + overrides: [ + { + files: ['lib/implementedProperties.js', 'lib/properties.js'], + rules: { + 'prettier/prettier': 'off', + }, + }, + { + files: 'scripts/**/*', + rules: { + 'no-console': 'off', + }, + }, + { + files: ['scripts/**/*', 'tests/**/*'], + env: { + node: true, + }, + }, + ], +}; diff --git a/docs/js/node_modules/cssstyle/.travis.yml b/docs/js/node_modules/cssstyle/.travis.yml new file mode 100644 index 000000000..cf9a2232e --- /dev/null +++ b/docs/js/node_modules/cssstyle/.travis.yml @@ -0,0 +1,15 @@ +sudo: false +language: node_js +cache: + directories: + - node_modules +notifications: + email: true +node_js: + - 6 + - 8 + - 10 + - 11 + +script: + - npm run test-ci diff --git a/docs/js/node_modules/cssstyle/MIT-LICENSE.txt b/docs/js/node_modules/cssstyle/MIT-LICENSE.txt new file mode 100644 index 000000000..060a7e630 --- /dev/null +++ b/docs/js/node_modules/cssstyle/MIT-LICENSE.txt @@ -0,0 +1,20 @@ +Copyright (c) Chad Walker + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/docs/js/node_modules/cssstyle/README.md b/docs/js/node_modules/cssstyle/README.md new file mode 100644 index 000000000..66b14ae9b --- /dev/null +++ b/docs/js/node_modules/cssstyle/README.md @@ -0,0 +1,27 @@ +# CSSStyleDeclaration + +[![NpmVersion](https://img.shields.io/npm/v/cssstyle.svg)](https://www.npmjs.com/package/cssstyle) [![Build Status](https://travis-ci.org/jsakas/CSSStyleDeclaration.svg?branch=master)](https://travis-ci.org/jsakas/CSSStyleDeclaration) + +CSSStyleDeclaration is a work-a-like to the CSSStyleDeclaration class in Nikita Vasilyev's [CSSOM](https://github.com/NV/CSSOM). I made it so that when using [jQuery in node](https://github.com/tmtk75/node-jquery) setting css attributes via $.fn.css() would work. node-jquery uses [jsdom](https://github.com/tmpvar/jsdom) to create a DOM to use in node. jsdom uses CSSOM for styling, and CSSOM's implementation of the [CSSStyleDeclaration](http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSStyleDeclaration) doesn't support [CSS2Properties](http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSS2Properties), which is how jQuery's [$.fn.css()](http://api.jquery.com/css/) operates. + +### Why not just issue a pull request? + +Well, NV wants to keep CSSOM fast (which I can appreciate) and CSS2Properties aren't required by the standard (though every browser has the interface). So I figured the path of least resistance would be to just modify this one class, publish it as a node module (that requires CSSOM) and then make a pull request of jsdom to use it. + +### How do I test this code? + +`npm test` should do the trick, assuming you have the dev dependencies installed: + +``` +$ npm test + +tests +✔ Verify Has Properties +✔ Verify Has Functions +✔ Verify Has Special Properties +✔ Test From Style String +✔ Test From Properties +✔ Test Shorthand Properties +✔ Test width and height Properties and null and empty strings +✔ Test Implicit Properties +``` diff --git a/docs/js/node_modules/cssstyle/lib/CSSStyleDeclaration.js b/docs/js/node_modules/cssstyle/lib/CSSStyleDeclaration.js new file mode 100644 index 000000000..0bb9ecede --- /dev/null +++ b/docs/js/node_modules/cssstyle/lib/CSSStyleDeclaration.js @@ -0,0 +1,255 @@ +/********************************************************************* + * This is a fork from the CSS Style Declaration part of + * https://github.com/NV/CSSOM + ********************************************************************/ +'use strict'; +var CSSOM = require('cssom'); +var allProperties = require('./allProperties'); +var allExtraProperties = require('./allExtraProperties'); +var implementedProperties = require('./implementedProperties'); +var { dashedToCamelCase } = require('./parsers'); +var getBasicPropertyDescriptor = require('./utils/getBasicPropertyDescriptor'); + +/** + * @constructor + * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSStyleDeclaration + */ +var CSSStyleDeclaration = function CSSStyleDeclaration(onChangeCallback) { + this._values = {}; + this._importants = {}; + this._length = 0; + this._onChange = + onChangeCallback || + function() { + return; + }; +}; +CSSStyleDeclaration.prototype = { + constructor: CSSStyleDeclaration, + + /** + * + * @param {string} name + * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSStyleDeclaration-getPropertyValue + * @return {string} the value of the property if it has been explicitly set for this declaration block. + * Returns the empty string if the property has not been set. + */ + getPropertyValue: function(name) { + if (!this._values.hasOwnProperty(name)) { + return ''; + } + return this._values[name].toString(); + }, + + /** + * + * @param {string} name + * @param {string} value + * @param {string} [priority=null] "important" or null + * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSStyleDeclaration-setProperty + */ + setProperty: function(name, value, priority) { + if (value === undefined) { + return; + } + if (value === null || value === '') { + this.removeProperty(name); + return; + } + var lowercaseName = name.toLowerCase(); + if (!allProperties.has(lowercaseName) && !allExtraProperties.has(lowercaseName)) { + return; + } + + this[lowercaseName] = value; + this._importants[lowercaseName] = priority; + }, + _setProperty: function(name, value, priority) { + if (value === undefined) { + return; + } + if (value === null || value === '') { + this.removeProperty(name); + return; + } + if (this._values[name]) { + // Property already exist. Overwrite it. + var index = Array.prototype.indexOf.call(this, name); + if (index < 0) { + this[this._length] = name; + this._length++; + } + } else { + // New property. + this[this._length] = name; + this._length++; + } + this._values[name] = value; + this._importants[name] = priority; + this._onChange(this.cssText); + }, + + /** + * + * @param {string} name + * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSStyleDeclaration-removeProperty + * @return {string} the value of the property if it has been explicitly set for this declaration block. + * Returns the empty string if the property has not been set or the property name does not correspond to a known CSS property. + */ + removeProperty: function(name) { + if (!this._values.hasOwnProperty(name)) { + return ''; + } + + var prevValue = this._values[name]; + delete this._values[name]; + delete this._importants[name]; + + var index = Array.prototype.indexOf.call(this, name); + if (index < 0) { + return prevValue; + } + + // That's what WebKit and Opera do + Array.prototype.splice.call(this, index, 1); + + // That's what Firefox does + //this[index] = "" + + this._onChange(this.cssText); + return prevValue; + }, + + /** + * + * @param {String} name + */ + getPropertyPriority: function(name) { + return this._importants[name] || ''; + }, + + getPropertyCSSValue: function() { + //FIXME + return; + }, + + /** + * element.style.overflow = "auto" + * element.style.getPropertyShorthand("overflow-x") + * -> "overflow" + */ + getPropertyShorthand: function() { + //FIXME + return; + }, + + isPropertyImplicit: function() { + //FIXME + return; + }, + + /** + * http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSStyleDeclaration-item + */ + item: function(index) { + index = parseInt(index, 10); + if (index < 0 || index >= this._length) { + return ''; + } + return this[index]; + }, +}; + +Object.defineProperties(CSSStyleDeclaration.prototype, { + cssText: { + get: function() { + var properties = []; + var i; + var name; + var value; + var priority; + for (i = 0; i < this._length; i++) { + name = this[i]; + value = this.getPropertyValue(name); + priority = this.getPropertyPriority(name); + if (priority !== '') { + priority = ' !' + priority; + } + properties.push([name, ': ', value, priority, ';'].join('')); + } + return properties.join(' '); + }, + set: function(value) { + var i; + this._values = {}; + Array.prototype.splice.call(this, 0, this._length); + this._importants = {}; + var dummyRule; + try { + dummyRule = CSSOM.parse('#bogus{' + value + '}').cssRules[0].style; + } catch (err) { + // malformed css, just return + return; + } + var rule_length = dummyRule.length; + var name; + for (i = 0; i < rule_length; ++i) { + name = dummyRule[i]; + this.setProperty( + dummyRule[i], + dummyRule.getPropertyValue(name), + dummyRule.getPropertyPriority(name) + ); + } + this._onChange(this.cssText); + }, + enumerable: true, + configurable: true, + }, + parentRule: { + get: function() { + return null; + }, + enumerable: true, + configurable: true, + }, + length: { + get: function() { + return this._length; + }, + /** + * This deletes indices if the new length is less then the current + * length. If the new length is more, it does nothing, the new indices + * will be undefined until set. + **/ + set: function(value) { + var i; + for (i = value; i < this._length; i++) { + delete this[i]; + } + this._length = value; + }, + enumerable: true, + configurable: true, + }, +}); + +require('./properties')(CSSStyleDeclaration.prototype); + +allProperties.forEach(function(property) { + if (!implementedProperties.has(property)) { + var declaration = getBasicPropertyDescriptor(property); + Object.defineProperty(CSSStyleDeclaration.prototype, property, declaration); + Object.defineProperty(CSSStyleDeclaration.prototype, dashedToCamelCase(property), declaration); + } +}); + +allExtraProperties.forEach(function(property) { + if (!implementedProperties.has(property)) { + var declaration = getBasicPropertyDescriptor(property); + Object.defineProperty(CSSStyleDeclaration.prototype, property, declaration); + Object.defineProperty(CSSStyleDeclaration.prototype, dashedToCamelCase(property), declaration); + } +}); + +exports.CSSStyleDeclaration = CSSStyleDeclaration; diff --git a/docs/js/node_modules/cssstyle/lib/allExtraProperties.js b/docs/js/node_modules/cssstyle/lib/allExtraProperties.js new file mode 100644 index 000000000..0d66ca8f4 --- /dev/null +++ b/docs/js/node_modules/cssstyle/lib/allExtraProperties.js @@ -0,0 +1,248 @@ +'use strict'; + +/** + * This file contains all implemented properties that are not a part of any + * current specifications or drafts, but are handled by browsers nevertheless. + */ + +var allExtraProperties = new Set(); +module.exports = allExtraProperties; +allExtraProperties.add('background-position-x'); +allExtraProperties.add('background-position-y'); +allExtraProperties.add('background-repeat-x'); +allExtraProperties.add('background-repeat-y'); +allExtraProperties.add('color-interpolation'); +allExtraProperties.add('color-profile'); +allExtraProperties.add('color-rendering'); +allExtraProperties.add('css-float'); +allExtraProperties.add('enable-background'); +allExtraProperties.add('fill'); +allExtraProperties.add('fill-opacity'); +allExtraProperties.add('fill-rule'); +allExtraProperties.add('glyph-orientation-horizontal'); +allExtraProperties.add('image-rendering'); +allExtraProperties.add('kerning'); +allExtraProperties.add('marker'); +allExtraProperties.add('marker-end'); +allExtraProperties.add('marker-mid'); +allExtraProperties.add('marker-offset'); +allExtraProperties.add('marker-start'); +allExtraProperties.add('marks'); +allExtraProperties.add('pointer-events'); +allExtraProperties.add('shape-rendering'); +allExtraProperties.add('size'); +allExtraProperties.add('src'); +allExtraProperties.add('stop-color'); +allExtraProperties.add('stop-opacity'); +allExtraProperties.add('stroke'); +allExtraProperties.add('stroke-dasharray'); +allExtraProperties.add('stroke-dashoffset'); +allExtraProperties.add('stroke-linecap'); +allExtraProperties.add('stroke-linejoin'); +allExtraProperties.add('stroke-miterlimit'); +allExtraProperties.add('stroke-opacity'); +allExtraProperties.add('stroke-width'); +allExtraProperties.add('text-anchor'); +allExtraProperties.add('text-line-through'); +allExtraProperties.add('text-line-through-color'); +allExtraProperties.add('text-line-through-mode'); +allExtraProperties.add('text-line-through-style'); +allExtraProperties.add('text-line-through-width'); +allExtraProperties.add('text-overline'); +allExtraProperties.add('text-overline-color'); +allExtraProperties.add('text-overline-mode'); +allExtraProperties.add('text-overline-style'); +allExtraProperties.add('text-overline-width'); +allExtraProperties.add('text-rendering'); +allExtraProperties.add('text-underline'); +allExtraProperties.add('text-underline-color'); +allExtraProperties.add('text-underline-mode'); +allExtraProperties.add('text-underline-style'); +allExtraProperties.add('text-underline-width'); +allExtraProperties.add('unicode-range'); +allExtraProperties.add('vector-effect'); +allExtraProperties.add('webkit-animation'); +allExtraProperties.add('webkit-animation-delay'); +allExtraProperties.add('webkit-animation-direction'); +allExtraProperties.add('webkit-animation-duration'); +allExtraProperties.add('webkit-animation-fill-mode'); +allExtraProperties.add('webkit-animation-iteration-count'); +allExtraProperties.add('webkit-animation-name'); +allExtraProperties.add('webkit-animation-play-state'); +allExtraProperties.add('webkit-animation-timing-function'); +allExtraProperties.add('webkit-appearance'); +allExtraProperties.add('webkit-aspect-ratio'); +allExtraProperties.add('webkit-backface-visibility'); +allExtraProperties.add('webkit-background-clip'); +allExtraProperties.add('webkit-background-composite'); +allExtraProperties.add('webkit-background-origin'); +allExtraProperties.add('webkit-background-size'); +allExtraProperties.add('webkit-border-after'); +allExtraProperties.add('webkit-border-after-color'); +allExtraProperties.add('webkit-border-after-style'); +allExtraProperties.add('webkit-border-after-width'); +allExtraProperties.add('webkit-border-before'); +allExtraProperties.add('webkit-border-before-color'); +allExtraProperties.add('webkit-border-before-style'); +allExtraProperties.add('webkit-border-before-width'); +allExtraProperties.add('webkit-border-end'); +allExtraProperties.add('webkit-border-end-color'); +allExtraProperties.add('webkit-border-end-style'); +allExtraProperties.add('webkit-border-end-width'); +allExtraProperties.add('webkit-border-fit'); +allExtraProperties.add('webkit-border-horizontal-spacing'); +allExtraProperties.add('webkit-border-image'); +allExtraProperties.add('webkit-border-radius'); +allExtraProperties.add('webkit-border-start'); +allExtraProperties.add('webkit-border-start-color'); +allExtraProperties.add('webkit-border-start-style'); +allExtraProperties.add('webkit-border-start-width'); +allExtraProperties.add('webkit-border-vertical-spacing'); +allExtraProperties.add('webkit-box-align'); +allExtraProperties.add('webkit-box-direction'); +allExtraProperties.add('webkit-box-flex'); +allExtraProperties.add('webkit-box-flex-group'); +allExtraProperties.add('webkit-box-lines'); +allExtraProperties.add('webkit-box-ordinal-group'); +allExtraProperties.add('webkit-box-orient'); +allExtraProperties.add('webkit-box-pack'); +allExtraProperties.add('webkit-box-reflect'); +allExtraProperties.add('webkit-box-shadow'); +allExtraProperties.add('webkit-color-correction'); +allExtraProperties.add('webkit-column-axis'); +allExtraProperties.add('webkit-column-break-after'); +allExtraProperties.add('webkit-column-break-before'); +allExtraProperties.add('webkit-column-break-inside'); +allExtraProperties.add('webkit-column-count'); +allExtraProperties.add('webkit-column-gap'); +allExtraProperties.add('webkit-column-rule'); +allExtraProperties.add('webkit-column-rule-color'); +allExtraProperties.add('webkit-column-rule-style'); +allExtraProperties.add('webkit-column-rule-width'); +allExtraProperties.add('webkit-columns'); +allExtraProperties.add('webkit-column-span'); +allExtraProperties.add('webkit-column-width'); +allExtraProperties.add('webkit-filter'); +allExtraProperties.add('webkit-flex-align'); +allExtraProperties.add('webkit-flex-direction'); +allExtraProperties.add('webkit-flex-flow'); +allExtraProperties.add('webkit-flex-item-align'); +allExtraProperties.add('webkit-flex-line-pack'); +allExtraProperties.add('webkit-flex-order'); +allExtraProperties.add('webkit-flex-pack'); +allExtraProperties.add('webkit-flex-wrap'); +allExtraProperties.add('webkit-flow-from'); +allExtraProperties.add('webkit-flow-into'); +allExtraProperties.add('webkit-font-feature-settings'); +allExtraProperties.add('webkit-font-kerning'); +allExtraProperties.add('webkit-font-size-delta'); +allExtraProperties.add('webkit-font-smoothing'); +allExtraProperties.add('webkit-font-variant-ligatures'); +allExtraProperties.add('webkit-highlight'); +allExtraProperties.add('webkit-hyphenate-character'); +allExtraProperties.add('webkit-hyphenate-limit-after'); +allExtraProperties.add('webkit-hyphenate-limit-before'); +allExtraProperties.add('webkit-hyphenate-limit-lines'); +allExtraProperties.add('webkit-hyphens'); +allExtraProperties.add('webkit-line-align'); +allExtraProperties.add('webkit-line-box-contain'); +allExtraProperties.add('webkit-line-break'); +allExtraProperties.add('webkit-line-clamp'); +allExtraProperties.add('webkit-line-grid'); +allExtraProperties.add('webkit-line-snap'); +allExtraProperties.add('webkit-locale'); +allExtraProperties.add('webkit-logical-height'); +allExtraProperties.add('webkit-logical-width'); +allExtraProperties.add('webkit-margin-after'); +allExtraProperties.add('webkit-margin-after-collapse'); +allExtraProperties.add('webkit-margin-before'); +allExtraProperties.add('webkit-margin-before-collapse'); +allExtraProperties.add('webkit-margin-bottom-collapse'); +allExtraProperties.add('webkit-margin-collapse'); +allExtraProperties.add('webkit-margin-end'); +allExtraProperties.add('webkit-margin-start'); +allExtraProperties.add('webkit-margin-top-collapse'); +allExtraProperties.add('webkit-marquee'); +allExtraProperties.add('webkit-marquee-direction'); +allExtraProperties.add('webkit-marquee-increment'); +allExtraProperties.add('webkit-marquee-repetition'); +allExtraProperties.add('webkit-marquee-speed'); +allExtraProperties.add('webkit-marquee-style'); +allExtraProperties.add('webkit-mask'); +allExtraProperties.add('webkit-mask-attachment'); +allExtraProperties.add('webkit-mask-box-image'); +allExtraProperties.add('webkit-mask-box-image-outset'); +allExtraProperties.add('webkit-mask-box-image-repeat'); +allExtraProperties.add('webkit-mask-box-image-slice'); +allExtraProperties.add('webkit-mask-box-image-source'); +allExtraProperties.add('webkit-mask-box-image-width'); +allExtraProperties.add('webkit-mask-clip'); +allExtraProperties.add('webkit-mask-composite'); +allExtraProperties.add('webkit-mask-image'); +allExtraProperties.add('webkit-mask-origin'); +allExtraProperties.add('webkit-mask-position'); +allExtraProperties.add('webkit-mask-position-x'); +allExtraProperties.add('webkit-mask-position-y'); +allExtraProperties.add('webkit-mask-repeat'); +allExtraProperties.add('webkit-mask-repeat-x'); +allExtraProperties.add('webkit-mask-repeat-y'); +allExtraProperties.add('webkit-mask-size'); +allExtraProperties.add('webkit-match-nearest-mail-blockquote-color'); +allExtraProperties.add('webkit-max-logical-height'); +allExtraProperties.add('webkit-max-logical-width'); +allExtraProperties.add('webkit-min-logical-height'); +allExtraProperties.add('webkit-min-logical-width'); +allExtraProperties.add('webkit-nbsp-mode'); +allExtraProperties.add('webkit-overflow-scrolling'); +allExtraProperties.add('webkit-padding-after'); +allExtraProperties.add('webkit-padding-before'); +allExtraProperties.add('webkit-padding-end'); +allExtraProperties.add('webkit-padding-start'); +allExtraProperties.add('webkit-perspective'); +allExtraProperties.add('webkit-perspective-origin'); +allExtraProperties.add('webkit-perspective-origin-x'); +allExtraProperties.add('webkit-perspective-origin-y'); +allExtraProperties.add('webkit-print-color-adjust'); +allExtraProperties.add('webkit-region-break-after'); +allExtraProperties.add('webkit-region-break-before'); +allExtraProperties.add('webkit-region-break-inside'); +allExtraProperties.add('webkit-region-overflow'); +allExtraProperties.add('webkit-rtl-ordering'); +allExtraProperties.add('webkit-svg-shadow'); +allExtraProperties.add('webkit-tap-highlight-color'); +allExtraProperties.add('webkit-text-combine'); +allExtraProperties.add('webkit-text-decorations-in-effect'); +allExtraProperties.add('webkit-text-emphasis'); +allExtraProperties.add('webkit-text-emphasis-color'); +allExtraProperties.add('webkit-text-emphasis-position'); +allExtraProperties.add('webkit-text-emphasis-style'); +allExtraProperties.add('webkit-text-fill-color'); +allExtraProperties.add('webkit-text-orientation'); +allExtraProperties.add('webkit-text-security'); +allExtraProperties.add('webkit-text-size-adjust'); +allExtraProperties.add('webkit-text-stroke'); +allExtraProperties.add('webkit-text-stroke-color'); +allExtraProperties.add('webkit-text-stroke-width'); +allExtraProperties.add('webkit-transform'); +allExtraProperties.add('webkit-transform-origin'); +allExtraProperties.add('webkit-transform-origin-x'); +allExtraProperties.add('webkit-transform-origin-y'); +allExtraProperties.add('webkit-transform-origin-z'); +allExtraProperties.add('webkit-transform-style'); +allExtraProperties.add('webkit-transition'); +allExtraProperties.add('webkit-transition-delay'); +allExtraProperties.add('webkit-transition-duration'); +allExtraProperties.add('webkit-transition-property'); +allExtraProperties.add('webkit-transition-timing-function'); +allExtraProperties.add('webkit-user-drag'); +allExtraProperties.add('webkit-user-modify'); +allExtraProperties.add('webkit-user-select'); +allExtraProperties.add('webkit-wrap'); +allExtraProperties.add('webkit-wrap-flow'); +allExtraProperties.add('webkit-wrap-margin'); +allExtraProperties.add('webkit-wrap-padding'); +allExtraProperties.add('webkit-wrap-shape-inside'); +allExtraProperties.add('webkit-wrap-shape-outside'); +allExtraProperties.add('webkit-wrap-through'); +allExtraProperties.add('webkit-writing-mode'); +allExtraProperties.add('zoom'); diff --git a/docs/js/node_modules/cssstyle/lib/allProperties.js b/docs/js/node_modules/cssstyle/lib/allProperties.js new file mode 100644 index 000000000..fc801df2d --- /dev/null +++ b/docs/js/node_modules/cssstyle/lib/allProperties.js @@ -0,0 +1,457 @@ +'use strict'; + +// autogenerated - 2/3/2019 + +/* + * + * https://www.w3.org/Style/CSS/all-properties.en.html + */ + +var allProperties = new Set(); +module.exports = allProperties; +allProperties.add('align-content'); +allProperties.add('align-items'); +allProperties.add('align-self'); +allProperties.add('alignment-baseline'); +allProperties.add('all'); +allProperties.add('animation'); +allProperties.add('animation-delay'); +allProperties.add('animation-direction'); +allProperties.add('animation-duration'); +allProperties.add('animation-fill-mode'); +allProperties.add('animation-iteration-count'); +allProperties.add('animation-name'); +allProperties.add('animation-play-state'); +allProperties.add('animation-timing-function'); +allProperties.add('appearance'); +allProperties.add('azimuth'); +allProperties.add('background'); +allProperties.add('background-attachment'); +allProperties.add('background-blend-mode'); +allProperties.add('background-clip'); +allProperties.add('background-color'); +allProperties.add('background-image'); +allProperties.add('background-origin'); +allProperties.add('background-position'); +allProperties.add('background-repeat'); +allProperties.add('background-size'); +allProperties.add('baseline-shift'); +allProperties.add('block-overflow'); +allProperties.add('block-size'); +allProperties.add('bookmark-label'); +allProperties.add('bookmark-level'); +allProperties.add('bookmark-state'); +allProperties.add('border'); +allProperties.add('border-block'); +allProperties.add('border-block-color'); +allProperties.add('border-block-end'); +allProperties.add('border-block-end-color'); +allProperties.add('border-block-end-style'); +allProperties.add('border-block-end-width'); +allProperties.add('border-block-start'); +allProperties.add('border-block-start-color'); +allProperties.add('border-block-start-style'); +allProperties.add('border-block-start-width'); +allProperties.add('border-block-style'); +allProperties.add('border-block-width'); +allProperties.add('border-bottom'); +allProperties.add('border-bottom-color'); +allProperties.add('border-bottom-left-radius'); +allProperties.add('border-bottom-right-radius'); +allProperties.add('border-bottom-style'); +allProperties.add('border-bottom-width'); +allProperties.add('border-boundary'); +allProperties.add('border-collapse'); +allProperties.add('border-color'); +allProperties.add('border-end-end-radius'); +allProperties.add('border-end-start-radius'); +allProperties.add('border-image'); +allProperties.add('border-image-outset'); +allProperties.add('border-image-repeat'); +allProperties.add('border-image-slice'); +allProperties.add('border-image-source'); +allProperties.add('border-image-width'); +allProperties.add('border-inline'); +allProperties.add('border-inline-color'); +allProperties.add('border-inline-end'); +allProperties.add('border-inline-end-color'); +allProperties.add('border-inline-end-style'); +allProperties.add('border-inline-end-width'); +allProperties.add('border-inline-start'); +allProperties.add('border-inline-start-color'); +allProperties.add('border-inline-start-style'); +allProperties.add('border-inline-start-width'); +allProperties.add('border-inline-style'); +allProperties.add('border-inline-width'); +allProperties.add('border-left'); +allProperties.add('border-left-color'); +allProperties.add('border-left-style'); +allProperties.add('border-left-width'); +allProperties.add('border-radius'); +allProperties.add('border-right'); +allProperties.add('border-right-color'); +allProperties.add('border-right-style'); +allProperties.add('border-right-width'); +allProperties.add('border-spacing'); +allProperties.add('border-start-end-radius'); +allProperties.add('border-start-start-radius'); +allProperties.add('border-style'); +allProperties.add('border-top'); +allProperties.add('border-top-color'); +allProperties.add('border-top-left-radius'); +allProperties.add('border-top-right-radius'); +allProperties.add('border-top-style'); +allProperties.add('border-top-width'); +allProperties.add('border-width'); +allProperties.add('bottom'); +allProperties.add('box-decoration-break'); +allProperties.add('box-shadow'); +allProperties.add('box-sizing'); +allProperties.add('box-snap'); +allProperties.add('break-after'); +allProperties.add('break-before'); +allProperties.add('break-inside'); +allProperties.add('caption-side'); +allProperties.add('caret'); +allProperties.add('caret-color'); +allProperties.add('caret-shape'); +allProperties.add('chains'); +allProperties.add('clear'); +allProperties.add('clip'); +allProperties.add('clip-path'); +allProperties.add('clip-rule'); +allProperties.add('color'); +allProperties.add('color-interpolation-filters'); +allProperties.add('column-count'); +allProperties.add('column-fill'); +allProperties.add('column-gap'); +allProperties.add('column-rule'); +allProperties.add('column-rule-color'); +allProperties.add('column-rule-style'); +allProperties.add('column-rule-width'); +allProperties.add('column-span'); +allProperties.add('column-width'); +allProperties.add('columns'); +allProperties.add('contain'); +allProperties.add('content'); +allProperties.add('continue'); +allProperties.add('counter-increment'); +allProperties.add('counter-reset'); +allProperties.add('counter-set'); +allProperties.add('cue'); +allProperties.add('cue-after'); +allProperties.add('cue-before'); +allProperties.add('cursor'); +allProperties.add('direction'); +allProperties.add('display'); +allProperties.add('dominant-baseline'); +allProperties.add('elevation'); +allProperties.add('empty-cells'); +allProperties.add('filter'); +allProperties.add('flex'); +allProperties.add('flex-basis'); +allProperties.add('flex-direction'); +allProperties.add('flex-flow'); +allProperties.add('flex-grow'); +allProperties.add('flex-shrink'); +allProperties.add('flex-wrap'); +allProperties.add('float'); +allProperties.add('flood-color'); +allProperties.add('flood-opacity'); +allProperties.add('flow'); +allProperties.add('flow-from'); +allProperties.add('flow-into'); +allProperties.add('font'); +allProperties.add('font-family'); +allProperties.add('font-feature-settings'); +allProperties.add('font-kerning'); +allProperties.add('font-language-override'); +allProperties.add('font-max-size'); +allProperties.add('font-min-size'); +allProperties.add('font-optical-sizing'); +allProperties.add('font-palette'); +allProperties.add('font-size'); +allProperties.add('font-size-adjust'); +allProperties.add('font-stretch'); +allProperties.add('font-style'); +allProperties.add('font-synthesis'); +allProperties.add('font-synthesis-small-caps'); +allProperties.add('font-synthesis-style'); +allProperties.add('font-synthesis-weight'); +allProperties.add('font-variant'); +allProperties.add('font-variant-alternates'); +allProperties.add('font-variant-caps'); +allProperties.add('font-variant-east-asian'); +allProperties.add('font-variant-emoji'); +allProperties.add('font-variant-ligatures'); +allProperties.add('font-variant-numeric'); +allProperties.add('font-variant-position'); +allProperties.add('font-variation-settings'); +allProperties.add('font-weight'); +allProperties.add('footnote-display'); +allProperties.add('footnote-policy'); +allProperties.add('gap'); +allProperties.add('glyph-orientation-vertical'); +allProperties.add('grid'); +allProperties.add('grid-area'); +allProperties.add('grid-auto-columns'); +allProperties.add('grid-auto-flow'); +allProperties.add('grid-auto-rows'); +allProperties.add('grid-column'); +allProperties.add('grid-column-end'); +allProperties.add('grid-column-start'); +allProperties.add('grid-row'); +allProperties.add('grid-row-end'); +allProperties.add('grid-row-start'); +allProperties.add('grid-template'); +allProperties.add('grid-template-areas'); +allProperties.add('grid-template-columns'); +allProperties.add('grid-template-rows'); +allProperties.add('hanging-punctuation'); +allProperties.add('height'); +allProperties.add('hyphenate-character'); +allProperties.add('hyphenate-limit-chars'); +allProperties.add('hyphenate-limit-last'); +allProperties.add('hyphenate-limit-lines'); +allProperties.add('hyphenate-limit-zone'); +allProperties.add('hyphens'); +allProperties.add('image-orientation'); +allProperties.add('image-resolution'); +allProperties.add('initial-letters'); +allProperties.add('initial-letters-align'); +allProperties.add('initial-letters-wrap'); +allProperties.add('inline-size'); +allProperties.add('inline-sizing'); +allProperties.add('inset'); +allProperties.add('inset-block'); +allProperties.add('inset-block-end'); +allProperties.add('inset-block-start'); +allProperties.add('inset-inline'); +allProperties.add('inset-inline-end'); +allProperties.add('inset-inline-start'); +allProperties.add('isolation'); +allProperties.add('justify-content'); +allProperties.add('justify-items'); +allProperties.add('justify-self'); +allProperties.add('left'); +allProperties.add('letter-spacing'); +allProperties.add('lighting-color'); +allProperties.add('line-break'); +allProperties.add('line-clamp'); +allProperties.add('line-grid'); +allProperties.add('line-height'); +allProperties.add('line-padding'); +allProperties.add('line-snap'); +allProperties.add('list-style'); +allProperties.add('list-style-image'); +allProperties.add('list-style-position'); +allProperties.add('list-style-type'); +allProperties.add('margin'); +allProperties.add('margin-block'); +allProperties.add('margin-block-end'); +allProperties.add('margin-block-start'); +allProperties.add('margin-bottom'); +allProperties.add('margin-inline'); +allProperties.add('margin-inline-end'); +allProperties.add('margin-inline-start'); +allProperties.add('margin-left'); +allProperties.add('margin-right'); +allProperties.add('margin-top'); +allProperties.add('margin-trim'); +allProperties.add('marker-side'); +allProperties.add('mask'); +allProperties.add('mask-border'); +allProperties.add('mask-border-mode'); +allProperties.add('mask-border-outset'); +allProperties.add('mask-border-repeat'); +allProperties.add('mask-border-slice'); +allProperties.add('mask-border-source'); +allProperties.add('mask-border-width'); +allProperties.add('mask-clip'); +allProperties.add('mask-composite'); +allProperties.add('mask-image'); +allProperties.add('mask-mode'); +allProperties.add('mask-origin'); +allProperties.add('mask-position'); +allProperties.add('mask-repeat'); +allProperties.add('mask-size'); +allProperties.add('mask-type'); +allProperties.add('max-block-size'); +allProperties.add('max-height'); +allProperties.add('max-inline-size'); +allProperties.add('max-lines'); +allProperties.add('max-width'); +allProperties.add('min-block-size'); +allProperties.add('min-height'); +allProperties.add('min-inline-size'); +allProperties.add('min-width'); +allProperties.add('mix-blend-mode'); +allProperties.add('nav-down'); +allProperties.add('nav-left'); +allProperties.add('nav-right'); +allProperties.add('nav-up'); +allProperties.add('object-fit'); +allProperties.add('object-position'); +allProperties.add('offset'); +allProperties.add('offset-after'); +allProperties.add('offset-anchor'); +allProperties.add('offset-before'); +allProperties.add('offset-distance'); +allProperties.add('offset-end'); +allProperties.add('offset-path'); +allProperties.add('offset-position'); +allProperties.add('offset-rotate'); +allProperties.add('offset-start'); +allProperties.add('opacity'); +allProperties.add('order'); +allProperties.add('orphans'); +allProperties.add('outline'); +allProperties.add('outline-color'); +allProperties.add('outline-offset'); +allProperties.add('outline-style'); +allProperties.add('outline-width'); +allProperties.add('overflow'); +allProperties.add('overflow-block'); +allProperties.add('overflow-inline'); +allProperties.add('overflow-wrap'); +allProperties.add('overflow-x'); +allProperties.add('overflow-y'); +allProperties.add('padding'); +allProperties.add('padding-block'); +allProperties.add('padding-block-end'); +allProperties.add('padding-block-start'); +allProperties.add('padding-bottom'); +allProperties.add('padding-inline'); +allProperties.add('padding-inline-end'); +allProperties.add('padding-inline-start'); +allProperties.add('padding-left'); +allProperties.add('padding-right'); +allProperties.add('padding-top'); +allProperties.add('page'); +allProperties.add('page-break-after'); +allProperties.add('page-break-before'); +allProperties.add('page-break-inside'); +allProperties.add('pause'); +allProperties.add('pause-after'); +allProperties.add('pause-before'); +allProperties.add('pitch'); +allProperties.add('pitch-range'); +allProperties.add('place-content'); +allProperties.add('place-items'); +allProperties.add('place-self'); +allProperties.add('play-during'); +allProperties.add('position'); +allProperties.add('presentation-level'); +allProperties.add('quotes'); +allProperties.add('region-fragment'); +allProperties.add('resize'); +allProperties.add('rest'); +allProperties.add('rest-after'); +allProperties.add('rest-before'); +allProperties.add('richness'); +allProperties.add('right'); +allProperties.add('row-gap'); +allProperties.add('ruby-align'); +allProperties.add('ruby-merge'); +allProperties.add('ruby-position'); +allProperties.add('running'); +allProperties.add('scroll-behavior'); +allProperties.add('scroll-margin'); +allProperties.add('scroll-margin-block'); +allProperties.add('scroll-margin-block-end'); +allProperties.add('scroll-margin-block-start'); +allProperties.add('scroll-margin-bottom'); +allProperties.add('scroll-margin-inline'); +allProperties.add('scroll-margin-inline-end'); +allProperties.add('scroll-margin-inline-start'); +allProperties.add('scroll-margin-left'); +allProperties.add('scroll-margin-right'); +allProperties.add('scroll-margin-top'); +allProperties.add('scroll-padding'); +allProperties.add('scroll-padding-block'); +allProperties.add('scroll-padding-block-end'); +allProperties.add('scroll-padding-block-start'); +allProperties.add('scroll-padding-bottom'); +allProperties.add('scroll-padding-inline'); +allProperties.add('scroll-padding-inline-end'); +allProperties.add('scroll-padding-inline-start'); +allProperties.add('scroll-padding-left'); +allProperties.add('scroll-padding-right'); +allProperties.add('scroll-padding-top'); +allProperties.add('scroll-snap-align'); +allProperties.add('scroll-snap-stop'); +allProperties.add('scroll-snap-type'); +allProperties.add('shape-image-threshold'); +allProperties.add('shape-inside'); +allProperties.add('shape-margin'); +allProperties.add('shape-outside'); +allProperties.add('speak'); +allProperties.add('speak-as'); +allProperties.add('speak-header'); +allProperties.add('speak-numeral'); +allProperties.add('speak-punctuation'); +allProperties.add('speech-rate'); +allProperties.add('stress'); +allProperties.add('string-set'); +allProperties.add('tab-size'); +allProperties.add('table-layout'); +allProperties.add('text-align'); +allProperties.add('text-align-all'); +allProperties.add('text-align-last'); +allProperties.add('text-combine-upright'); +allProperties.add('text-decoration'); +allProperties.add('text-decoration-color'); +allProperties.add('text-decoration-line'); +allProperties.add('text-decoration-style'); +allProperties.add('text-emphasis'); +allProperties.add('text-emphasis-color'); +allProperties.add('text-emphasis-position'); +allProperties.add('text-emphasis-style'); +allProperties.add('text-group-align'); +allProperties.add('text-indent'); +allProperties.add('text-justify'); +allProperties.add('text-orientation'); +allProperties.add('text-overflow'); +allProperties.add('text-shadow'); +allProperties.add('text-space-collapse'); +allProperties.add('text-space-trim'); +allProperties.add('text-spacing'); +allProperties.add('text-transform'); +allProperties.add('text-underline-position'); +allProperties.add('text-wrap'); +allProperties.add('top'); +allProperties.add('transform'); +allProperties.add('transform-box'); +allProperties.add('transform-origin'); +allProperties.add('transition'); +allProperties.add('transition-delay'); +allProperties.add('transition-duration'); +allProperties.add('transition-property'); +allProperties.add('transition-timing-function'); +allProperties.add('unicode-bidi'); +allProperties.add('user-select'); +allProperties.add('vertical-align'); +allProperties.add('visibility'); +allProperties.add('voice-balance'); +allProperties.add('voice-duration'); +allProperties.add('voice-family'); +allProperties.add('voice-pitch'); +allProperties.add('voice-range'); +allProperties.add('voice-rate'); +allProperties.add('voice-stress'); +allProperties.add('voice-volume'); +allProperties.add('volume'); +allProperties.add('white-space'); +allProperties.add('widows'); +allProperties.add('width'); +allProperties.add('will-change'); +allProperties.add('word-break'); +allProperties.add('word-spacing'); +allProperties.add('word-wrap'); +allProperties.add('wrap-after'); +allProperties.add('wrap-before'); +allProperties.add('wrap-flow'); +allProperties.add('wrap-inside'); +allProperties.add('wrap-through'); +allProperties.add('writing-mode'); +allProperties.add('z-index'); diff --git a/docs/js/node_modules/cssstyle/lib/constants.js b/docs/js/node_modules/cssstyle/lib/constants.js new file mode 100644 index 000000000..1b588695e --- /dev/null +++ b/docs/js/node_modules/cssstyle/lib/constants.js @@ -0,0 +1,6 @@ +'use strict'; + +module.exports.POSITION_AT_SHORTHAND = { + first: 0, + second: 1, +}; diff --git a/docs/js/node_modules/cssstyle/lib/implementedProperties.js b/docs/js/node_modules/cssstyle/lib/implementedProperties.js new file mode 100644 index 000000000..fd8cbca0a --- /dev/null +++ b/docs/js/node_modules/cssstyle/lib/implementedProperties.js @@ -0,0 +1,90 @@ +'use strict'; + +// autogenerated - 7/15/2019 + +/* + * + * https://www.w3.org/Style/CSS/all-properties.en.html + */ + +var implementedProperties = new Set(); +implementedProperties.add("azimuth"); +implementedProperties.add("background"); +implementedProperties.add("background-attachment"); +implementedProperties.add("background-color"); +implementedProperties.add("background-image"); +implementedProperties.add("background-position"); +implementedProperties.add("background-repeat"); +implementedProperties.add("border"); +implementedProperties.add("border-bottom"); +implementedProperties.add("border-bottom-color"); +implementedProperties.add("border-bottom-style"); +implementedProperties.add("border-bottom-width"); +implementedProperties.add("border-collapse"); +implementedProperties.add("border-color"); +implementedProperties.add("border-left"); +implementedProperties.add("border-left-color"); +implementedProperties.add("border-left-style"); +implementedProperties.add("border-left-width"); +implementedProperties.add("border-right"); +implementedProperties.add("border-right-color"); +implementedProperties.add("border-right-style"); +implementedProperties.add("border-right-width"); +implementedProperties.add("border-spacing"); +implementedProperties.add("border-style"); +implementedProperties.add("border-top"); +implementedProperties.add("border-top-color"); +implementedProperties.add("border-top-style"); +implementedProperties.add("border-top-width"); +implementedProperties.add("border-width"); +implementedProperties.add("bottom"); +implementedProperties.add("clear"); +implementedProperties.add("clip"); +implementedProperties.add("color"); +implementedProperties.add("css-float"); +implementedProperties.add("flex"); +implementedProperties.add("flex-basis"); +implementedProperties.add("flex-grow"); +implementedProperties.add("flex-shrink"); +implementedProperties.add("float"); +implementedProperties.add("flood-color"); +implementedProperties.add("font"); +implementedProperties.add("font-family"); +implementedProperties.add("font-size"); +implementedProperties.add("font-style"); +implementedProperties.add("font-variant"); +implementedProperties.add("font-weight"); +implementedProperties.add("height"); +implementedProperties.add("left"); +implementedProperties.add("lighting-color"); +implementedProperties.add("line-height"); +implementedProperties.add("margin"); +implementedProperties.add("margin-bottom"); +implementedProperties.add("margin-left"); +implementedProperties.add("margin-right"); +implementedProperties.add("margin-top"); +implementedProperties.add("opacity"); +implementedProperties.add("outline-color"); +implementedProperties.add("padding"); +implementedProperties.add("padding-bottom"); +implementedProperties.add("padding-left"); +implementedProperties.add("padding-right"); +implementedProperties.add("padding-top"); +implementedProperties.add("right"); +implementedProperties.add("stop-color"); +implementedProperties.add("text-line-through-color"); +implementedProperties.add("text-overline-color"); +implementedProperties.add("text-underline-color"); +implementedProperties.add("top"); +implementedProperties.add("webkit-border-after-color"); +implementedProperties.add("webkit-border-before-color"); +implementedProperties.add("webkit-border-end-color"); +implementedProperties.add("webkit-border-start-color"); +implementedProperties.add("webkit-column-rule-color"); +implementedProperties.add("webkit-match-nearest-mail-blockquote-color"); +implementedProperties.add("webkit-tap-highlight-color"); +implementedProperties.add("webkit-text-emphasis-color"); +implementedProperties.add("webkit-text-fill-color"); +implementedProperties.add("webkit-text-stroke-color"); +implementedProperties.add("width"); +module.exports = implementedProperties; diff --git a/docs/js/node_modules/cssstyle/lib/named_colors.json b/docs/js/node_modules/cssstyle/lib/named_colors.json new file mode 100644 index 000000000..63667a5a5 --- /dev/null +++ b/docs/js/node_modules/cssstyle/lib/named_colors.json @@ -0,0 +1,152 @@ +[ + "aliceblue", + "antiquewhite", + "aqua", + "aquamarine", + "azure", + "beige", + "bisque", + "black", + "blanchedalmond", + "blue", + "blueviolet", + "brown", + "burlywood", + "cadetblue", + "chartreuse", + "chocolate", + "coral", + "cornflowerblue", + "cornsilk", + "crimson", + "cyan", + "darkblue", + "darkcyan", + "darkgoldenrod", + "darkgray", + "darkgreen", + "darkgrey", + "darkkhaki", + "darkmagenta", + "darkolivegreen", + "darkorange", + "darkorchid", + "darkred", + "darksalmon", + "darkseagreen", + "darkslateblue", + "darkslategray", + "darkslategrey", + "darkturquoise", + "darkviolet", + "deeppink", + "deepskyblue", + "dimgray", + "dimgrey", + "dodgerblue", + "firebrick", + "floralwhite", + "forestgreen", + "fuchsia", + "gainsboro", + "ghostwhite", + "gold", + "goldenrod", + "gray", + "green", + "greenyellow", + "grey", + "honeydew", + "hotpink", + "indianred", + "indigo", + "ivory", + "khaki", + "lavender", + "lavenderblush", + "lawngreen", + "lemonchiffon", + "lightblue", + "lightcoral", + "lightcyan", + "lightgoldenrodyellow", + "lightgray", + "lightgreen", + "lightgrey", + "lightpink", + "lightsalmon", + "lightseagreen", + "lightskyblue", + "lightslategray", + "lightslategrey", + "lightsteelblue", + "lightyellow", + "lime", + "limegreen", + "linen", + "magenta", + "maroon", + "mediumaquamarine", + "mediumblue", + "mediumorchid", + "mediumpurple", + "mediumseagreen", + "mediumslateblue", + "mediumspringgreen", + "mediumturquoise", + "mediumvioletred", + "midnightblue", + "mintcream", + "mistyrose", + "moccasin", + "navajowhite", + "navy", + "oldlace", + "olive", + "olivedrab", + "orange", + "orangered", + "orchid", + "palegoldenrod", + "palegreen", + "paleturquoise", + "palevioletred", + "papayawhip", + "peachpuff", + "peru", + "pink", + "plum", + "powderblue", + "purple", + "rebeccapurple", + "red", + "rosybrown", + "royalblue", + "saddlebrown", + "salmon", + "sandybrown", + "seagreen", + "seashell", + "sienna", + "silver", + "skyblue", + "slateblue", + "slategray", + "slategrey", + "snow", + "springgreen", + "steelblue", + "tan", + "teal", + "thistle", + "tomato", + "turquoise", + "violet", + "wheat", + "white", + "whitesmoke", + "yellow", + "yellowgreen", + "transparent", + "currentcolor" +] diff --git a/docs/js/node_modules/cssstyle/lib/parsers.js b/docs/js/node_modules/cssstyle/lib/parsers.js new file mode 100644 index 000000000..24021ba0f --- /dev/null +++ b/docs/js/node_modules/cssstyle/lib/parsers.js @@ -0,0 +1,697 @@ +/********************************************************************* + * These are commonly used parsers for CSS Values they take a string * + * to parse and return a string after it's been converted, if needed * + ********************************************************************/ +'use strict'; + +const namedColors = require('./named_colors.json'); + +exports.TYPES = { + INTEGER: 1, + NUMBER: 2, + LENGTH: 3, + PERCENT: 4, + URL: 5, + COLOR: 6, + STRING: 7, + ANGLE: 8, + KEYWORD: 9, + NULL_OR_EMPTY_STR: 10, +}; + +// rough regular expressions +var integerRegEx = /^[-+]?[0-9]+$/; +var numberRegEx = /^[-+]?[0-9]*\.[0-9]+$/; +var lengthRegEx = /^(0|[-+]?[0-9]*\.?[0-9]+(in|cm|em|mm|pt|pc|px|ex|rem|vh|vw))$/; +var percentRegEx = /^[-+]?[0-9]*\.?[0-9]+%$/; +var urlRegEx = /^url\(\s*([^)]*)\s*\)$/; +var stringRegEx = /^("[^"]*"|'[^']*')$/; +var colorRegEx1 = /^#[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F]([0-9a-fA-F][0-9a-fA-F][0-9a-fA-F])?$/; +var colorRegEx2 = /^rgb\(([^)]*)\)$/; +var colorRegEx3 = /^rgba\(([^)]*)\)$/; +var colorRegEx4 = /^hsla?\(\s*(-?\d+|-?\d*.\d+)\s*,\s*(-?\d+|-?\d*.\d+)%\s*,\s*(-?\d+|-?\d*.\d+)%\s*(,\s*(-?\d+|-?\d*.\d+)\s*)?\)/; +var angleRegEx = /^([-+]?[0-9]*\.?[0-9]+)(deg|grad|rad)$/; + +// This will return one of the above types based on the passed in string +exports.valueType = function valueType(val) { + if (val === '' || val === null) { + return exports.TYPES.NULL_OR_EMPTY_STR; + } + if (typeof val === 'number') { + val = val.toString(); + } + + if (typeof val !== 'string') { + return undefined; + } + + if (integerRegEx.test(val)) { + return exports.TYPES.INTEGER; + } + if (numberRegEx.test(val)) { + return exports.TYPES.NUMBER; + } + if (lengthRegEx.test(val)) { + return exports.TYPES.LENGTH; + } + if (percentRegEx.test(val)) { + return exports.TYPES.PERCENT; + } + if (urlRegEx.test(val)) { + return exports.TYPES.URL; + } + if (stringRegEx.test(val)) { + return exports.TYPES.STRING; + } + if (angleRegEx.test(val)) { + return exports.TYPES.ANGLE; + } + if (colorRegEx1.test(val)) { + return exports.TYPES.COLOR; + } + var res = colorRegEx2.exec(val); + var parts; + if (res !== null) { + parts = res[1].split(/\s*,\s*/); + if (parts.length !== 3) { + return undefined; + } + if ( + parts.every(percentRegEx.test.bind(percentRegEx)) || + parts.every(integerRegEx.test.bind(integerRegEx)) + ) { + return exports.TYPES.COLOR; + } + return undefined; + } + res = colorRegEx3.exec(val); + if (res !== null) { + parts = res[1].split(/\s*,\s*/); + if (parts.length !== 4) { + return undefined; + } + if ( + parts.slice(0, 3).every(percentRegEx.test.bind(percentRegEx)) || + parts.every(integerRegEx.test.bind(integerRegEx)) + ) { + if (numberRegEx.test(parts[3])) { + return exports.TYPES.COLOR; + } + } + return undefined; + } + + if (colorRegEx4.test(val)) { + return exports.TYPES.COLOR; + } + + // could still be a color, one of the standard keyword colors + val = val.toLowerCase(); + + if (namedColors.includes(val)) { + return exports.TYPES.COLOR; + } + + switch (val) { + // the following are deprecated in CSS3 + case 'activeborder': + case 'activecaption': + case 'appworkspace': + case 'background': + case 'buttonface': + case 'buttonhighlight': + case 'buttonshadow': + case 'buttontext': + case 'captiontext': + case 'graytext': + case 'highlight': + case 'highlighttext': + case 'inactiveborder': + case 'inactivecaption': + case 'inactivecaptiontext': + case 'infobackground': + case 'infotext': + case 'menu': + case 'menutext': + case 'scrollbar': + case 'threeddarkshadow': + case 'threedface': + case 'threedhighlight': + case 'threedlightshadow': + case 'threedshadow': + case 'window': + case 'windowframe': + case 'windowtext': + return exports.TYPES.COLOR; + default: + return exports.TYPES.KEYWORD; + } +}; + +exports.parseInteger = function parseInteger(val) { + var type = exports.valueType(val); + if (type === exports.TYPES.NULL_OR_EMPTY_STR) { + return val; + } + if (type !== exports.TYPES.INTEGER) { + return undefined; + } + return String(parseInt(val, 10)); +}; + +exports.parseNumber = function parseNumber(val) { + var type = exports.valueType(val); + if (type === exports.TYPES.NULL_OR_EMPTY_STR) { + return val; + } + if (type !== exports.TYPES.NUMBER && type !== exports.TYPES.INTEGER) { + return undefined; + } + return String(parseFloat(val)); +}; + +exports.parseLength = function parseLength(val) { + if (val === 0 || val === '0') { + return '0px'; + } + var type = exports.valueType(val); + if (type === exports.TYPES.NULL_OR_EMPTY_STR) { + return val; + } + if (type !== exports.TYPES.LENGTH) { + return undefined; + } + return val; +}; + +exports.parsePercent = function parsePercent(val) { + if (val === 0 || val === '0') { + return '0%'; + } + var type = exports.valueType(val); + if (type === exports.TYPES.NULL_OR_EMPTY_STR) { + return val; + } + if (type !== exports.TYPES.PERCENT) { + return undefined; + } + return val; +}; + +// either a length or a percent +exports.parseMeasurement = function parseMeasurement(val) { + var length = exports.parseLength(val); + if (length !== undefined) { + return length; + } + return exports.parsePercent(val); +}; + +exports.parseUrl = function parseUrl(val) { + var type = exports.valueType(val); + if (type === exports.TYPES.NULL_OR_EMPTY_STR) { + return val; + } + var res = urlRegEx.exec(val); + // does it match the regex? + if (!res) { + return undefined; + } + var str = res[1]; + // if it starts with single or double quotes, does it end with the same? + if ((str[0] === '"' || str[0] === "'") && str[0] !== str[str.length - 1]) { + return undefined; + } + if (str[0] === '"' || str[0] === "'") { + str = str.substr(1, str.length - 2); + } + + var i; + for (i = 0; i < str.length; i++) { + switch (str[i]) { + case '(': + case ')': + case ' ': + case '\t': + case '\n': + case "'": + case '"': + return undefined; + case '\\': + i++; + break; + } + } + + return 'url(' + str + ')'; +}; + +exports.parseString = function parseString(val) { + var type = exports.valueType(val); + if (type === exports.TYPES.NULL_OR_EMPTY_STR) { + return val; + } + if (type !== exports.TYPES.STRING) { + return undefined; + } + var i; + for (i = 1; i < val.length - 1; i++) { + switch (val[i]) { + case val[0]: + return undefined; + case '\\': + i++; + while (i < val.length - 1 && /[0-9A-Fa-f]/.test(val[i])) { + i++; + } + break; + } + } + if (i >= val.length) { + return undefined; + } + return val; +}; + +exports.parseColor = function parseColor(val) { + var type = exports.valueType(val); + if (type === exports.TYPES.NULL_OR_EMPTY_STR) { + return val; + } + var red, + green, + blue, + hue, + saturation, + lightness, + alpha = 1; + var parts; + var res = colorRegEx1.exec(val); + // is it #aaa or #ababab + if (res) { + var hex = val.substr(1); + if (hex.length === 3) { + hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2]; + } + red = parseInt(hex.substr(0, 2), 16); + green = parseInt(hex.substr(2, 2), 16); + blue = parseInt(hex.substr(4, 2), 16); + return 'rgb(' + red + ', ' + green + ', ' + blue + ')'; + } + + res = colorRegEx2.exec(val); + if (res) { + parts = res[1].split(/\s*,\s*/); + if (parts.length !== 3) { + return undefined; + } + if (parts.every(percentRegEx.test.bind(percentRegEx))) { + red = Math.floor((parseFloat(parts[0].slice(0, -1)) * 255) / 100); + green = Math.floor((parseFloat(parts[1].slice(0, -1)) * 255) / 100); + blue = Math.floor((parseFloat(parts[2].slice(0, -1)) * 255) / 100); + } else if (parts.every(integerRegEx.test.bind(integerRegEx))) { + red = parseInt(parts[0], 10); + green = parseInt(parts[1], 10); + blue = parseInt(parts[2], 10); + } else { + return undefined; + } + red = Math.min(255, Math.max(0, red)); + green = Math.min(255, Math.max(0, green)); + blue = Math.min(255, Math.max(0, blue)); + return 'rgb(' + red + ', ' + green + ', ' + blue + ')'; + } + + res = colorRegEx3.exec(val); + if (res) { + parts = res[1].split(/\s*,\s*/); + if (parts.length !== 4) { + return undefined; + } + if (parts.slice(0, 3).every(percentRegEx.test.bind(percentRegEx))) { + red = Math.floor((parseFloat(parts[0].slice(0, -1)) * 255) / 100); + green = Math.floor((parseFloat(parts[1].slice(0, -1)) * 255) / 100); + blue = Math.floor((parseFloat(parts[2].slice(0, -1)) * 255) / 100); + alpha = parseFloat(parts[3]); + } else if (parts.slice(0, 3).every(integerRegEx.test.bind(integerRegEx))) { + red = parseInt(parts[0], 10); + green = parseInt(parts[1], 10); + blue = parseInt(parts[2], 10); + alpha = parseFloat(parts[3]); + } else { + return undefined; + } + if (isNaN(alpha)) { + alpha = 1; + } + red = Math.min(255, Math.max(0, red)); + green = Math.min(255, Math.max(0, green)); + blue = Math.min(255, Math.max(0, blue)); + alpha = Math.min(1, Math.max(0, alpha)); + if (alpha === 1) { + return 'rgb(' + red + ', ' + green + ', ' + blue + ')'; + } + return 'rgba(' + red + ', ' + green + ', ' + blue + ', ' + alpha + ')'; + } + + res = colorRegEx4.exec(val); + if (res) { + const [, _hue, _saturation, _lightness, _alphaString = ''] = res; + const _alpha = parseFloat(_alphaString.replace(',', '').trim()); + if (!_hue || !_saturation || !_lightness) { + return undefined; + } + hue = parseFloat(_hue); + saturation = parseInt(_saturation, 10); + lightness = parseInt(_lightness, 10); + if (_alpha && numberRegEx.test(_alpha)) { + alpha = parseFloat(_alpha); + } + if (!_alphaString || alpha === 1) { + return 'hsl(' + hue + ', ' + saturation + '%, ' + lightness + '%)'; + } + return 'hsla(' + hue + ', ' + saturation + '%, ' + lightness + '%, ' + alpha + ')'; + } + + if (type === exports.TYPES.COLOR) { + return val; + } + return undefined; +}; + +exports.parseAngle = function parseAngle(val) { + var type = exports.valueType(val); + if (type === exports.TYPES.NULL_OR_EMPTY_STR) { + return val; + } + if (type !== exports.TYPES.ANGLE) { + return undefined; + } + var res = angleRegEx.exec(val); + var flt = parseFloat(res[1]); + if (res[2] === 'rad') { + flt *= 180 / Math.PI; + } else if (res[2] === 'grad') { + flt *= 360 / 400; + } + + while (flt < 0) { + flt += 360; + } + while (flt > 360) { + flt -= 360; + } + return flt + 'deg'; +}; + +exports.parseKeyword = function parseKeyword(val, valid_keywords) { + var type = exports.valueType(val); + if (type === exports.TYPES.NULL_OR_EMPTY_STR) { + return val; + } + if (type !== exports.TYPES.KEYWORD) { + return undefined; + } + val = val.toString().toLowerCase(); + var i; + for (i = 0; i < valid_keywords.length; i++) { + if (valid_keywords[i].toLowerCase() === val) { + return valid_keywords[i]; + } + } + return undefined; +}; + +// utility to translate from border-width to borderWidth +var dashedToCamelCase = function(dashed) { + var i; + var camel = ''; + var nextCap = false; + for (i = 0; i < dashed.length; i++) { + if (dashed[i] !== '-') { + camel += nextCap ? dashed[i].toUpperCase() : dashed[i]; + nextCap = false; + } else { + nextCap = true; + } + } + return camel; +}; +exports.dashedToCamelCase = dashedToCamelCase; + +var is_space = /\s/; +var opening_deliminators = ['"', "'", '(']; +var closing_deliminators = ['"', "'", ')']; +// this splits on whitespace, but keeps quoted and parened parts together +var getParts = function(str) { + var deliminator_stack = []; + var length = str.length; + var i; + var parts = []; + var current_part = ''; + var opening_index; + var closing_index; + for (i = 0; i < length; i++) { + opening_index = opening_deliminators.indexOf(str[i]); + closing_index = closing_deliminators.indexOf(str[i]); + if (is_space.test(str[i])) { + if (deliminator_stack.length === 0) { + if (current_part !== '') { + parts.push(current_part); + } + current_part = ''; + } else { + current_part += str[i]; + } + } else { + if (str[i] === '\\') { + i++; + current_part += str[i]; + } else { + current_part += str[i]; + if ( + closing_index !== -1 && + closing_index === deliminator_stack[deliminator_stack.length - 1] + ) { + deliminator_stack.pop(); + } else if (opening_index !== -1) { + deliminator_stack.push(opening_index); + } + } + } + } + if (current_part !== '') { + parts.push(current_part); + } + return parts; +}; + +/* + * this either returns undefined meaning that it isn't valid + * or returns an object where the keys are dashed short + * hand properties and the values are the values to set + * on them + */ +exports.shorthandParser = function parse(v, shorthand_for) { + var obj = {}; + var type = exports.valueType(v); + if (type === exports.TYPES.NULL_OR_EMPTY_STR) { + Object.keys(shorthand_for).forEach(function(property) { + obj[property] = ''; + }); + return obj; + } + + if (typeof v === 'number') { + v = v.toString(); + } + + if (typeof v !== 'string') { + return undefined; + } + + if (v.toLowerCase() === 'inherit') { + return {}; + } + var parts = getParts(v); + var valid = true; + parts.forEach(function(part, i) { + var part_valid = false; + Object.keys(shorthand_for).forEach(function(property) { + if (shorthand_for[property].isValid(part, i)) { + part_valid = true; + obj[property] = part; + } + }); + valid = valid && part_valid; + }); + if (!valid) { + return undefined; + } + return obj; +}; + +exports.shorthandSetter = function(property, shorthand_for) { + return function(v) { + var obj = exports.shorthandParser(v, shorthand_for); + if (obj === undefined) { + return; + } + //console.log('shorthandSetter for:', property, 'obj:', obj); + Object.keys(obj).forEach(function(subprop) { + // in case subprop is an implicit property, this will clear + // *its* subpropertiesX + var camel = dashedToCamelCase(subprop); + this[camel] = obj[subprop]; + // in case it gets translated into something else (0 -> 0px) + obj[subprop] = this[camel]; + this.removeProperty(subprop); + // don't add in empty properties + if (obj[subprop] !== '') { + this._values[subprop] = obj[subprop]; + } + }, this); + Object.keys(shorthand_for).forEach(function(subprop) { + if (!obj.hasOwnProperty(subprop)) { + this.removeProperty(subprop); + delete this._values[subprop]; + } + }, this); + // in case the value is something like 'none' that removes all values, + // check that the generated one is not empty, first remove the property + // if it already exists, then call the shorthandGetter, if it's an empty + // string, don't set the property + this.removeProperty(property); + var calculated = exports.shorthandGetter(property, shorthand_for).call(this); + if (calculated !== '') { + this._setProperty(property, calculated); + } + }; +}; + +exports.shorthandGetter = function(property, shorthand_for) { + return function() { + if (this._values[property] !== undefined) { + return this.getPropertyValue(property); + } + return Object.keys(shorthand_for) + .map(function(subprop) { + return this.getPropertyValue(subprop); + }, this) + .filter(function(value) { + return value !== ''; + }) + .join(' '); + }; +}; + +// isValid(){1,4} | inherit +// if one, it applies to all +// if two, the first applies to the top and bottom, and the second to left and right +// if three, the first applies to the top, the second to left and right, the third bottom +// if four, top, right, bottom, left +exports.implicitSetter = function(property_before, property_after, isValid, parser) { + property_after = property_after || ''; + if (property_after !== '') { + property_after = '-' + property_after; + } + var part_names = ['top', 'right', 'bottom', 'left']; + + return function(v) { + if (typeof v === 'number') { + v = v.toString(); + } + if (typeof v !== 'string') { + return undefined; + } + var parts; + if (v.toLowerCase() === 'inherit' || v === '') { + parts = [v]; + } else { + parts = getParts(v); + } + if (parts.length < 1 || parts.length > 4) { + return undefined; + } + + if (!parts.every(isValid)) { + return undefined; + } + + parts = parts.map(function(part) { + return parser(part); + }); + this._setProperty(property_before + property_after, parts.join(' ')); + if (parts.length === 1) { + parts[1] = parts[0]; + } + if (parts.length === 2) { + parts[2] = parts[0]; + } + if (parts.length === 3) { + parts[3] = parts[1]; + } + + for (var i = 0; i < 4; i++) { + var property = property_before + '-' + part_names[i] + property_after; + this.removeProperty(property); + if (parts[i] !== '') { + this._values[property] = parts[i]; + } + } + return v; + }; +}; + +// +// Companion to implicitSetter, but for the individual parts. +// This sets the individual value, and checks to see if all four +// sub-parts are set. If so, it sets the shorthand version and removes +// the individual parts from the cssText. +// +exports.subImplicitSetter = function(prefix, part, isValid, parser) { + var property = prefix + '-' + part; + var subparts = [prefix + '-top', prefix + '-right', prefix + '-bottom', prefix + '-left']; + + return function(v) { + if (typeof v === 'number') { + v = v.toString(); + } + if (typeof v !== 'string') { + return undefined; + } + if (!isValid(v)) { + return undefined; + } + v = parser(v); + this._setProperty(property, v); + var parts = []; + for (var i = 0; i < 4; i++) { + if (this._values[subparts[i]] == null || this._values[subparts[i]] === '') { + break; + } + parts.push(this._values[subparts[i]]); + } + if (parts.length === 4) { + for (i = 0; i < 4; i++) { + this.removeProperty(subparts[i]); + this._values[subparts[i]] = parts[i]; + } + this._setProperty(prefix, parts.join(' ')); + } + return v; + }; +}; + +var camel_to_dashed = /[A-Z]/g; +var first_segment = /^\([^-]\)-/; +var vendor_prefixes = ['o', 'moz', 'ms', 'webkit']; +exports.camelToDashed = function(camel_case) { + var match; + var dashed = camel_case.replace(camel_to_dashed, '-$&').toLowerCase(); + match = dashed.match(first_segment); + if (match && vendor_prefixes.indexOf(match[1]) !== -1) { + dashed = '-' + dashed; + } + return dashed; +}; diff --git a/docs/js/node_modules/cssstyle/lib/properties.js b/docs/js/node_modules/cssstyle/lib/properties.js new file mode 100644 index 000000000..2b4939c83 --- /dev/null +++ b/docs/js/node_modules/cssstyle/lib/properties.js @@ -0,0 +1,1826 @@ +'use strict'; + +// autogenerated - 7/15/2019 + +/* + * + * https://www.w3.org/Style/CSS/all-properties.en.html + */ + +var external_dependency_parsers_0 = require("./parsers.js"); + +var external_dependency_constants_1 = require("./constants.js"); + +var azimuth_export_definition; +azimuth_export_definition = { + set: function (v) { + var valueType = external_dependency_parsers_0.valueType(v); + + if (valueType === external_dependency_parsers_0.TYPES.ANGLE) { + return this._setProperty('azimuth', external_dependency_parsers_0.parseAngle(v)); + } + + if (valueType === external_dependency_parsers_0.TYPES.KEYWORD) { + var keywords = v.toLowerCase().trim().split(/\s+/); + var hasBehind = false; + + if (keywords.length > 2) { + return; + } + + var behindIndex = keywords.indexOf('behind'); + hasBehind = behindIndex !== -1; + + if (keywords.length === 2) { + if (!hasBehind) { + return; + } + + keywords.splice(behindIndex, 1); + } + + if (keywords[0] === 'leftwards' || keywords[0] === 'rightwards') { + if (hasBehind) { + return; + } + + return this._setProperty('azimuth', keywords[0]); + } + + if (keywords[0] === 'behind') { + return this._setProperty('azimuth', '180deg'); + } + + switch (keywords[0]) { + case 'left-side': + return this._setProperty('azimuth', '270deg'); + + case 'far-left': + return this._setProperty('azimuth', (hasBehind ? 240 : 300) + 'deg'); + + case 'left': + return this._setProperty('azimuth', (hasBehind ? 220 : 320) + 'deg'); + + case 'center-left': + return this._setProperty('azimuth', (hasBehind ? 200 : 340) + 'deg'); + + case 'center': + return this._setProperty('azimuth', (hasBehind ? 180 : 0) + 'deg'); + + case 'center-right': + return this._setProperty('azimuth', (hasBehind ? 160 : 20) + 'deg'); + + case 'right': + return this._setProperty('azimuth', (hasBehind ? 140 : 40) + 'deg'); + + case 'far-right': + return this._setProperty('azimuth', (hasBehind ? 120 : 60) + 'deg'); + + case 'right-side': + return this._setProperty('azimuth', '90deg'); + + default: + return; + } + } + }, + get: function () { + return this.getPropertyValue('azimuth'); + }, + enumerable: true, + configurable: true +}; +var backgroundColor_export_isValid, backgroundColor_export_definition; + +var backgroundColor_local_var_parse = function parse(v) { + var parsed = external_dependency_parsers_0.parseColor(v); + + if (parsed !== undefined) { + return parsed; + } + + if (external_dependency_parsers_0.valueType(v) === external_dependency_parsers_0.TYPES.KEYWORD && (v.toLowerCase() === 'transparent' || v.toLowerCase() === 'inherit')) { + return v; + } + + return undefined; +}; + +backgroundColor_export_isValid = function isValid(v) { + return backgroundColor_local_var_parse(v) !== undefined; +}; + +backgroundColor_export_definition = { + set: function (v) { + var parsed = backgroundColor_local_var_parse(v); + + if (parsed === undefined) { + return; + } + + this._setProperty('background-color', parsed); + }, + get: function () { + return this.getPropertyValue('background-color'); + }, + enumerable: true, + configurable: true +}; +var backgroundImage_export_isValid, backgroundImage_export_definition; + +var backgroundImage_local_var_parse = function parse(v) { + var parsed = external_dependency_parsers_0.parseUrl(v); + + if (parsed !== undefined) { + return parsed; + } + + if (external_dependency_parsers_0.valueType(v) === external_dependency_parsers_0.TYPES.KEYWORD && (v.toLowerCase() === 'none' || v.toLowerCase() === 'inherit')) { + return v; + } + + return undefined; +}; + +backgroundImage_export_isValid = function isValid(v) { + return backgroundImage_local_var_parse(v) !== undefined; +}; + +backgroundImage_export_definition = { + set: function (v) { + this._setProperty('background-image', backgroundImage_local_var_parse(v)); + }, + get: function () { + return this.getPropertyValue('background-image'); + }, + enumerable: true, + configurable: true +}; +var backgroundRepeat_export_isValid, backgroundRepeat_export_definition; + +var backgroundRepeat_local_var_parse = function parse(v) { + if (external_dependency_parsers_0.valueType(v) === external_dependency_parsers_0.TYPES.KEYWORD && (v.toLowerCase() === 'repeat' || v.toLowerCase() === 'repeat-x' || v.toLowerCase() === 'repeat-y' || v.toLowerCase() === 'no-repeat' || v.toLowerCase() === 'inherit')) { + return v; + } + + return undefined; +}; + +backgroundRepeat_export_isValid = function isValid(v) { + return backgroundRepeat_local_var_parse(v) !== undefined; +}; + +backgroundRepeat_export_definition = { + set: function (v) { + this._setProperty('background-repeat', backgroundRepeat_local_var_parse(v)); + }, + get: function () { + return this.getPropertyValue('background-repeat'); + }, + enumerable: true, + configurable: true +}; +var backgroundAttachment_export_isValid, backgroundAttachment_export_definition; + +var backgroundAttachment_local_var_isValid = backgroundAttachment_export_isValid = function isValid(v) { + return external_dependency_parsers_0.valueType(v) === external_dependency_parsers_0.TYPES.KEYWORD && (v.toLowerCase() === 'scroll' || v.toLowerCase() === 'fixed' || v.toLowerCase() === 'inherit'); +}; + +backgroundAttachment_export_definition = { + set: function (v) { + if (!backgroundAttachment_local_var_isValid(v)) { + return; + } + + this._setProperty('background-attachment', v); + }, + get: function () { + return this.getPropertyValue('background-attachment'); + }, + enumerable: true, + configurable: true +}; +var backgroundPosition_export_isValid, backgroundPosition_export_definition; +var backgroundPosition_local_var_valid_keywords = ['top', 'center', 'bottom', 'left', 'right']; + +var backgroundPosition_local_var_parse = function parse(v) { + if (v === '' || v === null) { + return undefined; + } + + var parts = v.split(/\s+/); + + if (parts.length > 2 || parts.length < 1) { + return undefined; + } + + var types = []; + parts.forEach(function (part, index) { + types[index] = external_dependency_parsers_0.valueType(part); + }); + + if (parts.length === 1) { + if (types[0] === external_dependency_parsers_0.TYPES.LENGTH || types[0] === external_dependency_parsers_0.TYPES.PERCENT) { + return v; + } + + if (types[0] === external_dependency_parsers_0.TYPES.KEYWORD) { + if (backgroundPosition_local_var_valid_keywords.indexOf(v.toLowerCase()) !== -1 || v.toLowerCase() === 'inherit') { + return v; + } + } + + return undefined; + } + + if ((types[0] === external_dependency_parsers_0.TYPES.LENGTH || types[0] === external_dependency_parsers_0.TYPES.PERCENT) && (types[1] === external_dependency_parsers_0.TYPES.LENGTH || types[1] === external_dependency_parsers_0.TYPES.PERCENT)) { + return v; + } + + if (types[0] !== external_dependency_parsers_0.TYPES.KEYWORD || types[1] !== external_dependency_parsers_0.TYPES.KEYWORD) { + return undefined; + } + + if (backgroundPosition_local_var_valid_keywords.indexOf(parts[0]) !== -1 && backgroundPosition_local_var_valid_keywords.indexOf(parts[1]) !== -1) { + return v; + } + + return undefined; +}; + +backgroundPosition_export_isValid = function isValid(v) { + return backgroundPosition_local_var_parse(v) !== undefined; +}; + +backgroundPosition_export_definition = { + set: function (v) { + this._setProperty('background-position', backgroundPosition_local_var_parse(v)); + }, + get: function () { + return this.getPropertyValue('background-position'); + }, + enumerable: true, + configurable: true +}; +var background_export_definition; +var background_local_var_shorthand_for = { + 'background-color': { + isValid: backgroundColor_export_isValid, + definition: backgroundColor_export_definition + }, + 'background-image': { + isValid: backgroundImage_export_isValid, + definition: backgroundImage_export_definition + }, + 'background-repeat': { + isValid: backgroundRepeat_export_isValid, + definition: backgroundRepeat_export_definition + }, + 'background-attachment': { + isValid: backgroundAttachment_export_isValid, + definition: backgroundAttachment_export_definition + }, + 'background-position': { + isValid: backgroundPosition_export_isValid, + definition: backgroundPosition_export_definition + } +}; +background_export_definition = { + set: external_dependency_parsers_0.shorthandSetter('background', background_local_var_shorthand_for), + get: external_dependency_parsers_0.shorthandGetter('background', background_local_var_shorthand_for), + enumerable: true, + configurable: true +}; +var borderWidth_export_isValid, borderWidth_export_definition; +// the valid border-widths: +var borderWidth_local_var_widths = ['thin', 'medium', 'thick']; + +borderWidth_export_isValid = function parse(v) { + var length = external_dependency_parsers_0.parseLength(v); + + if (length !== undefined) { + return true; + } + + if (typeof v !== 'string') { + return false; + } + + if (v === '') { + return true; + } + + v = v.toLowerCase(); + + if (borderWidth_local_var_widths.indexOf(v) === -1) { + return false; + } + + return true; +}; + +var borderWidth_local_var_isValid = borderWidth_export_isValid; + +var borderWidth_local_var_parser = function (v) { + var length = external_dependency_parsers_0.parseLength(v); + + if (length !== undefined) { + return length; + } + + if (borderWidth_local_var_isValid(v)) { + return v.toLowerCase(); + } + + return undefined; +}; + +borderWidth_export_definition = { + set: external_dependency_parsers_0.implicitSetter('border', 'width', borderWidth_local_var_isValid, borderWidth_local_var_parser), + get: function () { + return this.getPropertyValue('border-width'); + }, + enumerable: true, + configurable: true +}; +var borderStyle_export_isValid, borderStyle_export_definition; +// the valid border-styles: +var borderStyle_local_var_styles = ['none', 'hidden', 'dotted', 'dashed', 'solid', 'double', 'groove', 'ridge', 'inset', 'outset']; + +borderStyle_export_isValid = function parse(v) { + return typeof v === 'string' && (v === '' || borderStyle_local_var_styles.indexOf(v) !== -1); +}; + +var borderStyle_local_var_isValid = borderStyle_export_isValid; + +var borderStyle_local_var_parser = function (v) { + if (borderStyle_local_var_isValid(v)) { + return v.toLowerCase(); + } + + return undefined; +}; + +borderStyle_export_definition = { + set: external_dependency_parsers_0.implicitSetter('border', 'style', borderStyle_local_var_isValid, borderStyle_local_var_parser), + get: function () { + return this.getPropertyValue('border-style'); + }, + enumerable: true, + configurable: true +}; +var borderColor_export_isValid, borderColor_export_definition; + +borderColor_export_isValid = function parse(v) { + if (typeof v !== 'string') { + return false; + } + + return v === '' || v.toLowerCase() === 'transparent' || external_dependency_parsers_0.valueType(v) === external_dependency_parsers_0.TYPES.COLOR; +}; + +var borderColor_local_var_isValid = borderColor_export_isValid; + +var borderColor_local_var_parser = function (v) { + if (borderColor_local_var_isValid(v)) { + return v.toLowerCase(); + } + + return undefined; +}; + +borderColor_export_definition = { + set: external_dependency_parsers_0.implicitSetter('border', 'color', borderColor_local_var_isValid, borderColor_local_var_parser), + get: function () { + return this.getPropertyValue('border-color'); + }, + enumerable: true, + configurable: true +}; +var border_export_definition; +var border_local_var_shorthand_for = { + 'border-width': { + isValid: borderWidth_export_isValid, + definition: borderWidth_export_definition + }, + 'border-style': { + isValid: borderStyle_export_isValid, + definition: borderStyle_export_definition + }, + 'border-color': { + isValid: borderColor_export_isValid, + definition: borderColor_export_definition + } +}; +var border_local_var_myShorthandSetter = external_dependency_parsers_0.shorthandSetter('border', border_local_var_shorthand_for); +var border_local_var_myShorthandGetter = external_dependency_parsers_0.shorthandGetter('border', border_local_var_shorthand_for); +border_export_definition = { + set: function (v) { + if (v.toString().toLowerCase() === 'none') { + v = ''; + } + + border_local_var_myShorthandSetter.call(this, v); + this.removeProperty('border-top'); + this.removeProperty('border-left'); + this.removeProperty('border-right'); + this.removeProperty('border-bottom'); + this._values['border-top'] = this._values.border; + this._values['border-left'] = this._values.border; + this._values['border-right'] = this._values.border; + this._values['border-bottom'] = this._values.border; + }, + get: border_local_var_myShorthandGetter, + enumerable: true, + configurable: true +}; +var borderBottomWidth_export_isValid, borderBottomWidth_export_definition; +var borderBottomWidth_local_var_isValid = borderBottomWidth_export_isValid = borderWidth_export_isValid; +borderBottomWidth_export_definition = { + set: function (v) { + if (borderBottomWidth_local_var_isValid(v)) { + this._setProperty('border-bottom-width', v); + } + }, + get: function () { + return this.getPropertyValue('border-bottom-width'); + }, + enumerable: true, + configurable: true +}; +var borderBottomStyle_export_isValid, borderBottomStyle_export_definition; +borderBottomStyle_export_isValid = borderStyle_export_isValid; +borderBottomStyle_export_definition = { + set: function (v) { + if (borderStyle_export_isValid(v)) { + if (v.toLowerCase() === 'none') { + v = ''; + this.removeProperty('border-bottom-width'); + } + + this._setProperty('border-bottom-style', v); + } + }, + get: function () { + return this.getPropertyValue('border-bottom-style'); + }, + enumerable: true, + configurable: true +}; +var borderBottomColor_export_isValid, borderBottomColor_export_definition; +var borderBottomColor_local_var_isValid = borderBottomColor_export_isValid = borderColor_export_isValid; +borderBottomColor_export_definition = { + set: function (v) { + if (borderBottomColor_local_var_isValid(v)) { + this._setProperty('border-bottom-color', v); + } + }, + get: function () { + return this.getPropertyValue('border-bottom-color'); + }, + enumerable: true, + configurable: true +}; +var borderBottom_export_definition; +var borderBottom_local_var_shorthand_for = { + 'border-bottom-width': { + isValid: borderBottomWidth_export_isValid, + definition: borderBottomWidth_export_definition + }, + 'border-bottom-style': { + isValid: borderBottomStyle_export_isValid, + definition: borderBottomStyle_export_definition + }, + 'border-bottom-color': { + isValid: borderBottomColor_export_isValid, + definition: borderBottomColor_export_definition + } +}; +borderBottom_export_definition = { + set: external_dependency_parsers_0.shorthandSetter('border-bottom', borderBottom_local_var_shorthand_for), + get: external_dependency_parsers_0.shorthandGetter('border-bottom', borderBottom_local_var_shorthand_for), + enumerable: true, + configurable: true +}; +var borderCollapse_export_definition; + +var borderCollapse_local_var_parse = function parse(v) { + if (external_dependency_parsers_0.valueType(v) === external_dependency_parsers_0.TYPES.KEYWORD && (v.toLowerCase() === 'collapse' || v.toLowerCase() === 'separate' || v.toLowerCase() === 'inherit')) { + return v; + } + + return undefined; +}; + +borderCollapse_export_definition = { + set: function (v) { + this._setProperty('border-collapse', borderCollapse_local_var_parse(v)); + }, + get: function () { + return this.getPropertyValue('border-collapse'); + }, + enumerable: true, + configurable: true +}; +var borderLeftWidth_export_isValid, borderLeftWidth_export_definition; +var borderLeftWidth_local_var_isValid = borderLeftWidth_export_isValid = borderWidth_export_isValid; +borderLeftWidth_export_definition = { + set: function (v) { + if (borderLeftWidth_local_var_isValid(v)) { + this._setProperty('border-left-width', v); + } + }, + get: function () { + return this.getPropertyValue('border-left-width'); + }, + enumerable: true, + configurable: true +}; +var borderLeftStyle_export_isValid, borderLeftStyle_export_definition; +borderLeftStyle_export_isValid = borderStyle_export_isValid; +borderLeftStyle_export_definition = { + set: function (v) { + if (borderStyle_export_isValid(v)) { + if (v.toLowerCase() === 'none') { + v = ''; + this.removeProperty('border-left-width'); + } + + this._setProperty('border-left-style', v); + } + }, + get: function () { + return this.getPropertyValue('border-left-style'); + }, + enumerable: true, + configurable: true +}; +var borderLeftColor_export_isValid, borderLeftColor_export_definition; +var borderLeftColor_local_var_isValid = borderLeftColor_export_isValid = borderColor_export_isValid; +borderLeftColor_export_definition = { + set: function (v) { + if (borderLeftColor_local_var_isValid(v)) { + this._setProperty('border-left-color', v); + } + }, + get: function () { + return this.getPropertyValue('border-left-color'); + }, + enumerable: true, + configurable: true +}; +var borderLeft_export_definition; +var borderLeft_local_var_shorthand_for = { + 'border-left-width': { + isValid: borderLeftWidth_export_isValid, + definition: borderLeftWidth_export_definition + }, + 'border-left-style': { + isValid: borderLeftStyle_export_isValid, + definition: borderLeftStyle_export_definition + }, + 'border-left-color': { + isValid: borderLeftColor_export_isValid, + definition: borderLeftColor_export_definition + } +}; +borderLeft_export_definition = { + set: external_dependency_parsers_0.shorthandSetter('border-left', borderLeft_local_var_shorthand_for), + get: external_dependency_parsers_0.shorthandGetter('border-left', borderLeft_local_var_shorthand_for), + enumerable: true, + configurable: true +}; +var borderRightWidth_export_isValid, borderRightWidth_export_definition; +var borderRightWidth_local_var_isValid = borderRightWidth_export_isValid = borderWidth_export_isValid; +borderRightWidth_export_definition = { + set: function (v) { + if (borderRightWidth_local_var_isValid(v)) { + this._setProperty('border-right-width', v); + } + }, + get: function () { + return this.getPropertyValue('border-right-width'); + }, + enumerable: true, + configurable: true +}; +var borderRightStyle_export_isValid, borderRightStyle_export_definition; +borderRightStyle_export_isValid = borderStyle_export_isValid; +borderRightStyle_export_definition = { + set: function (v) { + if (borderStyle_export_isValid(v)) { + if (v.toLowerCase() === 'none') { + v = ''; + this.removeProperty('border-right-width'); + } + + this._setProperty('border-right-style', v); + } + }, + get: function () { + return this.getPropertyValue('border-right-style'); + }, + enumerable: true, + configurable: true +}; +var borderRightColor_export_isValid, borderRightColor_export_definition; +var borderRightColor_local_var_isValid = borderRightColor_export_isValid = borderColor_export_isValid; +borderRightColor_export_definition = { + set: function (v) { + if (borderRightColor_local_var_isValid(v)) { + this._setProperty('border-right-color', v); + } + }, + get: function () { + return this.getPropertyValue('border-right-color'); + }, + enumerable: true, + configurable: true +}; +var borderRight_export_definition; +var borderRight_local_var_shorthand_for = { + 'border-right-width': { + isValid: borderRightWidth_export_isValid, + definition: borderRightWidth_export_definition + }, + 'border-right-style': { + isValid: borderRightStyle_export_isValid, + definition: borderRightStyle_export_definition + }, + 'border-right-color': { + isValid: borderRightColor_export_isValid, + definition: borderRightColor_export_definition + } +}; +borderRight_export_definition = { + set: external_dependency_parsers_0.shorthandSetter('border-right', borderRight_local_var_shorthand_for), + get: external_dependency_parsers_0.shorthandGetter('border-right', borderRight_local_var_shorthand_for), + enumerable: true, + configurable: true +}; +var borderSpacing_export_definition; + +// ? | inherit +// if one, it applies to both horizontal and verical spacing +// if two, the first applies to the horizontal and the second applies to vertical spacing +var borderSpacing_local_var_parse = function parse(v) { + if (v === '' || v === null) { + return undefined; + } + + if (v === 0) { + return '0px'; + } + + if (v.toLowerCase() === 'inherit') { + return v; + } + + var parts = v.split(/\s+/); + + if (parts.length !== 1 && parts.length !== 2) { + return undefined; + } + + parts.forEach(function (part) { + if (external_dependency_parsers_0.valueType(part) !== external_dependency_parsers_0.TYPES.LENGTH) { + return undefined; + } + }); + return v; +}; + +borderSpacing_export_definition = { + set: function (v) { + this._setProperty('border-spacing', borderSpacing_local_var_parse(v)); + }, + get: function () { + return this.getPropertyValue('border-spacing'); + }, + enumerable: true, + configurable: true +}; +var borderTopWidth_export_isValid, borderTopWidth_export_definition; +borderTopWidth_export_isValid = borderWidth_export_isValid; +borderTopWidth_export_definition = { + set: function (v) { + if (borderWidth_export_isValid(v)) { + this._setProperty('border-top-width', v); + } + }, + get: function () { + return this.getPropertyValue('border-top-width'); + }, + enumerable: true, + configurable: true +}; +var borderTopStyle_export_isValid, borderTopStyle_export_definition; +borderTopStyle_export_isValid = borderStyle_export_isValid; +borderTopStyle_export_definition = { + set: function (v) { + if (borderStyle_export_isValid(v)) { + if (v.toLowerCase() === 'none') { + v = ''; + this.removeProperty('border-top-width'); + } + + this._setProperty('border-top-style', v); + } + }, + get: function () { + return this.getPropertyValue('border-top-style'); + }, + enumerable: true, + configurable: true +}; +var borderTopColor_export_isValid, borderTopColor_export_definition; +var borderTopColor_local_var_isValid = borderTopColor_export_isValid = borderColor_export_isValid; +borderTopColor_export_definition = { + set: function (v) { + if (borderTopColor_local_var_isValid(v)) { + this._setProperty('border-top-color', v); + } + }, + get: function () { + return this.getPropertyValue('border-top-color'); + }, + enumerable: true, + configurable: true +}; +var borderTop_export_definition; +var borderTop_local_var_shorthand_for = { + 'border-top-width': { + isValid: borderTopWidth_export_isValid, + definition: borderTopWidth_export_definition + }, + 'border-top-style': { + isValid: borderTopStyle_export_isValid, + definition: borderTopStyle_export_definition + }, + 'border-top-color': { + isValid: borderTopColor_export_isValid, + definition: borderTopColor_export_definition + } +}; +borderTop_export_definition = { + set: external_dependency_parsers_0.shorthandSetter('border-top', borderTop_local_var_shorthand_for), + get: external_dependency_parsers_0.shorthandGetter('border-top', borderTop_local_var_shorthand_for), + enumerable: true, + configurable: true +}; +var bottom_export_definition; +bottom_export_definition = { + set: function (v) { + this._setProperty('bottom', external_dependency_parsers_0.parseMeasurement(v)); + }, + get: function () { + return this.getPropertyValue('bottom'); + }, + enumerable: true, + configurable: true +}; +var clear_export_definition; +var clear_local_var_clear_keywords = ['none', 'left', 'right', 'both', 'inherit']; +clear_export_definition = { + set: function (v) { + this._setProperty('clear', external_dependency_parsers_0.parseKeyword(v, clear_local_var_clear_keywords)); + }, + get: function () { + return this.getPropertyValue('clear'); + }, + enumerable: true, + configurable: true +}; +var clip_export_definition; +var clip_local_var_shape_regex = /^rect\((.*)\)$/i; + +var clip_local_var_parse = function (val) { + if (val === '' || val === null) { + return val; + } + + if (typeof val !== 'string') { + return undefined; + } + + val = val.toLowerCase(); + + if (val === 'auto' || val === 'inherit') { + return val; + } + + var matches = val.match(clip_local_var_shape_regex); + + if (!matches) { + return undefined; + } + + var parts = matches[1].split(/\s*,\s*/); + + if (parts.length !== 4) { + return undefined; + } + + var valid = parts.every(function (part, index) { + var measurement = external_dependency_parsers_0.parseMeasurement(part); + parts[index] = measurement; + return measurement !== undefined; + }); + + if (!valid) { + return undefined; + } + + parts = parts.join(', '); + return val.replace(matches[1], parts); +}; + +clip_export_definition = { + set: function (v) { + this._setProperty('clip', clip_local_var_parse(v)); + }, + get: function () { + return this.getPropertyValue('clip'); + }, + enumerable: true, + configurable: true +}; +var color_export_definition; +color_export_definition = { + set: function (v) { + this._setProperty('color', external_dependency_parsers_0.parseColor(v)); + }, + get: function () { + return this.getPropertyValue('color'); + }, + enumerable: true, + configurable: true +}; +var cssFloat_export_definition; +cssFloat_export_definition = { + set: function (v) { + this._setProperty('float', v); + }, + get: function () { + return this.getPropertyValue('float'); + }, + enumerable: true, + configurable: true +}; +var flexGrow_export_isValid, flexGrow_export_definition; + +flexGrow_export_isValid = function isValid(v, positionAtFlexShorthand) { + return external_dependency_parsers_0.parseNumber(v) !== undefined && positionAtFlexShorthand === external_dependency_constants_1.POSITION_AT_SHORTHAND.first; +}; + +flexGrow_export_definition = { + set: function (v) { + this._setProperty('flex-grow', external_dependency_parsers_0.parseNumber(v)); + }, + get: function () { + return this.getPropertyValue('flex-grow'); + }, + enumerable: true, + configurable: true +}; +var flexShrink_export_isValid, flexShrink_export_definition; + +flexShrink_export_isValid = function isValid(v, positionAtFlexShorthand) { + return external_dependency_parsers_0.parseNumber(v) !== undefined && positionAtFlexShorthand === external_dependency_constants_1.POSITION_AT_SHORTHAND.second; +}; + +flexShrink_export_definition = { + set: function (v) { + this._setProperty('flex-shrink', external_dependency_parsers_0.parseNumber(v)); + }, + get: function () { + return this.getPropertyValue('flex-shrink'); + }, + enumerable: true, + configurable: true +}; +var flexBasis_export_isValid, flexBasis_export_definition; + +function flexBasis_local_fn_parse(v) { + if (String(v).toLowerCase() === 'auto') { + return 'auto'; + } + + if (String(v).toLowerCase() === 'inherit') { + return 'inherit'; + } + + return external_dependency_parsers_0.parseMeasurement(v); +} + +flexBasis_export_isValid = function isValid(v) { + return flexBasis_local_fn_parse(v) !== undefined; +}; + +flexBasis_export_definition = { + set: function (v) { + this._setProperty('flex-basis', flexBasis_local_fn_parse(v)); + }, + get: function () { + return this.getPropertyValue('flex-basis'); + }, + enumerable: true, + configurable: true +}; +var flex_export_isValid, flex_export_definition; +var flex_local_var_shorthand_for = { + 'flex-grow': { + isValid: flexGrow_export_isValid, + definition: flexGrow_export_definition + }, + 'flex-shrink': { + isValid: flexShrink_export_isValid, + definition: flexShrink_export_definition + }, + 'flex-basis': { + isValid: flexBasis_export_isValid, + definition: flexBasis_export_definition + } +}; +var flex_local_var_myShorthandSetter = external_dependency_parsers_0.shorthandSetter('flex', flex_local_var_shorthand_for); + +flex_export_isValid = function isValid(v) { + return external_dependency_parsers_0.shorthandParser(v, flex_local_var_shorthand_for) !== undefined; +}; + +flex_export_definition = { + set: function (v) { + var normalizedValue = String(v).trim().toLowerCase(); + + if (normalizedValue === 'none') { + flex_local_var_myShorthandSetter.call(this, '0 0 auto'); + return; + } + + if (normalizedValue === 'initial') { + flex_local_var_myShorthandSetter.call(this, '0 1 auto'); + return; + } + + if (normalizedValue === 'auto') { + this.removeProperty('flex-grow'); + this.removeProperty('flex-shrink'); + this.setProperty('flex-basis', normalizedValue); + return; + } + + flex_local_var_myShorthandSetter.call(this, v); + }, + get: external_dependency_parsers_0.shorthandGetter('flex', flex_local_var_shorthand_for), + enumerable: true, + configurable: true +}; +var float_export_definition; +float_export_definition = { + set: function (v) { + this._setProperty('float', v); + }, + get: function () { + return this.getPropertyValue('float'); + }, + enumerable: true, + configurable: true +}; +var floodColor_export_definition; +floodColor_export_definition = { + set: function (v) { + this._setProperty('flood-color', external_dependency_parsers_0.parseColor(v)); + }, + get: function () { + return this.getPropertyValue('flood-color'); + }, + enumerable: true, + configurable: true +}; +var fontFamily_export_isValid, fontFamily_export_definition; +var fontFamily_local_var_partsRegEx = /\s*,\s*/; + +fontFamily_export_isValid = function isValid(v) { + if (v === '' || v === null) { + return true; + } + + var parts = v.split(fontFamily_local_var_partsRegEx); + var len = parts.length; + var i; + var type; + + for (i = 0; i < len; i++) { + type = external_dependency_parsers_0.valueType(parts[i]); + + if (type === external_dependency_parsers_0.TYPES.STRING || type === external_dependency_parsers_0.TYPES.KEYWORD) { + return true; + } + } + + return false; +}; + +fontFamily_export_definition = { + set: function (v) { + this._setProperty('font-family', v); + }, + get: function () { + return this.getPropertyValue('font-family'); + }, + enumerable: true, + configurable: true +}; +var fontSize_export_isValid, fontSize_export_definition; +var fontSize_local_var_absoluteSizes = ['xx-small', 'x-small', 'small', 'medium', 'large', 'x-large', 'xx-large']; +var fontSize_local_var_relativeSizes = ['larger', 'smaller']; + +fontSize_export_isValid = function (v) { + var type = external_dependency_parsers_0.valueType(v.toLowerCase()); + return type === external_dependency_parsers_0.TYPES.LENGTH || type === external_dependency_parsers_0.TYPES.PERCENT || type === external_dependency_parsers_0.TYPES.KEYWORD && fontSize_local_var_absoluteSizes.indexOf(v.toLowerCase()) !== -1 || type === external_dependency_parsers_0.TYPES.KEYWORD && fontSize_local_var_relativeSizes.indexOf(v.toLowerCase()) !== -1; +}; + +fontSize_export_definition = { + set: function (v) { + this._setProperty('font-size', v); + }, + get: function () { + return this.getPropertyValue('font-size'); + }, + enumerable: true, + configurable: true +}; +var fontStyle_export_isValid, fontStyle_export_definition; +var fontStyle_local_var_valid_styles = ['normal', 'italic', 'oblique', 'inherit']; + +fontStyle_export_isValid = function (v) { + return fontStyle_local_var_valid_styles.indexOf(v.toLowerCase()) !== -1; +}; + +fontStyle_export_definition = { + set: function (v) { + this._setProperty('font-style', v); + }, + get: function () { + return this.getPropertyValue('font-style'); + }, + enumerable: true, + configurable: true +}; +var fontVariant_export_isValid, fontVariant_export_definition; +var fontVariant_local_var_valid_variants = ['normal', 'small-caps', 'inherit']; + +fontVariant_export_isValid = function isValid(v) { + return fontVariant_local_var_valid_variants.indexOf(v.toLowerCase()) !== -1; +}; + +fontVariant_export_definition = { + set: function (v) { + this._setProperty('font-variant', v); + }, + get: function () { + return this.getPropertyValue('font-variant'); + }, + enumerable: true, + configurable: true +}; +var fontWeight_export_isValid, fontWeight_export_definition; +var fontWeight_local_var_valid_weights = ['normal', 'bold', 'bolder', 'lighter', '100', '200', '300', '400', '500', '600', '700', '800', '900', 'inherit']; + +fontWeight_export_isValid = function isValid(v) { + return fontWeight_local_var_valid_weights.indexOf(v.toLowerCase()) !== -1; +}; + +fontWeight_export_definition = { + set: function (v) { + this._setProperty('font-weight', v); + }, + get: function () { + return this.getPropertyValue('font-weight'); + }, + enumerable: true, + configurable: true +}; +var lineHeight_export_isValid, lineHeight_export_definition; + +lineHeight_export_isValid = function isValid(v) { + var type = external_dependency_parsers_0.valueType(v); + return type === external_dependency_parsers_0.TYPES.KEYWORD && v.toLowerCase() === 'normal' || v.toLowerCase() === 'inherit' || type === external_dependency_parsers_0.TYPES.NUMBER || type === external_dependency_parsers_0.TYPES.LENGTH || type === external_dependency_parsers_0.TYPES.PERCENT; +}; + +lineHeight_export_definition = { + set: function (v) { + this._setProperty('line-height', v); + }, + get: function () { + return this.getPropertyValue('line-height'); + }, + enumerable: true, + configurable: true +}; +var font_export_definition; +var font_local_var_shorthand_for = { + 'font-family': { + isValid: fontFamily_export_isValid, + definition: fontFamily_export_definition + }, + 'font-size': { + isValid: fontSize_export_isValid, + definition: fontSize_export_definition + }, + 'font-style': { + isValid: fontStyle_export_isValid, + definition: fontStyle_export_definition + }, + 'font-variant': { + isValid: fontVariant_export_isValid, + definition: fontVariant_export_definition + }, + 'font-weight': { + isValid: fontWeight_export_isValid, + definition: fontWeight_export_definition + }, + 'line-height': { + isValid: lineHeight_export_isValid, + definition: lineHeight_export_definition + } +}; +var font_local_var_static_fonts = ['caption', 'icon', 'menu', 'message-box', 'small-caption', 'status-bar', 'inherit']; +var font_local_var_setter = external_dependency_parsers_0.shorthandSetter('font', font_local_var_shorthand_for); +font_export_definition = { + set: function (v) { + var short = external_dependency_parsers_0.shorthandParser(v, font_local_var_shorthand_for); + + if (short !== undefined) { + return font_local_var_setter.call(this, v); + } + + if (external_dependency_parsers_0.valueType(v) === external_dependency_parsers_0.TYPES.KEYWORD && font_local_var_static_fonts.indexOf(v.toLowerCase()) !== -1) { + this._setProperty('font', v); + } + }, + get: external_dependency_parsers_0.shorthandGetter('font', font_local_var_shorthand_for), + enumerable: true, + configurable: true +}; +var height_export_definition; + +function height_local_fn_parse(v) { + if (String(v).toLowerCase() === 'auto') { + return 'auto'; + } + + if (String(v).toLowerCase() === 'inherit') { + return 'inherit'; + } + + return external_dependency_parsers_0.parseMeasurement(v); +} + +height_export_definition = { + set: function (v) { + this._setProperty('height', height_local_fn_parse(v)); + }, + get: function () { + return this.getPropertyValue('height'); + }, + enumerable: true, + configurable: true +}; +var left_export_definition; +left_export_definition = { + set: function (v) { + this._setProperty('left', external_dependency_parsers_0.parseMeasurement(v)); + }, + get: function () { + return this.getPropertyValue('left'); + }, + enumerable: true, + configurable: true +}; +var lightingColor_export_definition; +lightingColor_export_definition = { + set: function (v) { + this._setProperty('lighting-color', external_dependency_parsers_0.parseColor(v)); + }, + get: function () { + return this.getPropertyValue('lighting-color'); + }, + enumerable: true, + configurable: true +}; +var margin_export_definition, margin_export_isValid, margin_export_parser; +var margin_local_var_TYPES = external_dependency_parsers_0.TYPES; + +var margin_local_var_isValid = function (v) { + if (v.toLowerCase() === 'auto') { + return true; + } + + var type = external_dependency_parsers_0.valueType(v); + return type === margin_local_var_TYPES.LENGTH || type === margin_local_var_TYPES.PERCENT || type === margin_local_var_TYPES.INTEGER && (v === '0' || v === 0); +}; + +var margin_local_var_parser = function (v) { + var V = v.toLowerCase(); + + if (V === 'auto') { + return V; + } + + return external_dependency_parsers_0.parseMeasurement(v); +}; + +var margin_local_var_mySetter = external_dependency_parsers_0.implicitSetter('margin', '', margin_local_var_isValid, margin_local_var_parser); +var margin_local_var_myGlobal = external_dependency_parsers_0.implicitSetter('margin', '', function () { + return true; +}, function (v) { + return v; +}); +margin_export_definition = { + set: function (v) { + if (typeof v === 'number') { + v = String(v); + } + + if (typeof v !== 'string') { + return; + } + + var V = v.toLowerCase(); + + switch (V) { + case 'inherit': + case 'initial': + case 'unset': + case '': + margin_local_var_myGlobal.call(this, V); + break; + + default: + margin_local_var_mySetter.call(this, v); + break; + } + }, + get: function () { + return this.getPropertyValue('margin'); + }, + enumerable: true, + configurable: true +}; +margin_export_isValid = margin_local_var_isValid; +margin_export_parser = margin_local_var_parser; +var marginBottom_export_definition; +marginBottom_export_definition = { + set: external_dependency_parsers_0.subImplicitSetter('margin', 'bottom', { + definition: margin_export_definition, + isValid: margin_export_isValid, + parser: margin_export_parser + }.isValid, { + definition: margin_export_definition, + isValid: margin_export_isValid, + parser: margin_export_parser + }.parser), + get: function () { + return this.getPropertyValue('margin-bottom'); + }, + enumerable: true, + configurable: true +}; +var marginLeft_export_definition; +marginLeft_export_definition = { + set: external_dependency_parsers_0.subImplicitSetter('margin', 'left', { + definition: margin_export_definition, + isValid: margin_export_isValid, + parser: margin_export_parser + }.isValid, { + definition: margin_export_definition, + isValid: margin_export_isValid, + parser: margin_export_parser + }.parser), + get: function () { + return this.getPropertyValue('margin-left'); + }, + enumerable: true, + configurable: true +}; +var marginRight_export_definition; +marginRight_export_definition = { + set: external_dependency_parsers_0.subImplicitSetter('margin', 'right', { + definition: margin_export_definition, + isValid: margin_export_isValid, + parser: margin_export_parser + }.isValid, { + definition: margin_export_definition, + isValid: margin_export_isValid, + parser: margin_export_parser + }.parser), + get: function () { + return this.getPropertyValue('margin-right'); + }, + enumerable: true, + configurable: true +}; +var marginTop_export_definition; +marginTop_export_definition = { + set: external_dependency_parsers_0.subImplicitSetter('margin', 'top', { + definition: margin_export_definition, + isValid: margin_export_isValid, + parser: margin_export_parser + }.isValid, { + definition: margin_export_definition, + isValid: margin_export_isValid, + parser: margin_export_parser + }.parser), + get: function () { + return this.getPropertyValue('margin-top'); + }, + enumerable: true, + configurable: true +}; +var opacity_export_definition; +opacity_export_definition = { + set: function (v) { + this._setProperty('opacity', external_dependency_parsers_0.parseNumber(v)); + }, + get: function () { + return this.getPropertyValue('opacity'); + }, + enumerable: true, + configurable: true +}; +var outlineColor_export_definition; +outlineColor_export_definition = { + set: function (v) { + this._setProperty('outline-color', external_dependency_parsers_0.parseColor(v)); + }, + get: function () { + return this.getPropertyValue('outline-color'); + }, + enumerable: true, + configurable: true +}; +var padding_export_definition, padding_export_isValid, padding_export_parser; +var padding_local_var_TYPES = external_dependency_parsers_0.TYPES; + +var padding_local_var_isValid = function (v) { + var type = external_dependency_parsers_0.valueType(v); + return type === padding_local_var_TYPES.LENGTH || type === padding_local_var_TYPES.PERCENT || type === padding_local_var_TYPES.INTEGER && (v === '0' || v === 0); +}; + +var padding_local_var_parser = function (v) { + return external_dependency_parsers_0.parseMeasurement(v); +}; + +var padding_local_var_mySetter = external_dependency_parsers_0.implicitSetter('padding', '', padding_local_var_isValid, padding_local_var_parser); +var padding_local_var_myGlobal = external_dependency_parsers_0.implicitSetter('padding', '', function () { + return true; +}, function (v) { + return v; +}); +padding_export_definition = { + set: function (v) { + if (typeof v === 'number') { + v = String(v); + } + + if (typeof v !== 'string') { + return; + } + + var V = v.toLowerCase(); + + switch (V) { + case 'inherit': + case 'initial': + case 'unset': + case '': + padding_local_var_myGlobal.call(this, V); + break; + + default: + padding_local_var_mySetter.call(this, v); + break; + } + }, + get: function () { + return this.getPropertyValue('padding'); + }, + enumerable: true, + configurable: true +}; +padding_export_isValid = padding_local_var_isValid; +padding_export_parser = padding_local_var_parser; +var paddingBottom_export_definition; +paddingBottom_export_definition = { + set: external_dependency_parsers_0.subImplicitSetter('padding', 'bottom', { + definition: padding_export_definition, + isValid: padding_export_isValid, + parser: padding_export_parser + }.isValid, { + definition: padding_export_definition, + isValid: padding_export_isValid, + parser: padding_export_parser + }.parser), + get: function () { + return this.getPropertyValue('padding-bottom'); + }, + enumerable: true, + configurable: true +}; +var paddingLeft_export_definition; +paddingLeft_export_definition = { + set: external_dependency_parsers_0.subImplicitSetter('padding', 'left', { + definition: padding_export_definition, + isValid: padding_export_isValid, + parser: padding_export_parser + }.isValid, { + definition: padding_export_definition, + isValid: padding_export_isValid, + parser: padding_export_parser + }.parser), + get: function () { + return this.getPropertyValue('padding-left'); + }, + enumerable: true, + configurable: true +}; +var paddingRight_export_definition; +paddingRight_export_definition = { + set: external_dependency_parsers_0.subImplicitSetter('padding', 'right', { + definition: padding_export_definition, + isValid: padding_export_isValid, + parser: padding_export_parser + }.isValid, { + definition: padding_export_definition, + isValid: padding_export_isValid, + parser: padding_export_parser + }.parser), + get: function () { + return this.getPropertyValue('padding-right'); + }, + enumerable: true, + configurable: true +}; +var paddingTop_export_definition; +paddingTop_export_definition = { + set: external_dependency_parsers_0.subImplicitSetter('padding', 'top', { + definition: padding_export_definition, + isValid: padding_export_isValid, + parser: padding_export_parser + }.isValid, { + definition: padding_export_definition, + isValid: padding_export_isValid, + parser: padding_export_parser + }.parser), + get: function () { + return this.getPropertyValue('padding-top'); + }, + enumerable: true, + configurable: true +}; +var right_export_definition; +right_export_definition = { + set: function (v) { + this._setProperty('right', external_dependency_parsers_0.parseMeasurement(v)); + }, + get: function () { + return this.getPropertyValue('right'); + }, + enumerable: true, + configurable: true +}; +var stopColor_export_definition; +stopColor_export_definition = { + set: function (v) { + this._setProperty('stop-color', external_dependency_parsers_0.parseColor(v)); + }, + get: function () { + return this.getPropertyValue('stop-color'); + }, + enumerable: true, + configurable: true +}; +var textLineThroughColor_export_definition; +textLineThroughColor_export_definition = { + set: function (v) { + this._setProperty('text-line-through-color', external_dependency_parsers_0.parseColor(v)); + }, + get: function () { + return this.getPropertyValue('text-line-through-color'); + }, + enumerable: true, + configurable: true +}; +var textOverlineColor_export_definition; +textOverlineColor_export_definition = { + set: function (v) { + this._setProperty('text-overline-color', external_dependency_parsers_0.parseColor(v)); + }, + get: function () { + return this.getPropertyValue('text-overline-color'); + }, + enumerable: true, + configurable: true +}; +var textUnderlineColor_export_definition; +textUnderlineColor_export_definition = { + set: function (v) { + this._setProperty('text-underline-color', external_dependency_parsers_0.parseColor(v)); + }, + get: function () { + return this.getPropertyValue('text-underline-color'); + }, + enumerable: true, + configurable: true +}; +var top_export_definition; +top_export_definition = { + set: function (v) { + this._setProperty('top', external_dependency_parsers_0.parseMeasurement(v)); + }, + get: function () { + return this.getPropertyValue('top'); + }, + enumerable: true, + configurable: true +}; +var webkitBorderAfterColor_export_definition; +webkitBorderAfterColor_export_definition = { + set: function (v) { + this._setProperty('-webkit-border-after-color', external_dependency_parsers_0.parseColor(v)); + }, + get: function () { + return this.getPropertyValue('-webkit-border-after-color'); + }, + enumerable: true, + configurable: true +}; +var webkitBorderBeforeColor_export_definition; +webkitBorderBeforeColor_export_definition = { + set: function (v) { + this._setProperty('-webkit-border-before-color', external_dependency_parsers_0.parseColor(v)); + }, + get: function () { + return this.getPropertyValue('-webkit-border-before-color'); + }, + enumerable: true, + configurable: true +}; +var webkitBorderEndColor_export_definition; +webkitBorderEndColor_export_definition = { + set: function (v) { + this._setProperty('-webkit-border-end-color', external_dependency_parsers_0.parseColor(v)); + }, + get: function () { + return this.getPropertyValue('-webkit-border-end-color'); + }, + enumerable: true, + configurable: true +}; +var webkitBorderStartColor_export_definition; +webkitBorderStartColor_export_definition = { + set: function (v) { + this._setProperty('-webkit-border-start-color', external_dependency_parsers_0.parseColor(v)); + }, + get: function () { + return this.getPropertyValue('-webkit-border-start-color'); + }, + enumerable: true, + configurable: true +}; +var webkitColumnRuleColor_export_definition; +webkitColumnRuleColor_export_definition = { + set: function (v) { + this._setProperty('-webkit-column-rule-color', external_dependency_parsers_0.parseColor(v)); + }, + get: function () { + return this.getPropertyValue('-webkit-column-rule-color'); + }, + enumerable: true, + configurable: true +}; +var webkitMatchNearestMailBlockquoteColor_export_definition; +webkitMatchNearestMailBlockquoteColor_export_definition = { + set: function (v) { + this._setProperty('-webkit-match-nearest-mail-blockquote-color', external_dependency_parsers_0.parseColor(v)); + }, + get: function () { + return this.getPropertyValue('-webkit-match-nearest-mail-blockquote-color'); + }, + enumerable: true, + configurable: true +}; +var webkitTapHighlightColor_export_definition; +webkitTapHighlightColor_export_definition = { + set: function (v) { + this._setProperty('-webkit-tap-highlight-color', external_dependency_parsers_0.parseColor(v)); + }, + get: function () { + return this.getPropertyValue('-webkit-tap-highlight-color'); + }, + enumerable: true, + configurable: true +}; +var webkitTextEmphasisColor_export_definition; +webkitTextEmphasisColor_export_definition = { + set: function (v) { + this._setProperty('-webkit-text-emphasis-color', external_dependency_parsers_0.parseColor(v)); + }, + get: function () { + return this.getPropertyValue('-webkit-text-emphasis-color'); + }, + enumerable: true, + configurable: true +}; +var webkitTextFillColor_export_definition; +webkitTextFillColor_export_definition = { + set: function (v) { + this._setProperty('-webkit-text-fill-color', external_dependency_parsers_0.parseColor(v)); + }, + get: function () { + return this.getPropertyValue('-webkit-text-fill-color'); + }, + enumerable: true, + configurable: true +}; +var webkitTextStrokeColor_export_definition; +webkitTextStrokeColor_export_definition = { + set: function (v) { + this._setProperty('-webkit-text-stroke-color', external_dependency_parsers_0.parseColor(v)); + }, + get: function () { + return this.getPropertyValue('-webkit-text-stroke-color'); + }, + enumerable: true, + configurable: true +}; +var width_export_definition; + +function width_local_fn_parse(v) { + if (String(v).toLowerCase() === 'auto') { + return 'auto'; + } + + if (String(v).toLowerCase() === 'inherit') { + return 'inherit'; + } + + return external_dependency_parsers_0.parseMeasurement(v); +} + +width_export_definition = { + set: function (v) { + this._setProperty('width', width_local_fn_parse(v)); + }, + get: function () { + return this.getPropertyValue('width'); + }, + enumerable: true, + configurable: true +}; + +module.exports = function (prototype) { + Object.defineProperties(prototype, { + azimuth: azimuth_export_definition, + backgroundColor: backgroundColor_export_definition, + "background-color": backgroundColor_export_definition, + backgroundImage: backgroundImage_export_definition, + "background-image": backgroundImage_export_definition, + backgroundRepeat: backgroundRepeat_export_definition, + "background-repeat": backgroundRepeat_export_definition, + backgroundAttachment: backgroundAttachment_export_definition, + "background-attachment": backgroundAttachment_export_definition, + backgroundPosition: backgroundPosition_export_definition, + "background-position": backgroundPosition_export_definition, + background: background_export_definition, + borderWidth: borderWidth_export_definition, + "border-width": borderWidth_export_definition, + borderStyle: borderStyle_export_definition, + "border-style": borderStyle_export_definition, + borderColor: borderColor_export_definition, + "border-color": borderColor_export_definition, + border: border_export_definition, + borderBottomWidth: borderBottomWidth_export_definition, + "border-bottom-width": borderBottomWidth_export_definition, + borderBottomStyle: borderBottomStyle_export_definition, + "border-bottom-style": borderBottomStyle_export_definition, + borderBottomColor: borderBottomColor_export_definition, + "border-bottom-color": borderBottomColor_export_definition, + borderBottom: borderBottom_export_definition, + "border-bottom": borderBottom_export_definition, + borderCollapse: borderCollapse_export_definition, + "border-collapse": borderCollapse_export_definition, + borderLeftWidth: borderLeftWidth_export_definition, + "border-left-width": borderLeftWidth_export_definition, + borderLeftStyle: borderLeftStyle_export_definition, + "border-left-style": borderLeftStyle_export_definition, + borderLeftColor: borderLeftColor_export_definition, + "border-left-color": borderLeftColor_export_definition, + borderLeft: borderLeft_export_definition, + "border-left": borderLeft_export_definition, + borderRightWidth: borderRightWidth_export_definition, + "border-right-width": borderRightWidth_export_definition, + borderRightStyle: borderRightStyle_export_definition, + "border-right-style": borderRightStyle_export_definition, + borderRightColor: borderRightColor_export_definition, + "border-right-color": borderRightColor_export_definition, + borderRight: borderRight_export_definition, + "border-right": borderRight_export_definition, + borderSpacing: borderSpacing_export_definition, + "border-spacing": borderSpacing_export_definition, + borderTopWidth: borderTopWidth_export_definition, + "border-top-width": borderTopWidth_export_definition, + borderTopStyle: borderTopStyle_export_definition, + "border-top-style": borderTopStyle_export_definition, + borderTopColor: borderTopColor_export_definition, + "border-top-color": borderTopColor_export_definition, + borderTop: borderTop_export_definition, + "border-top": borderTop_export_definition, + bottom: bottom_export_definition, + clear: clear_export_definition, + clip: clip_export_definition, + color: color_export_definition, + cssFloat: cssFloat_export_definition, + "css-float": cssFloat_export_definition, + flexGrow: flexGrow_export_definition, + "flex-grow": flexGrow_export_definition, + flexShrink: flexShrink_export_definition, + "flex-shrink": flexShrink_export_definition, + flexBasis: flexBasis_export_definition, + "flex-basis": flexBasis_export_definition, + flex: flex_export_definition, + float: float_export_definition, + floodColor: floodColor_export_definition, + "flood-color": floodColor_export_definition, + fontFamily: fontFamily_export_definition, + "font-family": fontFamily_export_definition, + fontSize: fontSize_export_definition, + "font-size": fontSize_export_definition, + fontStyle: fontStyle_export_definition, + "font-style": fontStyle_export_definition, + fontVariant: fontVariant_export_definition, + "font-variant": fontVariant_export_definition, + fontWeight: fontWeight_export_definition, + "font-weight": fontWeight_export_definition, + lineHeight: lineHeight_export_definition, + "line-height": lineHeight_export_definition, + font: font_export_definition, + height: height_export_definition, + left: left_export_definition, + lightingColor: lightingColor_export_definition, + "lighting-color": lightingColor_export_definition, + margin: margin_export_definition, + marginBottom: marginBottom_export_definition, + "margin-bottom": marginBottom_export_definition, + marginLeft: marginLeft_export_definition, + "margin-left": marginLeft_export_definition, + marginRight: marginRight_export_definition, + "margin-right": marginRight_export_definition, + marginTop: marginTop_export_definition, + "margin-top": marginTop_export_definition, + opacity: opacity_export_definition, + outlineColor: outlineColor_export_definition, + "outline-color": outlineColor_export_definition, + padding: padding_export_definition, + paddingBottom: paddingBottom_export_definition, + "padding-bottom": paddingBottom_export_definition, + paddingLeft: paddingLeft_export_definition, + "padding-left": paddingLeft_export_definition, + paddingRight: paddingRight_export_definition, + "padding-right": paddingRight_export_definition, + paddingTop: paddingTop_export_definition, + "padding-top": paddingTop_export_definition, + right: right_export_definition, + stopColor: stopColor_export_definition, + "stop-color": stopColor_export_definition, + textLineThroughColor: textLineThroughColor_export_definition, + "text-line-through-color": textLineThroughColor_export_definition, + textOverlineColor: textOverlineColor_export_definition, + "text-overline-color": textOverlineColor_export_definition, + textUnderlineColor: textUnderlineColor_export_definition, + "text-underline-color": textUnderlineColor_export_definition, + top: top_export_definition, + webkitBorderAfterColor: webkitBorderAfterColor_export_definition, + "webkit-border-after-color": webkitBorderAfterColor_export_definition, + webkitBorderBeforeColor: webkitBorderBeforeColor_export_definition, + "webkit-border-before-color": webkitBorderBeforeColor_export_definition, + webkitBorderEndColor: webkitBorderEndColor_export_definition, + "webkit-border-end-color": webkitBorderEndColor_export_definition, + webkitBorderStartColor: webkitBorderStartColor_export_definition, + "webkit-border-start-color": webkitBorderStartColor_export_definition, + webkitColumnRuleColor: webkitColumnRuleColor_export_definition, + "webkit-column-rule-color": webkitColumnRuleColor_export_definition, + webkitMatchNearestMailBlockquoteColor: webkitMatchNearestMailBlockquoteColor_export_definition, + "webkit-match-nearest-mail-blockquote-color": webkitMatchNearestMailBlockquoteColor_export_definition, + webkitTapHighlightColor: webkitTapHighlightColor_export_definition, + "webkit-tap-highlight-color": webkitTapHighlightColor_export_definition, + webkitTextEmphasisColor: webkitTextEmphasisColor_export_definition, + "webkit-text-emphasis-color": webkitTextEmphasisColor_export_definition, + webkitTextFillColor: webkitTextFillColor_export_definition, + "webkit-text-fill-color": webkitTextFillColor_export_definition, + webkitTextStrokeColor: webkitTextStrokeColor_export_definition, + "webkit-text-stroke-color": webkitTextStrokeColor_export_definition, + width: width_export_definition + }); +}; diff --git a/docs/js/node_modules/cssstyle/lib/properties/azimuth.js b/docs/js/node_modules/cssstyle/lib/properties/azimuth.js new file mode 100644 index 000000000..f23a68df6 --- /dev/null +++ b/docs/js/node_modules/cssstyle/lib/properties/azimuth.js @@ -0,0 +1,67 @@ +'use strict'; + +var parsers = require('../parsers'); + +module.exports.definition = { + set: function(v) { + var valueType = parsers.valueType(v); + if (valueType === parsers.TYPES.ANGLE) { + return this._setProperty('azimuth', parsers.parseAngle(v)); + } + if (valueType === parsers.TYPES.KEYWORD) { + var keywords = v + .toLowerCase() + .trim() + .split(/\s+/); + var hasBehind = false; + if (keywords.length > 2) { + return; + } + var behindIndex = keywords.indexOf('behind'); + hasBehind = behindIndex !== -1; + + if (keywords.length === 2) { + if (!hasBehind) { + return; + } + keywords.splice(behindIndex, 1); + } + if (keywords[0] === 'leftwards' || keywords[0] === 'rightwards') { + if (hasBehind) { + return; + } + return this._setProperty('azimuth', keywords[0]); + } + if (keywords[0] === 'behind') { + return this._setProperty('azimuth', '180deg'); + } + switch (keywords[0]) { + case 'left-side': + return this._setProperty('azimuth', '270deg'); + case 'far-left': + return this._setProperty('azimuth', (hasBehind ? 240 : 300) + 'deg'); + case 'left': + return this._setProperty('azimuth', (hasBehind ? 220 : 320) + 'deg'); + case 'center-left': + return this._setProperty('azimuth', (hasBehind ? 200 : 340) + 'deg'); + case 'center': + return this._setProperty('azimuth', (hasBehind ? 180 : 0) + 'deg'); + case 'center-right': + return this._setProperty('azimuth', (hasBehind ? 160 : 20) + 'deg'); + case 'right': + return this._setProperty('azimuth', (hasBehind ? 140 : 40) + 'deg'); + case 'far-right': + return this._setProperty('azimuth', (hasBehind ? 120 : 60) + 'deg'); + case 'right-side': + return this._setProperty('azimuth', '90deg'); + default: + return; + } + } + }, + get: function() { + return this.getPropertyValue('azimuth'); + }, + enumerable: true, + configurable: true, +}; diff --git a/docs/js/node_modules/cssstyle/lib/properties/background.js b/docs/js/node_modules/cssstyle/lib/properties/background.js new file mode 100644 index 000000000..b843e0c7e --- /dev/null +++ b/docs/js/node_modules/cssstyle/lib/properties/background.js @@ -0,0 +1,19 @@ +'use strict'; + +var shorthandSetter = require('../parsers').shorthandSetter; +var shorthandGetter = require('../parsers').shorthandGetter; + +var shorthand_for = { + 'background-color': require('./backgroundColor'), + 'background-image': require('./backgroundImage'), + 'background-repeat': require('./backgroundRepeat'), + 'background-attachment': require('./backgroundAttachment'), + 'background-position': require('./backgroundPosition'), +}; + +module.exports.definition = { + set: shorthandSetter('background', shorthand_for), + get: shorthandGetter('background', shorthand_for), + enumerable: true, + configurable: true, +}; diff --git a/docs/js/node_modules/cssstyle/lib/properties/backgroundAttachment.js b/docs/js/node_modules/cssstyle/lib/properties/backgroundAttachment.js new file mode 100644 index 000000000..98c8f76b7 --- /dev/null +++ b/docs/js/node_modules/cssstyle/lib/properties/backgroundAttachment.js @@ -0,0 +1,24 @@ +'use strict'; + +var parsers = require('../parsers'); + +var isValid = (module.exports.isValid = function isValid(v) { + return ( + parsers.valueType(v) === parsers.TYPES.KEYWORD && + (v.toLowerCase() === 'scroll' || v.toLowerCase() === 'fixed' || v.toLowerCase() === 'inherit') + ); +}); + +module.exports.definition = { + set: function(v) { + if (!isValid(v)) { + return; + } + this._setProperty('background-attachment', v); + }, + get: function() { + return this.getPropertyValue('background-attachment'); + }, + enumerable: true, + configurable: true, +}; diff --git a/docs/js/node_modules/cssstyle/lib/properties/backgroundColor.js b/docs/js/node_modules/cssstyle/lib/properties/backgroundColor.js new file mode 100644 index 000000000..5cee7176f --- /dev/null +++ b/docs/js/node_modules/cssstyle/lib/properties/backgroundColor.js @@ -0,0 +1,36 @@ +'use strict'; + +var parsers = require('../parsers'); + +var parse = function parse(v) { + var parsed = parsers.parseColor(v); + if (parsed !== undefined) { + return parsed; + } + if ( + parsers.valueType(v) === parsers.TYPES.KEYWORD && + (v.toLowerCase() === 'transparent' || v.toLowerCase() === 'inherit') + ) { + return v; + } + return undefined; +}; + +module.exports.isValid = function isValid(v) { + return parse(v) !== undefined; +}; + +module.exports.definition = { + set: function(v) { + var parsed = parse(v); + if (parsed === undefined) { + return; + } + this._setProperty('background-color', parsed); + }, + get: function() { + return this.getPropertyValue('background-color'); + }, + enumerable: true, + configurable: true, +}; diff --git a/docs/js/node_modules/cssstyle/lib/properties/backgroundImage.js b/docs/js/node_modules/cssstyle/lib/properties/backgroundImage.js new file mode 100644 index 000000000..b6479a1f3 --- /dev/null +++ b/docs/js/node_modules/cssstyle/lib/properties/backgroundImage.js @@ -0,0 +1,32 @@ +'use strict'; + +var parsers = require('../parsers'); + +var parse = function parse(v) { + var parsed = parsers.parseUrl(v); + if (parsed !== undefined) { + return parsed; + } + if ( + parsers.valueType(v) === parsers.TYPES.KEYWORD && + (v.toLowerCase() === 'none' || v.toLowerCase() === 'inherit') + ) { + return v; + } + return undefined; +}; + +module.exports.isValid = function isValid(v) { + return parse(v) !== undefined; +}; + +module.exports.definition = { + set: function(v) { + this._setProperty('background-image', parse(v)); + }, + get: function() { + return this.getPropertyValue('background-image'); + }, + enumerable: true, + configurable: true, +}; diff --git a/docs/js/node_modules/cssstyle/lib/properties/backgroundPosition.js b/docs/js/node_modules/cssstyle/lib/properties/backgroundPosition.js new file mode 100644 index 000000000..4405fe6fe --- /dev/null +++ b/docs/js/node_modules/cssstyle/lib/properties/backgroundPosition.js @@ -0,0 +1,58 @@ +'use strict'; + +var parsers = require('../parsers'); + +var valid_keywords = ['top', 'center', 'bottom', 'left', 'right']; + +var parse = function parse(v) { + if (v === '' || v === null) { + return undefined; + } + var parts = v.split(/\s+/); + if (parts.length > 2 || parts.length < 1) { + return undefined; + } + var types = []; + parts.forEach(function(part, index) { + types[index] = parsers.valueType(part); + }); + if (parts.length === 1) { + if (types[0] === parsers.TYPES.LENGTH || types[0] === parsers.TYPES.PERCENT) { + return v; + } + if (types[0] === parsers.TYPES.KEYWORD) { + if (valid_keywords.indexOf(v.toLowerCase()) !== -1 || v.toLowerCase() === 'inherit') { + return v; + } + } + return undefined; + } + if ( + (types[0] === parsers.TYPES.LENGTH || types[0] === parsers.TYPES.PERCENT) && + (types[1] === parsers.TYPES.LENGTH || types[1] === parsers.TYPES.PERCENT) + ) { + return v; + } + if (types[0] !== parsers.TYPES.KEYWORD || types[1] !== parsers.TYPES.KEYWORD) { + return undefined; + } + if (valid_keywords.indexOf(parts[0]) !== -1 && valid_keywords.indexOf(parts[1]) !== -1) { + return v; + } + return undefined; +}; + +module.exports.isValid = function isValid(v) { + return parse(v) !== undefined; +}; + +module.exports.definition = { + set: function(v) { + this._setProperty('background-position', parse(v)); + }, + get: function() { + return this.getPropertyValue('background-position'); + }, + enumerable: true, + configurable: true, +}; diff --git a/docs/js/node_modules/cssstyle/lib/properties/backgroundRepeat.js b/docs/js/node_modules/cssstyle/lib/properties/backgroundRepeat.js new file mode 100644 index 000000000..379ade0cb --- /dev/null +++ b/docs/js/node_modules/cssstyle/lib/properties/backgroundRepeat.js @@ -0,0 +1,32 @@ +'use strict'; + +var parsers = require('../parsers'); + +var parse = function parse(v) { + if ( + parsers.valueType(v) === parsers.TYPES.KEYWORD && + (v.toLowerCase() === 'repeat' || + v.toLowerCase() === 'repeat-x' || + v.toLowerCase() === 'repeat-y' || + v.toLowerCase() === 'no-repeat' || + v.toLowerCase() === 'inherit') + ) { + return v; + } + return undefined; +}; + +module.exports.isValid = function isValid(v) { + return parse(v) !== undefined; +}; + +module.exports.definition = { + set: function(v) { + this._setProperty('background-repeat', parse(v)); + }, + get: function() { + return this.getPropertyValue('background-repeat'); + }, + enumerable: true, + configurable: true, +}; diff --git a/docs/js/node_modules/cssstyle/lib/properties/border.js b/docs/js/node_modules/cssstyle/lib/properties/border.js new file mode 100644 index 000000000..bf5b5d649 --- /dev/null +++ b/docs/js/node_modules/cssstyle/lib/properties/border.js @@ -0,0 +1,33 @@ +'use strict'; + +var shorthandSetter = require('../parsers').shorthandSetter; +var shorthandGetter = require('../parsers').shorthandGetter; + +var shorthand_for = { + 'border-width': require('./borderWidth'), + 'border-style': require('./borderStyle'), + 'border-color': require('./borderColor'), +}; + +var myShorthandSetter = shorthandSetter('border', shorthand_for); +var myShorthandGetter = shorthandGetter('border', shorthand_for); + +module.exports.definition = { + set: function(v) { + if (v.toString().toLowerCase() === 'none') { + v = ''; + } + myShorthandSetter.call(this, v); + this.removeProperty('border-top'); + this.removeProperty('border-left'); + this.removeProperty('border-right'); + this.removeProperty('border-bottom'); + this._values['border-top'] = this._values.border; + this._values['border-left'] = this._values.border; + this._values['border-right'] = this._values.border; + this._values['border-bottom'] = this._values.border; + }, + get: myShorthandGetter, + enumerable: true, + configurable: true, +}; diff --git a/docs/js/node_modules/cssstyle/lib/properties/borderBottom.js b/docs/js/node_modules/cssstyle/lib/properties/borderBottom.js new file mode 100644 index 000000000..aae2e5f7c --- /dev/null +++ b/docs/js/node_modules/cssstyle/lib/properties/borderBottom.js @@ -0,0 +1,17 @@ +'use strict'; + +var shorthandSetter = require('../parsers').shorthandSetter; +var shorthandGetter = require('../parsers').shorthandGetter; + +var shorthand_for = { + 'border-bottom-width': require('./borderBottomWidth'), + 'border-bottom-style': require('./borderBottomStyle'), + 'border-bottom-color': require('./borderBottomColor'), +}; + +module.exports.definition = { + set: shorthandSetter('border-bottom', shorthand_for), + get: shorthandGetter('border-bottom', shorthand_for), + enumerable: true, + configurable: true, +}; diff --git a/docs/js/node_modules/cssstyle/lib/properties/borderBottomColor.js b/docs/js/node_modules/cssstyle/lib/properties/borderBottomColor.js new file mode 100644 index 000000000..da5a4b560 --- /dev/null +++ b/docs/js/node_modules/cssstyle/lib/properties/borderBottomColor.js @@ -0,0 +1,16 @@ +'use strict'; + +var isValid = (module.exports.isValid = require('./borderColor').isValid); + +module.exports.definition = { + set: function(v) { + if (isValid(v)) { + this._setProperty('border-bottom-color', v); + } + }, + get: function() { + return this.getPropertyValue('border-bottom-color'); + }, + enumerable: true, + configurable: true, +}; diff --git a/docs/js/node_modules/cssstyle/lib/properties/borderBottomStyle.js b/docs/js/node_modules/cssstyle/lib/properties/borderBottomStyle.js new file mode 100644 index 000000000..35c44f058 --- /dev/null +++ b/docs/js/node_modules/cssstyle/lib/properties/borderBottomStyle.js @@ -0,0 +1,21 @@ +'use strict'; + +var isValid = require('./borderStyle').isValid; +module.exports.isValid = isValid; + +module.exports.definition = { + set: function(v) { + if (isValid(v)) { + if (v.toLowerCase() === 'none') { + v = ''; + this.removeProperty('border-bottom-width'); + } + this._setProperty('border-bottom-style', v); + } + }, + get: function() { + return this.getPropertyValue('border-bottom-style'); + }, + enumerable: true, + configurable: true, +}; diff --git a/docs/js/node_modules/cssstyle/lib/properties/borderBottomWidth.js b/docs/js/node_modules/cssstyle/lib/properties/borderBottomWidth.js new file mode 100644 index 000000000..db2e3b87b --- /dev/null +++ b/docs/js/node_modules/cssstyle/lib/properties/borderBottomWidth.js @@ -0,0 +1,16 @@ +'use strict'; + +var isValid = (module.exports.isValid = require('./borderWidth').isValid); + +module.exports.definition = { + set: function(v) { + if (isValid(v)) { + this._setProperty('border-bottom-width', v); + } + }, + get: function() { + return this.getPropertyValue('border-bottom-width'); + }, + enumerable: true, + configurable: true, +}; diff --git a/docs/js/node_modules/cssstyle/lib/properties/borderCollapse.js b/docs/js/node_modules/cssstyle/lib/properties/borderCollapse.js new file mode 100644 index 000000000..49bdeb069 --- /dev/null +++ b/docs/js/node_modules/cssstyle/lib/properties/borderCollapse.js @@ -0,0 +1,26 @@ +'use strict'; + +var parsers = require('../parsers'); + +var parse = function parse(v) { + if ( + parsers.valueType(v) === parsers.TYPES.KEYWORD && + (v.toLowerCase() === 'collapse' || + v.toLowerCase() === 'separate' || + v.toLowerCase() === 'inherit') + ) { + return v; + } + return undefined; +}; + +module.exports.definition = { + set: function(v) { + this._setProperty('border-collapse', parse(v)); + }, + get: function() { + return this.getPropertyValue('border-collapse'); + }, + enumerable: true, + configurable: true, +}; diff --git a/docs/js/node_modules/cssstyle/lib/properties/borderColor.js b/docs/js/node_modules/cssstyle/lib/properties/borderColor.js new file mode 100644 index 000000000..6605e0713 --- /dev/null +++ b/docs/js/node_modules/cssstyle/lib/properties/borderColor.js @@ -0,0 +1,30 @@ +'use strict'; + +var parsers = require('../parsers'); +var implicitSetter = require('../parsers').implicitSetter; + +module.exports.isValid = function parse(v) { + if (typeof v !== 'string') { + return false; + } + return ( + v === '' || v.toLowerCase() === 'transparent' || parsers.valueType(v) === parsers.TYPES.COLOR + ); +}; +var isValid = module.exports.isValid; + +var parser = function(v) { + if (isValid(v)) { + return v.toLowerCase(); + } + return undefined; +}; + +module.exports.definition = { + set: implicitSetter('border', 'color', isValid, parser), + get: function() { + return this.getPropertyValue('border-color'); + }, + enumerable: true, + configurable: true, +}; diff --git a/docs/js/node_modules/cssstyle/lib/properties/borderLeft.js b/docs/js/node_modules/cssstyle/lib/properties/borderLeft.js new file mode 100644 index 000000000..a05945e99 --- /dev/null +++ b/docs/js/node_modules/cssstyle/lib/properties/borderLeft.js @@ -0,0 +1,17 @@ +'use strict'; + +var shorthandSetter = require('../parsers').shorthandSetter; +var shorthandGetter = require('../parsers').shorthandGetter; + +var shorthand_for = { + 'border-left-width': require('./borderLeftWidth'), + 'border-left-style': require('./borderLeftStyle'), + 'border-left-color': require('./borderLeftColor'), +}; + +module.exports.definition = { + set: shorthandSetter('border-left', shorthand_for), + get: shorthandGetter('border-left', shorthand_for), + enumerable: true, + configurable: true, +}; diff --git a/docs/js/node_modules/cssstyle/lib/properties/borderLeftColor.js b/docs/js/node_modules/cssstyle/lib/properties/borderLeftColor.js new file mode 100644 index 000000000..eb3f2735c --- /dev/null +++ b/docs/js/node_modules/cssstyle/lib/properties/borderLeftColor.js @@ -0,0 +1,16 @@ +'use strict'; + +var isValid = (module.exports.isValid = require('./borderColor').isValid); + +module.exports.definition = { + set: function(v) { + if (isValid(v)) { + this._setProperty('border-left-color', v); + } + }, + get: function() { + return this.getPropertyValue('border-left-color'); + }, + enumerable: true, + configurable: true, +}; diff --git a/docs/js/node_modules/cssstyle/lib/properties/borderLeftStyle.js b/docs/js/node_modules/cssstyle/lib/properties/borderLeftStyle.js new file mode 100644 index 000000000..5e8a11335 --- /dev/null +++ b/docs/js/node_modules/cssstyle/lib/properties/borderLeftStyle.js @@ -0,0 +1,21 @@ +'use strict'; + +var isValid = require('./borderStyle').isValid; +module.exports.isValid = isValid; + +module.exports.definition = { + set: function(v) { + if (isValid(v)) { + if (v.toLowerCase() === 'none') { + v = ''; + this.removeProperty('border-left-width'); + } + this._setProperty('border-left-style', v); + } + }, + get: function() { + return this.getPropertyValue('border-left-style'); + }, + enumerable: true, + configurable: true, +}; diff --git a/docs/js/node_modules/cssstyle/lib/properties/borderLeftWidth.js b/docs/js/node_modules/cssstyle/lib/properties/borderLeftWidth.js new file mode 100644 index 000000000..8c680b10c --- /dev/null +++ b/docs/js/node_modules/cssstyle/lib/properties/borderLeftWidth.js @@ -0,0 +1,16 @@ +'use strict'; + +var isValid = (module.exports.isValid = require('./borderWidth').isValid); + +module.exports.definition = { + set: function(v) { + if (isValid(v)) { + this._setProperty('border-left-width', v); + } + }, + get: function() { + return this.getPropertyValue('border-left-width'); + }, + enumerable: true, + configurable: true, +}; diff --git a/docs/js/node_modules/cssstyle/lib/properties/borderRight.js b/docs/js/node_modules/cssstyle/lib/properties/borderRight.js new file mode 100644 index 000000000..17e26df96 --- /dev/null +++ b/docs/js/node_modules/cssstyle/lib/properties/borderRight.js @@ -0,0 +1,17 @@ +'use strict'; + +var shorthandSetter = require('../parsers').shorthandSetter; +var shorthandGetter = require('../parsers').shorthandGetter; + +var shorthand_for = { + 'border-right-width': require('./borderRightWidth'), + 'border-right-style': require('./borderRightStyle'), + 'border-right-color': require('./borderRightColor'), +}; + +module.exports.definition = { + set: shorthandSetter('border-right', shorthand_for), + get: shorthandGetter('border-right', shorthand_for), + enumerable: true, + configurable: true, +}; diff --git a/docs/js/node_modules/cssstyle/lib/properties/borderRightColor.js b/docs/js/node_modules/cssstyle/lib/properties/borderRightColor.js new file mode 100644 index 000000000..7c188f248 --- /dev/null +++ b/docs/js/node_modules/cssstyle/lib/properties/borderRightColor.js @@ -0,0 +1,16 @@ +'use strict'; + +var isValid = (module.exports.isValid = require('./borderColor').isValid); + +module.exports.definition = { + set: function(v) { + if (isValid(v)) { + this._setProperty('border-right-color', v); + } + }, + get: function() { + return this.getPropertyValue('border-right-color'); + }, + enumerable: true, + configurable: true, +}; diff --git a/docs/js/node_modules/cssstyle/lib/properties/borderRightStyle.js b/docs/js/node_modules/cssstyle/lib/properties/borderRightStyle.js new file mode 100644 index 000000000..68e820930 --- /dev/null +++ b/docs/js/node_modules/cssstyle/lib/properties/borderRightStyle.js @@ -0,0 +1,21 @@ +'use strict'; + +var isValid = require('./borderStyle').isValid; +module.exports.isValid = isValid; + +module.exports.definition = { + set: function(v) { + if (isValid(v)) { + if (v.toLowerCase() === 'none') { + v = ''; + this.removeProperty('border-right-width'); + } + this._setProperty('border-right-style', v); + } + }, + get: function() { + return this.getPropertyValue('border-right-style'); + }, + enumerable: true, + configurable: true, +}; diff --git a/docs/js/node_modules/cssstyle/lib/properties/borderRightWidth.js b/docs/js/node_modules/cssstyle/lib/properties/borderRightWidth.js new file mode 100644 index 000000000..d1090d717 --- /dev/null +++ b/docs/js/node_modules/cssstyle/lib/properties/borderRightWidth.js @@ -0,0 +1,16 @@ +'use strict'; + +var isValid = (module.exports.isValid = require('./borderWidth').isValid); + +module.exports.definition = { + set: function(v) { + if (isValid(v)) { + this._setProperty('border-right-width', v); + } + }, + get: function() { + return this.getPropertyValue('border-right-width'); + }, + enumerable: true, + configurable: true, +}; diff --git a/docs/js/node_modules/cssstyle/lib/properties/borderSpacing.js b/docs/js/node_modules/cssstyle/lib/properties/borderSpacing.js new file mode 100644 index 000000000..ff1ce882e --- /dev/null +++ b/docs/js/node_modules/cssstyle/lib/properties/borderSpacing.js @@ -0,0 +1,41 @@ +'use strict'; + +var parsers = require('../parsers'); + +// ? | inherit +// if one, it applies to both horizontal and verical spacing +// if two, the first applies to the horizontal and the second applies to vertical spacing + +var parse = function parse(v) { + if (v === '' || v === null) { + return undefined; + } + if (v === 0) { + return '0px'; + } + if (v.toLowerCase() === 'inherit') { + return v; + } + var parts = v.split(/\s+/); + if (parts.length !== 1 && parts.length !== 2) { + return undefined; + } + parts.forEach(function(part) { + if (parsers.valueType(part) !== parsers.TYPES.LENGTH) { + return undefined; + } + }); + + return v; +}; + +module.exports.definition = { + set: function(v) { + this._setProperty('border-spacing', parse(v)); + }, + get: function() { + return this.getPropertyValue('border-spacing'); + }, + enumerable: true, + configurable: true, +}; diff --git a/docs/js/node_modules/cssstyle/lib/properties/borderStyle.js b/docs/js/node_modules/cssstyle/lib/properties/borderStyle.js new file mode 100644 index 000000000..6e3e674f1 --- /dev/null +++ b/docs/js/node_modules/cssstyle/lib/properties/borderStyle.js @@ -0,0 +1,38 @@ +'use strict'; + +var implicitSetter = require('../parsers').implicitSetter; + +// the valid border-styles: +var styles = [ + 'none', + 'hidden', + 'dotted', + 'dashed', + 'solid', + 'double', + 'groove', + 'ridge', + 'inset', + 'outset', +]; + +module.exports.isValid = function parse(v) { + return typeof v === 'string' && (v === '' || styles.indexOf(v) !== -1); +}; +var isValid = module.exports.isValid; + +var parser = function(v) { + if (isValid(v)) { + return v.toLowerCase(); + } + return undefined; +}; + +module.exports.definition = { + set: implicitSetter('border', 'style', isValid, parser), + get: function() { + return this.getPropertyValue('border-style'); + }, + enumerable: true, + configurable: true, +}; diff --git a/docs/js/node_modules/cssstyle/lib/properties/borderTop.js b/docs/js/node_modules/cssstyle/lib/properties/borderTop.js new file mode 100644 index 000000000..c56d592d3 --- /dev/null +++ b/docs/js/node_modules/cssstyle/lib/properties/borderTop.js @@ -0,0 +1,17 @@ +'use strict'; + +var shorthandSetter = require('../parsers').shorthandSetter; +var shorthandGetter = require('../parsers').shorthandGetter; + +var shorthand_for = { + 'border-top-width': require('./borderTopWidth'), + 'border-top-style': require('./borderTopStyle'), + 'border-top-color': require('./borderTopColor'), +}; + +module.exports.definition = { + set: shorthandSetter('border-top', shorthand_for), + get: shorthandGetter('border-top', shorthand_for), + enumerable: true, + configurable: true, +}; diff --git a/docs/js/node_modules/cssstyle/lib/properties/borderTopColor.js b/docs/js/node_modules/cssstyle/lib/properties/borderTopColor.js new file mode 100644 index 000000000..cc353924a --- /dev/null +++ b/docs/js/node_modules/cssstyle/lib/properties/borderTopColor.js @@ -0,0 +1,16 @@ +'use strict'; + +var isValid = (module.exports.isValid = require('./borderColor').isValid); + +module.exports.definition = { + set: function(v) { + if (isValid(v)) { + this._setProperty('border-top-color', v); + } + }, + get: function() { + return this.getPropertyValue('border-top-color'); + }, + enumerable: true, + configurable: true, +}; diff --git a/docs/js/node_modules/cssstyle/lib/properties/borderTopStyle.js b/docs/js/node_modules/cssstyle/lib/properties/borderTopStyle.js new file mode 100644 index 000000000..938ea408f --- /dev/null +++ b/docs/js/node_modules/cssstyle/lib/properties/borderTopStyle.js @@ -0,0 +1,21 @@ +'use strict'; + +var isValid = require('./borderStyle').isValid; +module.exports.isValid = isValid; + +module.exports.definition = { + set: function(v) { + if (isValid(v)) { + if (v.toLowerCase() === 'none') { + v = ''; + this.removeProperty('border-top-width'); + } + this._setProperty('border-top-style', v); + } + }, + get: function() { + return this.getPropertyValue('border-top-style'); + }, + enumerable: true, + configurable: true, +}; diff --git a/docs/js/node_modules/cssstyle/lib/properties/borderTopWidth.js b/docs/js/node_modules/cssstyle/lib/properties/borderTopWidth.js new file mode 100644 index 000000000..940725351 --- /dev/null +++ b/docs/js/node_modules/cssstyle/lib/properties/borderTopWidth.js @@ -0,0 +1,17 @@ +'use strict'; + +var isValid = require('./borderWidth').isValid; +module.exports.isValid = isValid; + +module.exports.definition = { + set: function(v) { + if (isValid(v)) { + this._setProperty('border-top-width', v); + } + }, + get: function() { + return this.getPropertyValue('border-top-width'); + }, + enumerable: true, + configurable: true, +}; diff --git a/docs/js/node_modules/cssstyle/lib/properties/borderWidth.js b/docs/js/node_modules/cssstyle/lib/properties/borderWidth.js new file mode 100644 index 000000000..2b6d87183 --- /dev/null +++ b/docs/js/node_modules/cssstyle/lib/properties/borderWidth.js @@ -0,0 +1,46 @@ +'use strict'; + +var parsers = require('../parsers'); +var implicitSetter = require('../parsers').implicitSetter; + +// the valid border-widths: +var widths = ['thin', 'medium', 'thick']; + +module.exports.isValid = function parse(v) { + var length = parsers.parseLength(v); + if (length !== undefined) { + return true; + } + if (typeof v !== 'string') { + return false; + } + if (v === '') { + return true; + } + v = v.toLowerCase(); + if (widths.indexOf(v) === -1) { + return false; + } + return true; +}; +var isValid = module.exports.isValid; + +var parser = function(v) { + var length = parsers.parseLength(v); + if (length !== undefined) { + return length; + } + if (isValid(v)) { + return v.toLowerCase(); + } + return undefined; +}; + +module.exports.definition = { + set: implicitSetter('border', 'width', isValid, parser), + get: function() { + return this.getPropertyValue('border-width'); + }, + enumerable: true, + configurable: true, +}; diff --git a/docs/js/node_modules/cssstyle/lib/properties/bottom.js b/docs/js/node_modules/cssstyle/lib/properties/bottom.js new file mode 100644 index 000000000..e9d72b221 --- /dev/null +++ b/docs/js/node_modules/cssstyle/lib/properties/bottom.js @@ -0,0 +1,14 @@ +'use strict'; + +var parseMeasurement = require('../parsers').parseMeasurement; + +module.exports.definition = { + set: function(v) { + this._setProperty('bottom', parseMeasurement(v)); + }, + get: function() { + return this.getPropertyValue('bottom'); + }, + enumerable: true, + configurable: true, +}; diff --git a/docs/js/node_modules/cssstyle/lib/properties/clear.js b/docs/js/node_modules/cssstyle/lib/properties/clear.js new file mode 100644 index 000000000..22d980290 --- /dev/null +++ b/docs/js/node_modules/cssstyle/lib/properties/clear.js @@ -0,0 +1,16 @@ +'use strict'; + +var parseKeyword = require('../parsers').parseKeyword; + +var clear_keywords = ['none', 'left', 'right', 'both', 'inherit']; + +module.exports.definition = { + set: function(v) { + this._setProperty('clear', parseKeyword(v, clear_keywords)); + }, + get: function() { + return this.getPropertyValue('clear'); + }, + enumerable: true, + configurable: true, +}; diff --git a/docs/js/node_modules/cssstyle/lib/properties/clip.js b/docs/js/node_modules/cssstyle/lib/properties/clip.js new file mode 100644 index 000000000..91ba675e9 --- /dev/null +++ b/docs/js/node_modules/cssstyle/lib/properties/clip.js @@ -0,0 +1,47 @@ +'use strict'; + +var parseMeasurement = require('../parsers').parseMeasurement; + +var shape_regex = /^rect\((.*)\)$/i; + +var parse = function(val) { + if (val === '' || val === null) { + return val; + } + if (typeof val !== 'string') { + return undefined; + } + val = val.toLowerCase(); + if (val === 'auto' || val === 'inherit') { + return val; + } + var matches = val.match(shape_regex); + if (!matches) { + return undefined; + } + var parts = matches[1].split(/\s*,\s*/); + if (parts.length !== 4) { + return undefined; + } + var valid = parts.every(function(part, index) { + var measurement = parseMeasurement(part); + parts[index] = measurement; + return measurement !== undefined; + }); + if (!valid) { + return undefined; + } + parts = parts.join(', '); + return val.replace(matches[1], parts); +}; + +module.exports.definition = { + set: function(v) { + this._setProperty('clip', parse(v)); + }, + get: function() { + return this.getPropertyValue('clip'); + }, + enumerable: true, + configurable: true, +}; diff --git a/docs/js/node_modules/cssstyle/lib/properties/color.js b/docs/js/node_modules/cssstyle/lib/properties/color.js new file mode 100644 index 000000000..1b5ca3dcd --- /dev/null +++ b/docs/js/node_modules/cssstyle/lib/properties/color.js @@ -0,0 +1,14 @@ +'use strict'; + +var parseColor = require('../parsers').parseColor; + +module.exports.definition = { + set: function(v) { + this._setProperty('color', parseColor(v)); + }, + get: function() { + return this.getPropertyValue('color'); + }, + enumerable: true, + configurable: true, +}; diff --git a/docs/js/node_modules/cssstyle/lib/properties/cssFloat.js b/docs/js/node_modules/cssstyle/lib/properties/cssFloat.js new file mode 100644 index 000000000..1c619cc7a --- /dev/null +++ b/docs/js/node_modules/cssstyle/lib/properties/cssFloat.js @@ -0,0 +1,12 @@ +'use strict'; + +module.exports.definition = { + set: function(v) { + this._setProperty('float', v); + }, + get: function() { + return this.getPropertyValue('float'); + }, + enumerable: true, + configurable: true, +}; diff --git a/docs/js/node_modules/cssstyle/lib/properties/flex.js b/docs/js/node_modules/cssstyle/lib/properties/flex.js new file mode 100644 index 000000000..b56fd55de --- /dev/null +++ b/docs/js/node_modules/cssstyle/lib/properties/flex.js @@ -0,0 +1,45 @@ +'use strict'; + +var shorthandParser = require('../parsers').shorthandParser; +var shorthandSetter = require('../parsers').shorthandSetter; +var shorthandGetter = require('../parsers').shorthandGetter; + +var shorthand_for = { + 'flex-grow': require('./flexGrow'), + 'flex-shrink': require('./flexShrink'), + 'flex-basis': require('./flexBasis'), +}; + +var myShorthandSetter = shorthandSetter('flex', shorthand_for); + +module.exports.isValid = function isValid(v) { + return shorthandParser(v, shorthand_for) !== undefined; +}; + +module.exports.definition = { + set: function(v) { + var normalizedValue = String(v) + .trim() + .toLowerCase(); + + if (normalizedValue === 'none') { + myShorthandSetter.call(this, '0 0 auto'); + return; + } + if (normalizedValue === 'initial') { + myShorthandSetter.call(this, '0 1 auto'); + return; + } + if (normalizedValue === 'auto') { + this.removeProperty('flex-grow'); + this.removeProperty('flex-shrink'); + this.setProperty('flex-basis', normalizedValue); + return; + } + + myShorthandSetter.call(this, v); + }, + get: shorthandGetter('flex', shorthand_for), + enumerable: true, + configurable: true, +}; diff --git a/docs/js/node_modules/cssstyle/lib/properties/flexBasis.js b/docs/js/node_modules/cssstyle/lib/properties/flexBasis.js new file mode 100644 index 000000000..0c7cddf0e --- /dev/null +++ b/docs/js/node_modules/cssstyle/lib/properties/flexBasis.js @@ -0,0 +1,28 @@ +'use strict'; + +var parseMeasurement = require('../parsers').parseMeasurement; + +function parse(v) { + if (String(v).toLowerCase() === 'auto') { + return 'auto'; + } + if (String(v).toLowerCase() === 'inherit') { + return 'inherit'; + } + return parseMeasurement(v); +} + +module.exports.isValid = function isValid(v) { + return parse(v) !== undefined; +}; + +module.exports.definition = { + set: function(v) { + this._setProperty('flex-basis', parse(v)); + }, + get: function() { + return this.getPropertyValue('flex-basis'); + }, + enumerable: true, + configurable: true, +}; diff --git a/docs/js/node_modules/cssstyle/lib/properties/flexGrow.js b/docs/js/node_modules/cssstyle/lib/properties/flexGrow.js new file mode 100644 index 000000000..6e2966362 --- /dev/null +++ b/docs/js/node_modules/cssstyle/lib/properties/flexGrow.js @@ -0,0 +1,19 @@ +'use strict'; + +var parseNumber = require('../parsers').parseNumber; +var POSITION_AT_SHORTHAND = require('../constants').POSITION_AT_SHORTHAND; + +module.exports.isValid = function isValid(v, positionAtFlexShorthand) { + return parseNumber(v) !== undefined && positionAtFlexShorthand === POSITION_AT_SHORTHAND.first; +}; + +module.exports.definition = { + set: function(v) { + this._setProperty('flex-grow', parseNumber(v)); + }, + get: function() { + return this.getPropertyValue('flex-grow'); + }, + enumerable: true, + configurable: true, +}; diff --git a/docs/js/node_modules/cssstyle/lib/properties/flexShrink.js b/docs/js/node_modules/cssstyle/lib/properties/flexShrink.js new file mode 100644 index 000000000..63ff86fd2 --- /dev/null +++ b/docs/js/node_modules/cssstyle/lib/properties/flexShrink.js @@ -0,0 +1,19 @@ +'use strict'; + +var parseNumber = require('../parsers').parseNumber; +var POSITION_AT_SHORTHAND = require('../constants').POSITION_AT_SHORTHAND; + +module.exports.isValid = function isValid(v, positionAtFlexShorthand) { + return parseNumber(v) !== undefined && positionAtFlexShorthand === POSITION_AT_SHORTHAND.second; +}; + +module.exports.definition = { + set: function(v) { + this._setProperty('flex-shrink', parseNumber(v)); + }, + get: function() { + return this.getPropertyValue('flex-shrink'); + }, + enumerable: true, + configurable: true, +}; diff --git a/docs/js/node_modules/cssstyle/lib/properties/float.js b/docs/js/node_modules/cssstyle/lib/properties/float.js new file mode 100644 index 000000000..1c619cc7a --- /dev/null +++ b/docs/js/node_modules/cssstyle/lib/properties/float.js @@ -0,0 +1,12 @@ +'use strict'; + +module.exports.definition = { + set: function(v) { + this._setProperty('float', v); + }, + get: function() { + return this.getPropertyValue('float'); + }, + enumerable: true, + configurable: true, +}; diff --git a/docs/js/node_modules/cssstyle/lib/properties/floodColor.js b/docs/js/node_modules/cssstyle/lib/properties/floodColor.js new file mode 100644 index 000000000..8a4f29c69 --- /dev/null +++ b/docs/js/node_modules/cssstyle/lib/properties/floodColor.js @@ -0,0 +1,14 @@ +'use strict'; + +var parseColor = require('../parsers').parseColor; + +module.exports.definition = { + set: function(v) { + this._setProperty('flood-color', parseColor(v)); + }, + get: function() { + return this.getPropertyValue('flood-color'); + }, + enumerable: true, + configurable: true, +}; diff --git a/docs/js/node_modules/cssstyle/lib/properties/font.js b/docs/js/node_modules/cssstyle/lib/properties/font.js new file mode 100644 index 000000000..9492dc63c --- /dev/null +++ b/docs/js/node_modules/cssstyle/lib/properties/font.js @@ -0,0 +1,43 @@ +'use strict'; + +var TYPES = require('../parsers').TYPES; +var valueType = require('../parsers').valueType; +var shorthandParser = require('../parsers').shorthandParser; +var shorthandSetter = require('../parsers').shorthandSetter; +var shorthandGetter = require('../parsers').shorthandGetter; + +var shorthand_for = { + 'font-family': require('./fontFamily'), + 'font-size': require('./fontSize'), + 'font-style': require('./fontStyle'), + 'font-variant': require('./fontVariant'), + 'font-weight': require('./fontWeight'), + 'line-height': require('./lineHeight'), +}; + +var static_fonts = [ + 'caption', + 'icon', + 'menu', + 'message-box', + 'small-caption', + 'status-bar', + 'inherit', +]; + +var setter = shorthandSetter('font', shorthand_for); + +module.exports.definition = { + set: function(v) { + var short = shorthandParser(v, shorthand_for); + if (short !== undefined) { + return setter.call(this, v); + } + if (valueType(v) === TYPES.KEYWORD && static_fonts.indexOf(v.toLowerCase()) !== -1) { + this._setProperty('font', v); + } + }, + get: shorthandGetter('font', shorthand_for), + enumerable: true, + configurable: true, +}; diff --git a/docs/js/node_modules/cssstyle/lib/properties/fontFamily.js b/docs/js/node_modules/cssstyle/lib/properties/fontFamily.js new file mode 100644 index 000000000..40bd1c132 --- /dev/null +++ b/docs/js/node_modules/cssstyle/lib/properties/fontFamily.js @@ -0,0 +1,33 @@ +'use strict'; + +var TYPES = require('../parsers').TYPES; +var valueType = require('../parsers').valueType; + +var partsRegEx = /\s*,\s*/; +module.exports.isValid = function isValid(v) { + if (v === '' || v === null) { + return true; + } + var parts = v.split(partsRegEx); + var len = parts.length; + var i; + var type; + for (i = 0; i < len; i++) { + type = valueType(parts[i]); + if (type === TYPES.STRING || type === TYPES.KEYWORD) { + return true; + } + } + return false; +}; + +module.exports.definition = { + set: function(v) { + this._setProperty('font-family', v); + }, + get: function() { + return this.getPropertyValue('font-family'); + }, + enumerable: true, + configurable: true, +}; diff --git a/docs/js/node_modules/cssstyle/lib/properties/fontSize.js b/docs/js/node_modules/cssstyle/lib/properties/fontSize.js new file mode 100644 index 000000000..287c82a80 --- /dev/null +++ b/docs/js/node_modules/cssstyle/lib/properties/fontSize.js @@ -0,0 +1,28 @@ +'use strict'; + +var TYPES = require('../parsers').TYPES; +var valueType = require('../parsers').valueType; + +var absoluteSizes = ['xx-small', 'x-small', 'small', 'medium', 'large', 'x-large', 'xx-large']; +var relativeSizes = ['larger', 'smaller']; + +module.exports.isValid = function(v) { + var type = valueType(v.toLowerCase()); + return ( + type === TYPES.LENGTH || + type === TYPES.PERCENT || + (type === TYPES.KEYWORD && absoluteSizes.indexOf(v.toLowerCase()) !== -1) || + (type === TYPES.KEYWORD && relativeSizes.indexOf(v.toLowerCase()) !== -1) + ); +}; + +module.exports.definition = { + set: function(v) { + this._setProperty('font-size', v); + }, + get: function() { + return this.getPropertyValue('font-size'); + }, + enumerable: true, + configurable: true, +}; diff --git a/docs/js/node_modules/cssstyle/lib/properties/fontStyle.js b/docs/js/node_modules/cssstyle/lib/properties/fontStyle.js new file mode 100644 index 000000000..63d5e921f --- /dev/null +++ b/docs/js/node_modules/cssstyle/lib/properties/fontStyle.js @@ -0,0 +1,18 @@ +'use strict'; + +var valid_styles = ['normal', 'italic', 'oblique', 'inherit']; + +module.exports.isValid = function(v) { + return valid_styles.indexOf(v.toLowerCase()) !== -1; +}; + +module.exports.definition = { + set: function(v) { + this._setProperty('font-style', v); + }, + get: function() { + return this.getPropertyValue('font-style'); + }, + enumerable: true, + configurable: true, +}; diff --git a/docs/js/node_modules/cssstyle/lib/properties/fontVariant.js b/docs/js/node_modules/cssstyle/lib/properties/fontVariant.js new file mode 100644 index 000000000..f03b5ea22 --- /dev/null +++ b/docs/js/node_modules/cssstyle/lib/properties/fontVariant.js @@ -0,0 +1,18 @@ +'use strict'; + +var valid_variants = ['normal', 'small-caps', 'inherit']; + +module.exports.isValid = function isValid(v) { + return valid_variants.indexOf(v.toLowerCase()) !== -1; +}; + +module.exports.definition = { + set: function(v) { + this._setProperty('font-variant', v); + }, + get: function() { + return this.getPropertyValue('font-variant'); + }, + enumerable: true, + configurable: true, +}; diff --git a/docs/js/node_modules/cssstyle/lib/properties/fontWeight.js b/docs/js/node_modules/cssstyle/lib/properties/fontWeight.js new file mode 100644 index 000000000..b854f6ab7 --- /dev/null +++ b/docs/js/node_modules/cssstyle/lib/properties/fontWeight.js @@ -0,0 +1,33 @@ +'use strict'; + +var valid_weights = [ + 'normal', + 'bold', + 'bolder', + 'lighter', + '100', + '200', + '300', + '400', + '500', + '600', + '700', + '800', + '900', + 'inherit', +]; + +module.exports.isValid = function isValid(v) { + return valid_weights.indexOf(v.toLowerCase()) !== -1; +}; + +module.exports.definition = { + set: function(v) { + this._setProperty('font-weight', v); + }, + get: function() { + return this.getPropertyValue('font-weight'); + }, + enumerable: true, + configurable: true, +}; diff --git a/docs/js/node_modules/cssstyle/lib/properties/height.js b/docs/js/node_modules/cssstyle/lib/properties/height.js new file mode 100644 index 000000000..82543c05d --- /dev/null +++ b/docs/js/node_modules/cssstyle/lib/properties/height.js @@ -0,0 +1,24 @@ +'use strict'; + +var parseMeasurement = require('../parsers').parseMeasurement; + +function parse(v) { + if (String(v).toLowerCase() === 'auto') { + return 'auto'; + } + if (String(v).toLowerCase() === 'inherit') { + return 'inherit'; + } + return parseMeasurement(v); +} + +module.exports.definition = { + set: function(v) { + this._setProperty('height', parse(v)); + }, + get: function() { + return this.getPropertyValue('height'); + }, + enumerable: true, + configurable: true, +}; diff --git a/docs/js/node_modules/cssstyle/lib/properties/left.js b/docs/js/node_modules/cssstyle/lib/properties/left.js new file mode 100644 index 000000000..72bb2fafd --- /dev/null +++ b/docs/js/node_modules/cssstyle/lib/properties/left.js @@ -0,0 +1,14 @@ +'use strict'; + +var parseMeasurement = require('../parsers').parseMeasurement; + +module.exports.definition = { + set: function(v) { + this._setProperty('left', parseMeasurement(v)); + }, + get: function() { + return this.getPropertyValue('left'); + }, + enumerable: true, + configurable: true, +}; diff --git a/docs/js/node_modules/cssstyle/lib/properties/lightingColor.js b/docs/js/node_modules/cssstyle/lib/properties/lightingColor.js new file mode 100644 index 000000000..9f9643df4 --- /dev/null +++ b/docs/js/node_modules/cssstyle/lib/properties/lightingColor.js @@ -0,0 +1,14 @@ +'use strict'; + +var parseColor = require('../parsers').parseColor; + +module.exports.definition = { + set: function(v) { + this._setProperty('lighting-color', parseColor(v)); + }, + get: function() { + return this.getPropertyValue('lighting-color'); + }, + enumerable: true, + configurable: true, +}; diff --git a/docs/js/node_modules/cssstyle/lib/properties/lineHeight.js b/docs/js/node_modules/cssstyle/lib/properties/lineHeight.js new file mode 100644 index 000000000..6f7a037f6 --- /dev/null +++ b/docs/js/node_modules/cssstyle/lib/properties/lineHeight.js @@ -0,0 +1,26 @@ +'use strict'; + +var TYPES = require('../parsers').TYPES; +var valueType = require('../parsers').valueType; + +module.exports.isValid = function isValid(v) { + var type = valueType(v); + return ( + (type === TYPES.KEYWORD && v.toLowerCase() === 'normal') || + v.toLowerCase() === 'inherit' || + type === TYPES.NUMBER || + type === TYPES.LENGTH || + type === TYPES.PERCENT + ); +}; + +module.exports.definition = { + set: function(v) { + this._setProperty('line-height', v); + }, + get: function() { + return this.getPropertyValue('line-height'); + }, + enumerable: true, + configurable: true, +}; diff --git a/docs/js/node_modules/cssstyle/lib/properties/margin.js b/docs/js/node_modules/cssstyle/lib/properties/margin.js new file mode 100644 index 000000000..2a8f972bd --- /dev/null +++ b/docs/js/node_modules/cssstyle/lib/properties/margin.js @@ -0,0 +1,68 @@ +'use strict'; + +var parsers = require('../parsers.js'); +var TYPES = parsers.TYPES; + +var isValid = function(v) { + if (v.toLowerCase() === 'auto') { + return true; + } + var type = parsers.valueType(v); + return ( + type === TYPES.LENGTH || + type === TYPES.PERCENT || + (type === TYPES.INTEGER && (v === '0' || v === 0)) + ); +}; + +var parser = function(v) { + var V = v.toLowerCase(); + if (V === 'auto') { + return V; + } + return parsers.parseMeasurement(v); +}; + +var mySetter = parsers.implicitSetter('margin', '', isValid, parser); +var myGlobal = parsers.implicitSetter( + 'margin', + '', + function() { + return true; + }, + function(v) { + return v; + } +); + +module.exports.definition = { + set: function(v) { + if (typeof v === 'number') { + v = String(v); + } + if (typeof v !== 'string') { + return; + } + var V = v.toLowerCase(); + switch (V) { + case 'inherit': + case 'initial': + case 'unset': + case '': + myGlobal.call(this, V); + break; + + default: + mySetter.call(this, v); + break; + } + }, + get: function() { + return this.getPropertyValue('margin'); + }, + enumerable: true, + configurable: true, +}; + +module.exports.isValid = isValid; +module.exports.parser = parser; diff --git a/docs/js/node_modules/cssstyle/lib/properties/marginBottom.js b/docs/js/node_modules/cssstyle/lib/properties/marginBottom.js new file mode 100644 index 000000000..378172e24 --- /dev/null +++ b/docs/js/node_modules/cssstyle/lib/properties/marginBottom.js @@ -0,0 +1,13 @@ +'use strict'; + +var margin = require('./margin.js'); +var parsers = require('../parsers.js'); + +module.exports.definition = { + set: parsers.subImplicitSetter('margin', 'bottom', margin.isValid, margin.parser), + get: function() { + return this.getPropertyValue('margin-bottom'); + }, + enumerable: true, + configurable: true, +}; diff --git a/docs/js/node_modules/cssstyle/lib/properties/marginLeft.js b/docs/js/node_modules/cssstyle/lib/properties/marginLeft.js new file mode 100644 index 000000000..0c67317be --- /dev/null +++ b/docs/js/node_modules/cssstyle/lib/properties/marginLeft.js @@ -0,0 +1,13 @@ +'use strict'; + +var margin = require('./margin.js'); +var parsers = require('../parsers.js'); + +module.exports.definition = { + set: parsers.subImplicitSetter('margin', 'left', margin.isValid, margin.parser), + get: function() { + return this.getPropertyValue('margin-left'); + }, + enumerable: true, + configurable: true, +}; diff --git a/docs/js/node_modules/cssstyle/lib/properties/marginRight.js b/docs/js/node_modules/cssstyle/lib/properties/marginRight.js new file mode 100644 index 000000000..6cdf26bd2 --- /dev/null +++ b/docs/js/node_modules/cssstyle/lib/properties/marginRight.js @@ -0,0 +1,13 @@ +'use strict'; + +var margin = require('./margin.js'); +var parsers = require('../parsers.js'); + +module.exports.definition = { + set: parsers.subImplicitSetter('margin', 'right', margin.isValid, margin.parser), + get: function() { + return this.getPropertyValue('margin-right'); + }, + enumerable: true, + configurable: true, +}; diff --git a/docs/js/node_modules/cssstyle/lib/properties/marginTop.js b/docs/js/node_modules/cssstyle/lib/properties/marginTop.js new file mode 100644 index 000000000..6a57621b3 --- /dev/null +++ b/docs/js/node_modules/cssstyle/lib/properties/marginTop.js @@ -0,0 +1,13 @@ +'use strict'; + +var margin = require('./margin.js'); +var parsers = require('../parsers.js'); + +module.exports.definition = { + set: parsers.subImplicitSetter('margin', 'top', margin.isValid, margin.parser), + get: function() { + return this.getPropertyValue('margin-top'); + }, + enumerable: true, + configurable: true, +}; diff --git a/docs/js/node_modules/cssstyle/lib/properties/opacity.js b/docs/js/node_modules/cssstyle/lib/properties/opacity.js new file mode 100644 index 000000000..b26a3b68d --- /dev/null +++ b/docs/js/node_modules/cssstyle/lib/properties/opacity.js @@ -0,0 +1,14 @@ +'use strict'; + +var parseNumber = require('../parsers').parseNumber; + +module.exports.definition = { + set: function(v) { + this._setProperty('opacity', parseNumber(v)); + }, + get: function() { + return this.getPropertyValue('opacity'); + }, + enumerable: true, + configurable: true, +}; diff --git a/docs/js/node_modules/cssstyle/lib/properties/outlineColor.js b/docs/js/node_modules/cssstyle/lib/properties/outlineColor.js new file mode 100644 index 000000000..fc8093dfd --- /dev/null +++ b/docs/js/node_modules/cssstyle/lib/properties/outlineColor.js @@ -0,0 +1,14 @@ +'use strict'; + +var parseColor = require('../parsers').parseColor; + +module.exports.definition = { + set: function(v) { + this._setProperty('outline-color', parseColor(v)); + }, + get: function() { + return this.getPropertyValue('outline-color'); + }, + enumerable: true, + configurable: true, +}; diff --git a/docs/js/node_modules/cssstyle/lib/properties/padding.js b/docs/js/node_modules/cssstyle/lib/properties/padding.js new file mode 100644 index 000000000..1287b19ec --- /dev/null +++ b/docs/js/node_modules/cssstyle/lib/properties/padding.js @@ -0,0 +1,61 @@ +'use strict'; + +var parsers = require('../parsers.js'); +var TYPES = parsers.TYPES; + +var isValid = function(v) { + var type = parsers.valueType(v); + return ( + type === TYPES.LENGTH || + type === TYPES.PERCENT || + (type === TYPES.INTEGER && (v === '0' || v === 0)) + ); +}; + +var parser = function(v) { + return parsers.parseMeasurement(v); +}; + +var mySetter = parsers.implicitSetter('padding', '', isValid, parser); +var myGlobal = parsers.implicitSetter( + 'padding', + '', + function() { + return true; + }, + function(v) { + return v; + } +); + +module.exports.definition = { + set: function(v) { + if (typeof v === 'number') { + v = String(v); + } + if (typeof v !== 'string') { + return; + } + var V = v.toLowerCase(); + switch (V) { + case 'inherit': + case 'initial': + case 'unset': + case '': + myGlobal.call(this, V); + break; + + default: + mySetter.call(this, v); + break; + } + }, + get: function() { + return this.getPropertyValue('padding'); + }, + enumerable: true, + configurable: true, +}; + +module.exports.isValid = isValid; +module.exports.parser = parser; diff --git a/docs/js/node_modules/cssstyle/lib/properties/paddingBottom.js b/docs/js/node_modules/cssstyle/lib/properties/paddingBottom.js new file mode 100644 index 000000000..3ce88e576 --- /dev/null +++ b/docs/js/node_modules/cssstyle/lib/properties/paddingBottom.js @@ -0,0 +1,13 @@ +'use strict'; + +var padding = require('./padding.js'); +var parsers = require('../parsers.js'); + +module.exports.definition = { + set: parsers.subImplicitSetter('padding', 'bottom', padding.isValid, padding.parser), + get: function() { + return this.getPropertyValue('padding-bottom'); + }, + enumerable: true, + configurable: true, +}; diff --git a/docs/js/node_modules/cssstyle/lib/properties/paddingLeft.js b/docs/js/node_modules/cssstyle/lib/properties/paddingLeft.js new file mode 100644 index 000000000..04363385f --- /dev/null +++ b/docs/js/node_modules/cssstyle/lib/properties/paddingLeft.js @@ -0,0 +1,13 @@ +'use strict'; + +var padding = require('./padding.js'); +var parsers = require('../parsers.js'); + +module.exports.definition = { + set: parsers.subImplicitSetter('padding', 'left', padding.isValid, padding.parser), + get: function() { + return this.getPropertyValue('padding-left'); + }, + enumerable: true, + configurable: true, +}; diff --git a/docs/js/node_modules/cssstyle/lib/properties/paddingRight.js b/docs/js/node_modules/cssstyle/lib/properties/paddingRight.js new file mode 100644 index 000000000..ff9bd34eb --- /dev/null +++ b/docs/js/node_modules/cssstyle/lib/properties/paddingRight.js @@ -0,0 +1,13 @@ +'use strict'; + +var padding = require('./padding.js'); +var parsers = require('../parsers.js'); + +module.exports.definition = { + set: parsers.subImplicitSetter('padding', 'right', padding.isValid, padding.parser), + get: function() { + return this.getPropertyValue('padding-right'); + }, + enumerable: true, + configurable: true, +}; diff --git a/docs/js/node_modules/cssstyle/lib/properties/paddingTop.js b/docs/js/node_modules/cssstyle/lib/properties/paddingTop.js new file mode 100644 index 000000000..eca878168 --- /dev/null +++ b/docs/js/node_modules/cssstyle/lib/properties/paddingTop.js @@ -0,0 +1,13 @@ +'use strict'; + +var padding = require('./padding.js'); +var parsers = require('../parsers.js'); + +module.exports.definition = { + set: parsers.subImplicitSetter('padding', 'top', padding.isValid, padding.parser), + get: function() { + return this.getPropertyValue('padding-top'); + }, + enumerable: true, + configurable: true, +}; diff --git a/docs/js/node_modules/cssstyle/lib/properties/right.js b/docs/js/node_modules/cssstyle/lib/properties/right.js new file mode 100644 index 000000000..eb4c3d496 --- /dev/null +++ b/docs/js/node_modules/cssstyle/lib/properties/right.js @@ -0,0 +1,14 @@ +'use strict'; + +var parseMeasurement = require('../parsers').parseMeasurement; + +module.exports.definition = { + set: function(v) { + this._setProperty('right', parseMeasurement(v)); + }, + get: function() { + return this.getPropertyValue('right'); + }, + enumerable: true, + configurable: true, +}; diff --git a/docs/js/node_modules/cssstyle/lib/properties/stopColor.js b/docs/js/node_modules/cssstyle/lib/properties/stopColor.js new file mode 100644 index 000000000..912d8e207 --- /dev/null +++ b/docs/js/node_modules/cssstyle/lib/properties/stopColor.js @@ -0,0 +1,14 @@ +'use strict'; + +var parseColor = require('../parsers').parseColor; + +module.exports.definition = { + set: function(v) { + this._setProperty('stop-color', parseColor(v)); + }, + get: function() { + return this.getPropertyValue('stop-color'); + }, + enumerable: true, + configurable: true, +}; diff --git a/docs/js/node_modules/cssstyle/lib/properties/textLineThroughColor.js b/docs/js/node_modules/cssstyle/lib/properties/textLineThroughColor.js new file mode 100644 index 000000000..ae53dbbd1 --- /dev/null +++ b/docs/js/node_modules/cssstyle/lib/properties/textLineThroughColor.js @@ -0,0 +1,14 @@ +'use strict'; + +var parseColor = require('../parsers').parseColor; + +module.exports.definition = { + set: function(v) { + this._setProperty('text-line-through-color', parseColor(v)); + }, + get: function() { + return this.getPropertyValue('text-line-through-color'); + }, + enumerable: true, + configurable: true, +}; diff --git a/docs/js/node_modules/cssstyle/lib/properties/textOverlineColor.js b/docs/js/node_modules/cssstyle/lib/properties/textOverlineColor.js new file mode 100644 index 000000000..c6adf7ce6 --- /dev/null +++ b/docs/js/node_modules/cssstyle/lib/properties/textOverlineColor.js @@ -0,0 +1,14 @@ +'use strict'; + +var parseColor = require('../parsers').parseColor; + +module.exports.definition = { + set: function(v) { + this._setProperty('text-overline-color', parseColor(v)); + }, + get: function() { + return this.getPropertyValue('text-overline-color'); + }, + enumerable: true, + configurable: true, +}; diff --git a/docs/js/node_modules/cssstyle/lib/properties/textUnderlineColor.js b/docs/js/node_modules/cssstyle/lib/properties/textUnderlineColor.js new file mode 100644 index 000000000..a243a9ca5 --- /dev/null +++ b/docs/js/node_modules/cssstyle/lib/properties/textUnderlineColor.js @@ -0,0 +1,14 @@ +'use strict'; + +var parseColor = require('../parsers').parseColor; + +module.exports.definition = { + set: function(v) { + this._setProperty('text-underline-color', parseColor(v)); + }, + get: function() { + return this.getPropertyValue('text-underline-color'); + }, + enumerable: true, + configurable: true, +}; diff --git a/docs/js/node_modules/cssstyle/lib/properties/top.js b/docs/js/node_modules/cssstyle/lib/properties/top.js new file mode 100644 index 000000000..f71986fea --- /dev/null +++ b/docs/js/node_modules/cssstyle/lib/properties/top.js @@ -0,0 +1,14 @@ +'use strict'; + +var parseMeasurement = require('../parsers').parseMeasurement; + +module.exports.definition = { + set: function(v) { + this._setProperty('top', parseMeasurement(v)); + }, + get: function() { + return this.getPropertyValue('top'); + }, + enumerable: true, + configurable: true, +}; diff --git a/docs/js/node_modules/cssstyle/lib/properties/webkitBorderAfterColor.js b/docs/js/node_modules/cssstyle/lib/properties/webkitBorderAfterColor.js new file mode 100644 index 000000000..ed021949d --- /dev/null +++ b/docs/js/node_modules/cssstyle/lib/properties/webkitBorderAfterColor.js @@ -0,0 +1,14 @@ +'use strict'; + +var parseColor = require('../parsers').parseColor; + +module.exports.definition = { + set: function(v) { + this._setProperty('-webkit-border-after-color', parseColor(v)); + }, + get: function() { + return this.getPropertyValue('-webkit-border-after-color'); + }, + enumerable: true, + configurable: true, +}; diff --git a/docs/js/node_modules/cssstyle/lib/properties/webkitBorderBeforeColor.js b/docs/js/node_modules/cssstyle/lib/properties/webkitBorderBeforeColor.js new file mode 100644 index 000000000..a4507a196 --- /dev/null +++ b/docs/js/node_modules/cssstyle/lib/properties/webkitBorderBeforeColor.js @@ -0,0 +1,14 @@ +'use strict'; + +var parseColor = require('../parsers').parseColor; + +module.exports.definition = { + set: function(v) { + this._setProperty('-webkit-border-before-color', parseColor(v)); + }, + get: function() { + return this.getPropertyValue('-webkit-border-before-color'); + }, + enumerable: true, + configurable: true, +}; diff --git a/docs/js/node_modules/cssstyle/lib/properties/webkitBorderEndColor.js b/docs/js/node_modules/cssstyle/lib/properties/webkitBorderEndColor.js new file mode 100644 index 000000000..499545dcf --- /dev/null +++ b/docs/js/node_modules/cssstyle/lib/properties/webkitBorderEndColor.js @@ -0,0 +1,14 @@ +'use strict'; + +var parseColor = require('../parsers').parseColor; + +module.exports.definition = { + set: function(v) { + this._setProperty('-webkit-border-end-color', parseColor(v)); + }, + get: function() { + return this.getPropertyValue('-webkit-border-end-color'); + }, + enumerable: true, + configurable: true, +}; diff --git a/docs/js/node_modules/cssstyle/lib/properties/webkitBorderStartColor.js b/docs/js/node_modules/cssstyle/lib/properties/webkitBorderStartColor.js new file mode 100644 index 000000000..8429e3284 --- /dev/null +++ b/docs/js/node_modules/cssstyle/lib/properties/webkitBorderStartColor.js @@ -0,0 +1,14 @@ +'use strict'; + +var parseColor = require('../parsers').parseColor; + +module.exports.definition = { + set: function(v) { + this._setProperty('-webkit-border-start-color', parseColor(v)); + }, + get: function() { + return this.getPropertyValue('-webkit-border-start-color'); + }, + enumerable: true, + configurable: true, +}; diff --git a/docs/js/node_modules/cssstyle/lib/properties/webkitColumnRuleColor.js b/docs/js/node_modules/cssstyle/lib/properties/webkitColumnRuleColor.js new file mode 100644 index 000000000..7130d5f19 --- /dev/null +++ b/docs/js/node_modules/cssstyle/lib/properties/webkitColumnRuleColor.js @@ -0,0 +1,14 @@ +'use strict'; + +var parseColor = require('../parsers').parseColor; + +module.exports.definition = { + set: function(v) { + this._setProperty('-webkit-column-rule-color', parseColor(v)); + }, + get: function() { + return this.getPropertyValue('-webkit-column-rule-color'); + }, + enumerable: true, + configurable: true, +}; diff --git a/docs/js/node_modules/cssstyle/lib/properties/webkitMatchNearestMailBlockquoteColor.js b/docs/js/node_modules/cssstyle/lib/properties/webkitMatchNearestMailBlockquoteColor.js new file mode 100644 index 000000000..e075891c5 --- /dev/null +++ b/docs/js/node_modules/cssstyle/lib/properties/webkitMatchNearestMailBlockquoteColor.js @@ -0,0 +1,14 @@ +'use strict'; + +var parseColor = require('../parsers').parseColor; + +module.exports.definition = { + set: function(v) { + this._setProperty('-webkit-match-nearest-mail-blockquote-color', parseColor(v)); + }, + get: function() { + return this.getPropertyValue('-webkit-match-nearest-mail-blockquote-color'); + }, + enumerable: true, + configurable: true, +}; diff --git a/docs/js/node_modules/cssstyle/lib/properties/webkitTapHighlightColor.js b/docs/js/node_modules/cssstyle/lib/properties/webkitTapHighlightColor.js new file mode 100644 index 000000000..d01932947 --- /dev/null +++ b/docs/js/node_modules/cssstyle/lib/properties/webkitTapHighlightColor.js @@ -0,0 +1,14 @@ +'use strict'; + +var parseColor = require('../parsers').parseColor; + +module.exports.definition = { + set: function(v) { + this._setProperty('-webkit-tap-highlight-color', parseColor(v)); + }, + get: function() { + return this.getPropertyValue('-webkit-tap-highlight-color'); + }, + enumerable: true, + configurable: true, +}; diff --git a/docs/js/node_modules/cssstyle/lib/properties/webkitTextEmphasisColor.js b/docs/js/node_modules/cssstyle/lib/properties/webkitTextEmphasisColor.js new file mode 100644 index 000000000..cdeab5356 --- /dev/null +++ b/docs/js/node_modules/cssstyle/lib/properties/webkitTextEmphasisColor.js @@ -0,0 +1,14 @@ +'use strict'; + +var parseColor = require('../parsers').parseColor; + +module.exports.definition = { + set: function(v) { + this._setProperty('-webkit-text-emphasis-color', parseColor(v)); + }, + get: function() { + return this.getPropertyValue('-webkit-text-emphasis-color'); + }, + enumerable: true, + configurable: true, +}; diff --git a/docs/js/node_modules/cssstyle/lib/properties/webkitTextFillColor.js b/docs/js/node_modules/cssstyle/lib/properties/webkitTextFillColor.js new file mode 100644 index 000000000..ef5bd6732 --- /dev/null +++ b/docs/js/node_modules/cssstyle/lib/properties/webkitTextFillColor.js @@ -0,0 +1,14 @@ +'use strict'; + +var parseColor = require('../parsers').parseColor; + +module.exports.definition = { + set: function(v) { + this._setProperty('-webkit-text-fill-color', parseColor(v)); + }, + get: function() { + return this.getPropertyValue('-webkit-text-fill-color'); + }, + enumerable: true, + configurable: true, +}; diff --git a/docs/js/node_modules/cssstyle/lib/properties/webkitTextStrokeColor.js b/docs/js/node_modules/cssstyle/lib/properties/webkitTextStrokeColor.js new file mode 100644 index 000000000..72a227793 --- /dev/null +++ b/docs/js/node_modules/cssstyle/lib/properties/webkitTextStrokeColor.js @@ -0,0 +1,14 @@ +'use strict'; + +var parseColor = require('../parsers').parseColor; + +module.exports.definition = { + set: function(v) { + this._setProperty('-webkit-text-stroke-color', parseColor(v)); + }, + get: function() { + return this.getPropertyValue('-webkit-text-stroke-color'); + }, + enumerable: true, + configurable: true, +}; diff --git a/docs/js/node_modules/cssstyle/lib/properties/width.js b/docs/js/node_modules/cssstyle/lib/properties/width.js new file mode 100644 index 000000000..a8c365daa --- /dev/null +++ b/docs/js/node_modules/cssstyle/lib/properties/width.js @@ -0,0 +1,24 @@ +'use strict'; + +var parseMeasurement = require('../parsers').parseMeasurement; + +function parse(v) { + if (String(v).toLowerCase() === 'auto') { + return 'auto'; + } + if (String(v).toLowerCase() === 'inherit') { + return 'inherit'; + } + return parseMeasurement(v); +} + +module.exports.definition = { + set: function(v) { + this._setProperty('width', parse(v)); + }, + get: function() { + return this.getPropertyValue('width'); + }, + enumerable: true, + configurable: true, +}; diff --git a/docs/js/node_modules/cssstyle/lib/utils/getBasicPropertyDescriptor.js b/docs/js/node_modules/cssstyle/lib/utils/getBasicPropertyDescriptor.js new file mode 100644 index 000000000..ded2cc457 --- /dev/null +++ b/docs/js/node_modules/cssstyle/lib/utils/getBasicPropertyDescriptor.js @@ -0,0 +1,14 @@ +'use strict'; + +module.exports = function getBasicPropertyDescriptor(name) { + return { + set: function(v) { + this._setProperty(name, v); + }, + get: function() { + return this.getPropertyValue(name); + }, + enumerable: true, + configurable: true, + }; +}; diff --git a/docs/js/node_modules/cssstyle/package.json b/docs/js/node_modules/cssstyle/package.json new file mode 100644 index 000000000..e6aab8b56 --- /dev/null +++ b/docs/js/node_modules/cssstyle/package.json @@ -0,0 +1,105 @@ +{ + "_from": "cssstyle@^1.0.0", + "_id": "cssstyle@1.4.0", + "_inBundle": false, + "_integrity": "sha512-GBrLZYZ4X4x6/QEoBnIrqb8B/f5l4+8me2dkom/j1Gtbxy0kBv6OGzKuAsGM75bkGwGAFkt56Iwg28S3XTZgSA==", + "_location": "/cssstyle", + "_phantomChildren": {}, + "_requested": { + "type": "range", + "registry": true, + "raw": "cssstyle@^1.0.0", + "name": "cssstyle", + "escapedName": "cssstyle", + "rawSpec": "^1.0.0", + "saveSpec": null, + "fetchSpec": "^1.0.0" + }, + "_requiredBy": [ + "/jsdom" + ], + "_resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-1.4.0.tgz", + "_shasum": "9d31328229d3c565c61e586b02041a28fccdccf1", + "_spec": "cssstyle@^1.0.0", + "_where": "/Users/hectorip/Education/Eloquent-JavaScript-ES/node_modules/jsdom", + "bugs": { + "url": "https://github.com/jsakas/CSSStyleDeclaration/issues" + }, + "bundleDependencies": false, + "contributors": [ + { + "name": "Chad Walker", + "email": "chad@chad-cat-lore-eddie.com", + "url": "https://github.com/chad3814" + }, + { + "name": "Rafał Ruciński", + "email": "fatfisz@gmail.com", + "url": "https://fatfisz.com" + }, + { + "name": "Nikita Vasilyev", + "email": "me@elv1s.ru" + }, + { + "name": "Davide P. Cervone" + }, + { + "name": "Forbes Lindesay" + } + ], + "dependencies": { + "cssom": "0.3.x" + }, + "deprecated": false, + "description": "CSSStyleDeclaration Object Model implementation", + "devDependencies": { + "babel-generator": "~6.26.1", + "babel-traverse": "~6.26.0", + "babel-types": "~6.26.0", + "babylon": "~6.18.0", + "eslint": "5.13.0", + "eslint-config-prettier": "4.0.0", + "eslint-plugin-prettier": "3.0.1", + "nodeunit": "~0.11.3", + "npm-run-all": "^4.1.5", + "prettier": "1.16.4", + "request": "^2.88.0", + "resolve": "~1.8.1" + }, + "directories": { + "lib": "./lib" + }, + "homepage": "https://github.com/jsakas/CSSStyleDeclaration", + "keywords": [ + "CSS", + "CSSStyleDeclaration", + "StyleSheet" + ], + "license": "MIT", + "main": "./lib/CSSStyleDeclaration.js", + "maintainers": [ + { + "name": "Jon Sakas", + "email": "jon.sakas@gmail.com", + "url": "https://jon.sakas.co/" + } + ], + "name": "cssstyle", + "repository": { + "type": "git", + "url": "git+https://github.com/jsakas/CSSStyleDeclaration.git" + }, + "scripts": { + "download": "node ./scripts/download_latest_properties.js && eslint lib/allProperties.js --fix", + "generate": "run-p generate:*", + "generate:implemented_properties": "node ./scripts/generate_implemented_properties.js", + "generate:properties": "node ./scripts/generate_properties.js", + "lint": "npm run generate && eslint . --max-warnings 0", + "lint:fix": "eslint . --fix --max-warnings 0", + "prepublishOnly": "npm run test-ci", + "test": "npm run generate && nodeunit tests", + "test-ci": "npm run lint && npm run test" + }, + "version": "1.4.0" +} diff --git a/docs/js/node_modules/cssstyle/scripts/download_latest_properties.js b/docs/js/node_modules/cssstyle/scripts/download_latest_properties.js new file mode 100644 index 000000000..5f17ef580 --- /dev/null +++ b/docs/js/node_modules/cssstyle/scripts/download_latest_properties.js @@ -0,0 +1,88 @@ +'use strict'; + +/* + * W3C provides JSON list of all CSS properties and their status in the standard + * + * documentation: https://www.w3.org/Style/CSS/all-properties.en.html + * JSON url: ( https://www.w3.org/Style/CSS/all-properties.en.json ) + * + * Download that file, filter out duplicates and filter the properties based on the wanted standard level + * + * ED - Editors' Draft (not a W3C Technical Report) + * FPWD - First Public Working Draft + * WD - Working Draft + * LC - Last Call Working Draft + * CR - Candidate Recommendation + * PR - Proposed Recommendation + * REC - Recommendation + * NOTE - Working Group Note + */ + +var fs = require('fs'); +var path = require('path'); + +var request = require('request'); + +const { camelToDashed } = require('../lib/parsers'); + +var url = 'https://www.w3.org/Style/CSS/all-properties.en.json'; + +console.log('Downloading CSS properties...'); + +function toCamelCase(propName) { + return propName.replace(/-([a-z])/g, function(g) { + return g[1].toUpperCase(); + }); +} + +request(url, function(error, response, body) { + if (!error && response.statusCode === 200) { + var allCSSProperties = JSON.parse(body); + + // Filter out all properties newer than Working Draft + var workingDraftAndOlderProperties = allCSSProperties.filter(function(cssProp) { + // TODO: --* css Needs additional logic to this module, so filter it out for now + return cssProp.status !== 'ED' && cssProp.status !== 'FPWD' && cssProp.property !== '--*'; + }); + + // Remove duplicates, there can be many properties in different states of standard + // and add only property names to the list + var CSSpropertyNames = []; + workingDraftAndOlderProperties.forEach(function(cssProp) { + const camelCaseName = toCamelCase(cssProp.property); + + if (CSSpropertyNames.indexOf(camelCaseName) === -1) { + CSSpropertyNames.push(camelCaseName); + } + }); + + var out_file = fs.createWriteStream(path.resolve(__dirname, './../lib/allProperties.js'), { + encoding: 'utf-8', + }); + + var date_today = new Date(); + out_file.write( + "'use strict';\n\n// autogenerated - " + + (date_today.getMonth() + 1 + '/' + date_today.getDate() + '/' + date_today.getFullYear()) + + '\n\n' + ); + out_file.write('/*\n *\n * https://www.w3.org/Style/CSS/all-properties.en.html\n */\n\n'); + + out_file.write('var allProperties = new Set();\n'); + out_file.write('module.exports = allProperties;\n'); + + CSSpropertyNames.forEach(function(property) { + out_file.write('allProperties.add(' + JSON.stringify(camelToDashed(property)) + ');\n'); + }); + + out_file.end(function(err) { + if (err) { + throw err; + } + + console.log('Generated ' + Object.keys(CSSpropertyNames).length + ' properties.'); + }); + } else { + throw error; + } +}); diff --git a/docs/js/node_modules/cssstyle/scripts/generate_implemented_properties.js b/docs/js/node_modules/cssstyle/scripts/generate_implemented_properties.js new file mode 100644 index 000000000..caa88f12c --- /dev/null +++ b/docs/js/node_modules/cssstyle/scripts/generate_implemented_properties.js @@ -0,0 +1,61 @@ +'use strict'; + +const fs = require('fs'); +const path = require('path'); +const t = require('babel-types'); +const generate = require('babel-generator').default; +const camelToDashed = require('../lib/parsers').camelToDashed; + +const dashedProperties = fs + .readdirSync(path.resolve(__dirname, '../lib/properties')) + .filter(propertyFile => propertyFile.substr(-3) === '.js') + .map(propertyFile => camelToDashed(propertyFile.replace('.js', ''))); + +const out_file = fs.createWriteStream(path.resolve(__dirname, '../lib/implementedProperties.js'), { + encoding: 'utf-8', +}); +var date_today = new Date(); +out_file.write( + "'use strict';\n\n// autogenerated - " + + (date_today.getMonth() + 1 + '/' + date_today.getDate() + '/' + date_today.getFullYear()) + + '\n\n' +); +out_file.write('/*\n *\n * https://www.w3.org/Style/CSS/all-properties.en.html\n */\n\n'); + +const statements = []; +statements.push( + t.variableDeclaration('var', [ + t.variableDeclarator( + t.identifier('implementedProperties'), + t.newExpression(t.identifier('Set'), []) + ), + ]) +); + +dashedProperties.forEach(property => { + statements.push( + t.expressionStatement( + t.callExpression( + t.memberExpression(t.identifier('implementedProperties'), t.identifier('add')), + [t.stringLiteral(property)] + ) + ) + ); +}); + +statements.push( + t.expressionStatement( + t.assignmentExpression( + '=', + t.memberExpression(t.identifier('module'), t.identifier('exports')), + t.identifier('implementedProperties') + ) + ) +); + +out_file.write(generate(t.program(statements)).code + '\n'); +out_file.end(function(err) { + if (err) { + throw err; + } +}); diff --git a/docs/js/node_modules/cssstyle/scripts/generate_properties.js b/docs/js/node_modules/cssstyle/scripts/generate_properties.js new file mode 100644 index 000000000..33a42728b --- /dev/null +++ b/docs/js/node_modules/cssstyle/scripts/generate_properties.js @@ -0,0 +1,292 @@ +'use strict'; + +var fs = require('fs'); +var path = require('path'); +var babylon = require('babylon'); +var t = require('babel-types'); +var generate = require('babel-generator').default; +var traverse = require('babel-traverse').default; +var resolve = require('resolve'); + +var camelToDashed = require('../lib/parsers').camelToDashed; + +var basename = path.basename; +var dirname = path.dirname; + +var uniqueIndex = 0; +function getUniqueIndex() { + return uniqueIndex++; +} + +var property_files = fs + .readdirSync(path.resolve(__dirname, '../lib/properties')) + .filter(function(property) { + return property.substr(-3) === '.js'; + }); +var out_file = fs.createWriteStream(path.resolve(__dirname, '../lib/properties.js'), { + encoding: 'utf-8', +}); +var date_today = new Date(); +out_file.write( + "'use strict';\n\n// autogenerated - " + + (date_today.getMonth() + 1 + '/' + date_today.getDate() + '/' + date_today.getFullYear()) + + '\n\n' +); +out_file.write('/*\n *\n * https://www.w3.org/Style/CSS/all-properties.en.html\n */\n\n'); + +function isModuleDotExports(node) { + return ( + t.isMemberExpression(node, { computed: false }) && + t.isIdentifier(node.object, { name: 'module' }) && + t.isIdentifier(node.property, { name: 'exports' }) + ); +} +function isRequire(node, filename) { + if ( + t.isCallExpression(node) && + t.isIdentifier(node.callee, { name: 'require' }) && + node.arguments.length === 1 && + t.isStringLiteral(node.arguments[0]) + ) { + var relative = node.arguments[0].value; + var fullPath = resolve.sync(relative, { basedir: dirname(filename) }); + return { relative: relative, fullPath: fullPath }; + } else { + return false; + } +} + +// step 1: parse all files and figure out their dependencies +var parsedFilesByPath = {}; +property_files.map(function(property) { + var filename = path.resolve(__dirname, '../lib/properties/' + property); + var src = fs.readFileSync(filename, 'utf8'); + property = basename(property, '.js'); + var ast = babylon.parse(src); + var dependencies = []; + traverse(ast, { + enter(path) { + var r; + if ((r = isRequire(path.node, filename))) { + dependencies.push(r.fullPath); + } + }, + }); + parsedFilesByPath[filename] = { + filename: filename, + property: property, + ast: ast, + dependencies: dependencies, + }; +}); + +// step 2: serialize the files in an order where dependencies are always above +// the files they depend on +var externalDependencies = []; +var parsedFiles = []; +var addedFiles = {}; +function addFile(filename, dependencyPath) { + if (dependencyPath.indexOf(filename) !== -1) { + throw new Error( + 'Circular dependency: ' + + dependencyPath + .slice(dependencyPath.indexOf(filename)) + .concat([filename]) + .join(' -> ') + ); + } + var file = parsedFilesByPath[filename]; + if (addedFiles[filename]) { + return; + } + if (!file) { + externalDependencies.push(filename); + } else { + file.dependencies.forEach(function(dependency) { + addFile(dependency, dependencyPath.concat([filename])); + }); + parsedFiles.push(parsedFilesByPath[filename]); + } + addedFiles[filename] = true; +} +Object.keys(parsedFilesByPath).forEach(function(filename) { + addFile(filename, []); +}); +// Step 3: add files to output +// renaming exports to local variables `moduleName_export_exportName` +// and updating require calls as appropriate +var moduleExportsByPath = {}; +var statements = []; +externalDependencies.forEach(function(filename, i) { + var id = t.identifier( + 'external_dependency_' + basename(filename, '.js').replace(/[^A-Za-z]/g, '') + '_' + i + ); + moduleExportsByPath[filename] = { defaultExports: id }; + var relativePath = path.relative(path.resolve(__dirname + '/../lib'), filename); + if (relativePath[0] !== '.') { + relativePath = './' + relativePath; + } + statements.push( + t.variableDeclaration('var', [ + t.variableDeclarator( + id, + t.callExpression(t.identifier('require'), [t.stringLiteral(relativePath)]) + ), + ]) + ); +}); +function getRequireValue(node, file) { + var r, e; + // replace require("./foo").bar with the named export from foo + if ( + t.isMemberExpression(node, { computed: false }) && + (r = isRequire(node.object, file.filename)) + ) { + e = moduleExportsByPath[r.fullPath]; + if (!e) { + return; + } + if (!e.namedExports) { + return t.memberExpression(e.defaultExports, node.property); + } + if (!e.namedExports[node.property.name]) { + throw new Error(r.relative + ' does not export ' + node.property.name); + } + return e.namedExports[node.property.name]; + + // replace require("./foo") with the default export of foo + } else if ((r = isRequire(node, file.filename))) { + e = moduleExportsByPath[r.fullPath]; + if (!e) { + if (/^\.\.\//.test(r.relative)) { + node.arguments[0].value = r.relative.substr(1); + } + return; + } + return e.defaultExports; + } +} +parsedFiles.forEach(function(file) { + var namedExports = {}; + var localVariableMap = {}; + + traverse(file.ast, { + enter(path) { + // replace require calls with the corresponding value + var r; + if ((r = getRequireValue(path.node, file))) { + path.replaceWith(r); + return; + } + + // if we see `var foo = require('bar')` we can just inline the variable + // representing `require('bar')` wherever `foo` was used. + if ( + t.isVariableDeclaration(path.node) && + path.node.declarations.length === 1 && + t.isIdentifier(path.node.declarations[0].id) && + (r = getRequireValue(path.node.declarations[0].init, file)) + ) { + var newName = 'compiled_local_variable_reference_' + getUniqueIndex(); + path.scope.rename(path.node.declarations[0].id.name, newName); + localVariableMap[newName] = r; + path.remove(); + return; + } + + // rename all top level functions to keep them local to the module + if (t.isFunctionDeclaration(path.node) && t.isProgram(path.parent)) { + path.scope.rename(path.node.id.name, file.property + '_local_fn_' + path.node.id.name); + return; + } + + // rename all top level variables to keep them local to the module + if (t.isVariableDeclaration(path.node) && t.isProgram(path.parent)) { + path.node.declarations.forEach(function(declaration) { + path.scope.rename( + declaration.id.name, + file.property + '_local_var_' + declaration.id.name + ); + }); + return; + } + + // replace module.exports.bar with a variable for the named export + if ( + t.isMemberExpression(path.node, { computed: false }) && + isModuleDotExports(path.node.object) + ) { + var name = path.node.property.name; + var identifier = t.identifier(file.property + '_export_' + name); + path.replaceWith(identifier); + namedExports[name] = identifier; + } + }, + }); + traverse(file.ast, { + enter(path) { + if ( + t.isIdentifier(path.node) && + Object.prototype.hasOwnProperty.call(localVariableMap, path.node.name) + ) { + path.replaceWith(localVariableMap[path.node.name]); + } + }, + }); + var defaultExports = t.objectExpression( + Object.keys(namedExports).map(function(name) { + return t.objectProperty(t.identifier(name), namedExports[name]); + }) + ); + moduleExportsByPath[file.filename] = { + namedExports: namedExports, + defaultExports: defaultExports, + }; + statements.push( + t.variableDeclaration( + 'var', + Object.keys(namedExports).map(function(name) { + return t.variableDeclarator(namedExports[name]); + }) + ) + ); + statements.push.apply(statements, file.ast.program.body); +}); +var propertyDefinitions = []; +parsedFiles.forEach(function(file) { + var dashed = camelToDashed(file.property); + propertyDefinitions.push( + t.objectProperty( + t.identifier(file.property), + t.identifier(file.property + '_export_definition') + ) + ); + if (file.property !== dashed) { + propertyDefinitions.push( + t.objectProperty(t.stringLiteral(dashed), t.identifier(file.property + '_export_definition')) + ); + } +}); +var definePropertiesCall = t.callExpression( + t.memberExpression(t.identifier('Object'), t.identifier('defineProperties')), + [t.identifier('prototype'), t.objectExpression(propertyDefinitions)] +); +statements.push( + t.expressionStatement( + t.assignmentExpression( + '=', + t.memberExpression(t.identifier('module'), t.identifier('exports')), + t.functionExpression( + null, + [t.identifier('prototype')], + t.blockStatement([t.expressionStatement(definePropertiesCall)]) + ) + ) + ) +); +out_file.write(generate(t.program(statements)).code + '\n'); +out_file.end(function(err) { + if (err) { + throw err; + } +}); diff --git a/docs/js/node_modules/cssstyle/tests/tests.js b/docs/js/node_modules/cssstyle/tests/tests.js new file mode 100644 index 000000000..945040c20 --- /dev/null +++ b/docs/js/node_modules/cssstyle/tests/tests.js @@ -0,0 +1,669 @@ +'use strict'; +var cssstyle = require('../lib/CSSStyleDeclaration'); + +var { dashedToCamelCase } = require('../lib/parsers'); + +var dashedProperties = [ + ...require('../lib/allProperties'), + ...require('../lib/allExtraProperties'), +]; +var allowedProperties = dashedProperties.map(dashedToCamelCase); +var implementedProperties = Array.from(require('../lib/implementedProperties')).map( + dashedToCamelCase +); +var invalidProperties = implementedProperties.filter(function(property) { + return !allowedProperties.includes(property); +}); + +module.exports = { + 'Verify Has Only Valid Properties Implemented': function(test) { + test.expect(1); + test.ok( + invalidProperties.length === 0, + invalidProperties.length + + ' invalid properties implemented: ' + + Array.from(invalidProperties).join(', ') + ); + test.done(); + }, + 'Verify Has All Properties': function(test) { + var style = new cssstyle.CSSStyleDeclaration(); + test.expect(allowedProperties.length * 2); + allowedProperties.forEach(function(property) { + test.ok(style.__lookupGetter__(property), 'missing ' + property + ' property'); + test.ok(style.__lookupSetter__(property), 'missing ' + property + ' property'); + }); + test.done(); + }, + 'Verify Has All Dashed Properties': function(test) { + var style = new cssstyle.CSSStyleDeclaration(); + test.expect(dashedProperties.length * 2); + dashedProperties.forEach(function(property) { + test.ok(style.__lookupGetter__(property), 'missing ' + property + ' property'); + test.ok(style.__lookupSetter__(property), 'missing ' + property + ' property'); + }); + test.done(); + }, + 'Verify Has Functions': function(test) { + var style = new cssstyle.CSSStyleDeclaration(); + test.expect(6); + test.ok(typeof style.getPropertyValue === 'function', 'missing getPropertyValue()'); + test.ok(typeof style.getPropertyCSSValue === 'function', 'missing getPropertyCSSValue()'); + test.ok(typeof style.removeProperty === 'function', 'missing removeProperty()'); + test.ok(typeof style.getPropertyPriority === 'function', 'missing getPropertyPriority()'); + test.ok(typeof style.setProperty === 'function', 'missing setProperty()'); + test.ok(typeof style.item === 'function', 'missing item()'); + test.done(); + }, + 'Verify Has Special Properties': function(test) { + var style = new cssstyle.CSSStyleDeclaration(); + test.expect(5); + test.ok(style.__lookupGetter__('cssText'), 'missing cssText getter'); + test.ok(style.__lookupSetter__('cssText'), 'missing cssText setter'); + test.ok(style.__lookupGetter__('length'), 'missing length getter'); + test.ok(style.__lookupSetter__('length'), 'missing length setter'); + test.ok(style.__lookupGetter__('parentRule'), 'missing parentRule getter'); + test.done(); + }, + 'Test From Style String': function(test) { + var style = new cssstyle.CSSStyleDeclaration(); + test.expect(8); + style.cssText = 'color: blue; background-color: red; width: 78%; height: 50vh;'; + test.ok(4 === style.length, 'length is not 4'); + test.ok( + 'color: blue; background-color: red; width: 78%; height: 50vh;' === style.cssText, + 'cssText is wrong' + ); + test.ok('blue' === style.getPropertyValue('color'), "getPropertyValue('color') failed"); + test.ok('color' === style.item(0), 'item(0) failed'); + test.ok('background-color' === style[1], 'style[1] failed'); + test.ok( + 'red' === style.backgroundColor, + 'style.backgroundColor failed with "' + style.backgroundColor + '"' + ); + style.cssText = ''; + test.ok('' === style.cssText, 'cssText is not empty'); + test.ok(0 === style.length, 'length is not 0'); + test.done(); + }, + 'Test From Properties': function(test) { + var style = new cssstyle.CSSStyleDeclaration(); + test.expect(11); + style.color = 'blue'; + test.ok(1 === style.length, 'length is not 1'); + test.ok('color' === style[0], 'style[0] is not color'); + test.ok('color: blue;' === style.cssText, 'cssText is wrong'); + test.ok('color' === style.item(0), 'item(0) is not color'); + test.ok('blue' === style.color, 'color is not blue'); + style.backgroundColor = 'red'; + test.ok(2 === style.length, 'length is not 2'); + test.ok('color' === style[0], 'style[0] is not color'); + test.ok('background-color' === style[1], 'style[1] is not background-color'); + test.ok('color: blue; background-color: red;' === style.cssText, 'cssText is wrong'); + test.ok('red' === style.backgroundColor, 'backgroundColor is not red'); + style.removeProperty('color'); + test.ok('background-color' === style[0], 'style[0] is not background-color'); + test.done(); + }, + 'Test Shorthand Properties': function(test) { + var style = new cssstyle.CSSStyleDeclaration(); + test.expect(11); + style.background = 'blue url(http://www.example.com/some_img.jpg)'; + test.ok('blue' === style.backgroundColor, 'backgroundColor is not blue'); + test.ok( + 'url(http://www.example.com/some_img.jpg)' === style.backgroundImage, + 'backgroundImage is wrong' + ); + test.ok( + 'blue url(http://www.example.com/some_img.jpg)' === style.background, + 'background is different' + ); + style.border = '0 solid black'; + test.ok('0px' === style.borderWidth, 'borderWidth is not 0px'); + test.ok('solid' === style.borderStyle, 'borderStyle is not solid'); + test.ok('black' === style.borderColor, 'borderColor is not black'); + test.ok('0px' === style.borderTopWidth, 'borderTopWidth is not 0px'); + test.ok('solid' === style.borderLeftStyle, 'borderLeftStyle is not solid'); + test.ok('black' === style.borderBottomColor, 'borderBottomColor is not black'); + style.font = '12em monospace'; + test.ok('12em' === style.fontSize, 'fontSize is not 12em'); + test.ok('monospace' === style.fontFamily, 'fontFamily is not monospace'); + test.done(); + }, + 'Test width and height Properties and null and empty strings': function(test) { + var style = new cssstyle.CSSStyleDeclaration(); + test.expect(9); + style.height = 6; + test.ok('' === style.height, 'height does not remain unset'); + style.width = 0; + test.ok('0px' === style.width, 'width is not 0px'); + style.height = '34%'; + test.ok('34%' === style.height, 'height is not 34%'); + style.height = '100vh'; + test.ok('100vh' === style.height, 'height is not 100vh'); + style.height = '100vw'; + test.ok('100vw' === style.height, 'height is not 100vw'); + style.height = ''; + test.ok(style.length === 1, 'length is not 1'); + test.ok('width: 0px;' === style.cssText, 'cssText is not "width: 0px;"'); + style.width = null; + test.ok(style.length === 0, 'length is not 0'); + test.ok('' === style.cssText, 'cssText is not empty string'); + test.done(); + }, + 'Test Implicit Properties': function(test) { + var style = new cssstyle.CSSStyleDeclaration(); + test.expect(7); + style.borderWidth = 0; + test.ok(1 === style.length, 'length is not 1'); + test.ok('0px' === style.borderWidth, 'borderWidth is not 0px'); + test.ok('0px' === style.borderTopWidth, 'borderTopWidth is not 0px'); + test.ok('0px' === style.borderBottomWidth, 'borderBottomWidth is not 0px'); + test.ok('0px' === style.borderLeftWidth, 'borderLeftWidth is not 0px'); + test.ok('0px' === style.borderRightWidth, 'borderRightWidth is not 0px'); + test.ok( + 'border-width: 0px;' === style.cssText, + 'cssText is not "border-width: 0px", "' + style.cssText + '"' + ); + test.done(); + }, + 'Test Top, Left, Right, Bottom Properties': function(test) { + var style = new cssstyle.CSSStyleDeclaration(); + test.expect(6); + style.top = 0; + style.left = '0%'; + style.right = '5em'; + style.bottom = '12pt'; + test.ok('0px' === style.top, 'top is not 0px'); + test.ok('0%' === style.left, 'left is not 0%'); + test.ok('5em' === style.right, 'right is not 5em'); + test.ok('12pt' === style.bottom, 'bottom is not 12pt'); + test.ok(4 === style.length, 'length is not 4'); + test.ok( + 'top: 0px; left: 0%; right: 5em; bottom: 12pt;' === style.cssText, + 'text is not "top: 0px; left: 0%; right: 5em; bottom: 12pt;"' + ); + test.done(); + }, + 'Test Clear and Clip Properties': function(test) { + var style = new cssstyle.CSSStyleDeclaration(); + test.expect(10); + style.clear = 'none'; + test.ok('none' === style.clear, 'clear is not none'); + style.clear = 'lfet'; // intentionally wrong + test.ok('none' === style.clear, 'clear is not still none'); + style.clear = 'left'; + test.ok('left' === style.clear, 'clear is not left'); + style.clear = 'right'; + test.ok('right' === style.clear, 'clear is not right'); + style.clear = 'both'; + test.ok('both' === style.clear, 'clear is not both'); + style.clip = 'elipse(5px, 10px)'; + test.ok('' === style.clip, 'clip should not be set'); + test.ok(1 === style.length, 'length is not 1'); + style.clip = 'rect(0, 3Em, 2pt, 50%)'; + test.ok( + 'rect(0px, 3em, 2pt, 50%)' === style.clip, + 'clip is not "rect(0px, 3em, 2pt, 50%)", "' + style.clip + '"' + ); + test.ok(2 === style.length, 'length is not 2'); + test.ok( + 'clear: both; clip: rect(0px, 3em, 2pt, 50%);' === style.cssText, + 'cssText is not "clear: both; clip: rect(0px, 3em, 2pt, 50%);"' + ); + test.done(); + }, + 'Test colors': function(test) { + var style = new cssstyle.CSSStyleDeclaration(); + test.expect(9); + style.color = 'rgba(0,0,0,0)'; + test.ok('rgba(0, 0, 0, 0)' === style.color, 'color is not rgba(0, 0, 0, 0)'); + style.color = 'rgba(5%, 10%, 20%, 0.4)'; + test.ok('rgba(12, 25, 51, 0.4)' === style.color, 'color is not rgba(12, 25, 51, 0.4)'); + style.color = 'rgb(33%, 34%, 33%)'; + test.ok('rgb(84, 86, 84)' === style.color, 'color is not rgb(84, 86, 84)'); + style.color = 'rgba(300, 200, 100, 1.5)'; + test.ok('rgb(255, 200, 100)' === style.color, 'color is not rgb(255, 200, 100) ' + style.color); + style.color = 'hsla(0, 1%, 2%, 0.5)'; + test.ok( + 'hsla(0, 1%, 2%, 0.5)' === style.color, + 'color is not hsla(0, 1%, 2%, 0.5) ' + style.color + ); + style.color = 'hsl(0, 1%, 2%)'; + test.ok('hsl(0, 1%, 2%)' === style.color, 'color is not hsl(0, 1%, 2%) ' + style.color); + style.color = 'rebeccapurple'; + test.ok('rebeccapurple' === style.color, 'color is not rebeccapurple ' + style.color); + style.color = 'transparent'; + test.ok('transparent' === style.color, 'color is not transparent ' + style.color); + style.color = 'currentcolor'; + test.ok('currentcolor' === style.color, 'color is not currentcolor ' + style.color); + test.done(); + }, + 'Test short hand properties with embedded spaces': function(test) { + var style = new cssstyle.CSSStyleDeclaration(); + test.expect(4); + style.background = 'rgb(0, 0, 0) url(/something/somewhere.jpg)'; + test.ok( + 'rgb(0, 0, 0)' === style.backgroundColor, + 'backgroundColor is not rgb(0, 0, 0): ' + style.backgroundColor + ); + test.ok( + 'url(/something/somewhere.jpg)' === style.backgroundImage, + 'backgroundImage is not url(/something/somewhere.jpg): ' + style.backgroundImage + ); + test.ok( + 'background: rgb(0, 0, 0) url(/something/somewhere.jpg);' === style.cssText, + 'cssText is not correct: ' + style.cssText + ); + style = new cssstyle.CSSStyleDeclaration(); + style.border = ' 1px solid black '; + test.ok( + '1px solid black' === style.border, + 'multiple spaces not properly parsed: ' + style.border + ); + test.done(); + }, + 'Setting shorthand properties to an empty string should clear all dependent properties': function( + test + ) { + var style = new cssstyle.CSSStyleDeclaration(); + test.expect(2); + style.borderWidth = '1px'; + test.ok( + 'border-width: 1px;' === style.cssText, + 'cssText is not "border-width: 1px;": ' + style.cssText + ); + style.border = ''; + test.ok('' === style.cssText, 'cssText is not "": ' + style.cssText); + test.done(); + }, + 'Setting implicit properties to an empty string should clear all dependent properties': function( + test + ) { + var style = new cssstyle.CSSStyleDeclaration(); + test.expect(2); + style.borderTopWidth = '1px'; + test.ok( + 'border-top-width: 1px;' === style.cssText, + 'cssText is not "border-top-width: 1px;": ' + style.cssText + ); + style.borderWidth = ''; + test.ok('' === style.cssText, 'cssText is not "": ' + style.cssText); + test.done(); + }, + 'Setting a shorthand property, whose shorthands are implicit properties, to an empty string should clear all dependent properties': function( + test + ) { + var style = new cssstyle.CSSStyleDeclaration(); + test.expect(4); + style.borderTopWidth = '1px'; + test.ok( + 'border-top-width: 1px;' === style.cssText, + 'cssText is not "border-top-width: 1px;": ' + style.cssText + ); + style.border = ''; + test.ok('' === style.cssText, 'cssText is not "": ' + style.cssText); + style.borderTop = '1px solid black'; + test.ok( + 'border-top: 1px solid black;' === style.cssText, + 'cssText is not "border-top: 1px solid black;": ' + style.cssText + ); + style.border = ''; + test.ok('' === style.cssText, 'cssText is not "": ' + style.cssText); + test.done(); + }, + 'Setting border values to "none" should clear dependent values': function(test) { + var style = new cssstyle.CSSStyleDeclaration(); + test.expect(8); + style.borderTopWidth = '1px'; + test.ok( + 'border-top-width: 1px;' === style.cssText, + 'cssText is not "border-top-width: 1px;": ' + style.cssText + ); + style.border = 'none'; + test.ok('' === style.cssText, 'cssText is not "": ' + style.cssText); + style.borderTopWidth = '1px'; + test.ok( + 'border-top-width: 1px;' === style.cssText, + 'cssText is not "border-top-width: 1px;": ' + style.cssText + ); + style.borderTopStyle = 'none'; + test.ok('' === style.cssText, 'cssText is not "": ' + style.cssText); + style.borderTopWidth = '1px'; + test.ok( + 'border-top-width: 1px;' === style.cssText, + 'cssText is not "border-top-width: 1px;": ' + style.cssText + ); + style.borderTop = 'none'; + test.ok('' === style.cssText, 'cssText is not "": ' + style.cssText); + style.borderTopWidth = '1px'; + style.borderLeftWidth = '1px'; + test.ok( + 'border-top-width: 1px; border-left-width: 1px;' === style.cssText, + 'cssText is not "border-top-width: 1px; border-left-width: 1px;": ' + style.cssText + ); + style.borderTop = 'none'; + test.ok( + 'border-left-width: 1px;' === style.cssText, + 'cssText is not "border-left-width: 1px;": ' + style.cssText + ); + test.done(); + }, + 'Setting border to 0 should be okay': function(test) { + var style = new cssstyle.CSSStyleDeclaration(); + test.expect(1); + style.border = 0; + test.ok('border: 0px;' === style.cssText, 'cssText is not "border: 0px;": ' + style.cssText); + test.done(); + }, + 'Setting values implicit and shorthand properties via cssText and setProperty should propagate to dependent properties': function( + test + ) { + var style = new cssstyle.CSSStyleDeclaration(); + test.expect(4); + style.cssText = 'border: 1px solid black;'; + test.ok( + 'border: 1px solid black;' === style.cssText, + 'cssText is not "border: 1px solid black;": ' + style.cssText + ); + test.ok( + '1px solid black' === style.borderTop, + 'borderTop is not "1px solid black": ' + style.borderTop + ); + style.border = ''; + test.ok('' === style.cssText, 'cssText is not "": ' + style.cssText); + style.setProperty('border', '1px solid black'); + test.ok( + 'border: 1px solid black;' === style.cssText, + 'cssText is not "border: 1px solid black;": ' + style.cssText + ); + test.done(); + }, + 'Setting opacity should work': function(test) { + var style = new cssstyle.CSSStyleDeclaration(); + test.expect(3); + style.setProperty('opacity', 0.75); + test.ok( + 'opacity: 0.75;' === style.cssText, + 'cssText is not "opacity: 0.75;": ' + style.cssText + ); + style.opacity = '0.50'; + test.ok('opacity: 0.5;' === style.cssText, 'cssText is not "opacity: 0.5;": ' + style.cssText); + style.opacity = 1.0; + test.ok('opacity: 1;' === style.cssText, 'cssText is not "opacity: 1;": ' + style.cssText); + test.done(); + }, + 'Width and height of auto should work': function(test) { + var style = new cssstyle.CSSStyleDeclaration(); + test.expect(4); + style.width = 'auto'; + test.equal(style.cssText, 'width: auto;', 'cssText is not "width: auto;": ' + style.cssText); + test.equal(style.width, 'auto', 'width is not "auto": ' + style.width); + style = new cssstyle.CSSStyleDeclaration(); + style.height = 'auto'; + test.equal(style.cssText, 'height: auto;', 'cssText is not "height: auto;": ' + style.cssText); + test.equal(style.height, 'auto', 'height is not "auto": ' + style.height); + test.done(); + }, + 'Padding and margin should set/clear shorthand properties': function(test) { + var style = new cssstyle.CSSStyleDeclaration(); + var parts = ['Top', 'Right', 'Bottom', 'Left']; + var testParts = function(name, v, V) { + style[name] = v; + for (var i = 0; i < 4; i++) { + var part = name + parts[i]; + test.equal(V[i], style[part], part + ' is not "' + V[i] + '": "' + style[part] + '"'); + } + test.equal(v, style[name], name + ' is not "' + v + '": "' + style[name] + '"'); + style[name] = ''; + }; + test.expect(50); + testParts('padding', '1px', ['1px', '1px', '1px', '1px']); + testParts('padding', '1px 2%', ['1px', '2%', '1px', '2%']); + testParts('padding', '1px 2px 3px', ['1px', '2px', '3px', '2px']); + testParts('padding', '1px 2px 3px 4px', ['1px', '2px', '3px', '4px']); + style.paddingTop = style.paddingRight = style.paddingBottom = style.paddingLeft = '1px'; + testParts('padding', '', ['', '', '', '']); + testParts('margin', '1px', ['1px', '1px', '1px', '1px']); + testParts('margin', '1px auto', ['1px', 'auto', '1px', 'auto']); + testParts('margin', '1px 2% 3px', ['1px', '2%', '3px', '2%']); + testParts('margin', '1px 2px 3px 4px', ['1px', '2px', '3px', '4px']); + style.marginTop = style.marginRight = style.marginBottom = style.marginLeft = '1px'; + testParts('margin', '', ['', '', '', '']); + test.done(); + }, + 'Padding and margin shorthands should set main properties': function(test) { + var style = new cssstyle.CSSStyleDeclaration(); + var parts = ['Top', 'Right', 'Bottom', 'Left']; + var testParts = function(name, v, V) { + var expect; + for (var i = 0; i < 4; i++) { + style[name] = v; + style[name + parts[i]] = V; + expect = v.split(/ /); + expect[i] = V; + expect = expect.join(' '); + test.equal(expect, style[name], name + ' is not "' + expect + '": "' + style[name] + '"'); + } + }; + test.expect(12); + testParts('padding', '1px 2px 3px 4px', '10px'); + testParts('margin', '1px 2px 3px 4px', '10px'); + testParts('margin', '1px 2px 3px 4px', 'auto'); + test.done(); + }, + 'Setting a value to 0 should return the string value': function(test) { + var style = new cssstyle.CSSStyleDeclaration(); + test.expect(1); + style.setProperty('fill-opacity', 0); + test.ok('0' === style.fillOpacity, 'fillOpacity is not "0": ' + style.fillOpacity); + test.done(); + }, + 'onChange callback should be called when the cssText changes': function(test) { + var style = new cssstyle.CSSStyleDeclaration(function(cssText) { + test.ok('opacity: 0;' === cssText, 'cssText is not "opacity: 0;": ' + cssText); + test.done(); + }); + test.expect(1); + style.setProperty('opacity', 0); + }, + 'Setting float should work the same as cssFloat': function(test) { + var style = new cssstyle.CSSStyleDeclaration(); + test.expect(1); + style.float = 'left'; + test.ok('left' === style.cssFloat, 'cssFloat is not "left": ' + style.cssFloat); + test.done(); + }, + 'Setting improper css to cssText should not throw': function(test) { + var style = new cssstyle.CSSStyleDeclaration(); + test.expect(2); + style.cssText = 'color: '; + test.ok('' === style.cssText, "cssText wasn't cleared: " + style.cssText); + style.color = 'black'; + style.cssText = 'float: '; + test.ok('' === style.cssText, "cssText wasn't cleared: " + style.cssText); + test.done(); + }, + 'Make sure url parsing works with quotes': function(test) { + var style = new cssstyle.CSSStyleDeclaration(); + test.expect(3); + style.backgroundImage = 'url(http://some/url/here1.png)'; + test.ok( + 'url(http://some/url/here1.png)' === style.backgroundImage, + "background-image wasn't url(http://some/url/here1.png): " + style.backgroundImage + ); + style.backgroundImage = "url('http://some/url/here2.png')"; + test.ok( + 'url(http://some/url/here2.png)' === style.backgroundImage, + "background-image wasn't url(http://some/url/here2.png): " + style.backgroundImage + ); + style.backgroundImage = 'url("http://some/url/here3.png")'; + test.ok( + 'url(http://some/url/here3.png)' === style.backgroundImage, + "background-image wasn't url(http://some/url/here3.png): " + style.backgroundImage + ); + test.done(); + }, + 'Make sure setting 0 to a padding or margin works': function(test) { + var style = new cssstyle.CSSStyleDeclaration(); + test.expect(2); + style.padding = 0; + test.equal(style.cssText, 'padding: 0px;', 'padding is not 0px'); + style.margin = '1em'; + style.marginTop = '0'; + test.equal(style.marginTop, '0px', 'margin-top is not 0px'); + test.done(); + }, + 'Make sure setting ex units to a padding or margin works': function(test) { + var style = new cssstyle.CSSStyleDeclaration(); + test.expect(2); + style.padding = '1ex'; + test.equal(style.cssText, 'padding: 1ex;', 'padding is not 1ex'); + style.margin = '1em'; + style.marginTop = '0.5ex'; + test.equal(style.marginTop, '0.5ex', 'margin-top is not 0.5ex'); + test.done(); + }, + 'Make sure setting null to background works': function(test) { + var style = new cssstyle.CSSStyleDeclaration(); + test.expect(2); + style.background = 'red'; + test.equal(style.cssText, 'background: red;', 'background is not red'); + style.background = null; + test.equal(style.cssText, '', 'cssText is not empty'); + test.done(); + }, + 'Flex properties should keep their values': function(test) { + var style = new cssstyle.CSSStyleDeclaration(); + test.expect(2); + style.flexDirection = 'column'; + test.equal(style.cssText, 'flex-direction: column;', 'flex-direction is not column'); + style.flexDirection = 'row'; + test.equal(style.cssText, 'flex-direction: row;', 'flex-direction is not column'); + test.done(); + }, + 'Make sure camelCase properties are not assigned with `.setProperty()`': function(test) { + var style = new cssstyle.CSSStyleDeclaration(); + test.expect(1); + style.setProperty('fontSize', '12px'); + test.equal(style.cssText, '', 'cssText is not empty'); + test.done(); + }, + 'Make sure casing is ignored in `.setProperty()`': function(test) { + var style = new cssstyle.CSSStyleDeclaration(); + test.expect(2); + style.setProperty('FoNt-SiZe', '12px'); + test.equal(style.fontSize, '12px', 'font-size: 12px'); + test.equal(style.getPropertyValue('font-size'), '12px', 'font-size: 12px'); + test.done(); + }, + 'Support non string entries in border-spacing': function(test) { + var style = new cssstyle.CSSStyleDeclaration(); + test.expect(1); + style.borderSpacing = 0; + test.equal(style.cssText, 'border-spacing: 0px;', 'border-spacing is not 0'); + test.done(); + }, + 'Float should be valid property for `.setProperty()`': function(test) { + var style = new cssstyle.CSSStyleDeclaration(); + test.expect(2); + style.setProperty('float', 'left'); + test.equal(style.float, 'left'); + test.equal(style.getPropertyValue('float'), 'left', 'float: left'); + test.done(); + }, + 'Make sure flex-shrink works': function(test) { + var style = new cssstyle.CSSStyleDeclaration(); + test.expect(3); + style.setProperty('flex-shrink', 0); + test.equal(style.getPropertyValue('flex-shrink'), '0', 'flex-shrink is not 0'); + style.setProperty('flex-shrink', 1); + test.equal(style.getPropertyValue('flex-shrink'), '1', 'flex-shrink is not 1'); + test.equal(style.cssText, 'flex-shrink: 1;', 'flex-shrink cssText is incorrect'); + test.done(); + }, + 'Make sure flex-grow works': function(test) { + var style = new cssstyle.CSSStyleDeclaration(); + test.expect(2); + style.setProperty('flex-grow', 2); + test.equal(style.getPropertyValue('flex-grow'), '2', 'flex-grow is not 2'); + test.equal(style.cssText, 'flex-grow: 2;', 'flex-grow cssText is incorrect'); + test.done(); + }, + 'Make sure flex-basis works': function(test) { + var style = new cssstyle.CSSStyleDeclaration(); + test.expect(5); + + style.setProperty('flex-basis', 0); + test.equal(style.getPropertyValue('flex-basis'), '0px', 'flex-basis is not 0px'); + + style.setProperty('flex-basis', '250px'); + test.equal(style.getPropertyValue('flex-basis'), '250px', 'flex-basis is not 250px'); + + style.setProperty('flex-basis', '10em'); + test.equal(style.getPropertyValue('flex-basis'), '10em', 'flex-basis is not 10em'); + + style.setProperty('flex-basis', '30%'); + test.equal(style.getPropertyValue('flex-basis'), '30%', 'flex-basis is not 30%'); + + test.equal(style.cssText, 'flex-basis: 30%;', 'flex-basis cssText is incorrect'); + + test.done(); + }, + 'Make sure shorthand flex works': function(test) { + var style = new cssstyle.CSSStyleDeclaration(); + test.expect(19); + + style.setProperty('flex', 'none'); + test.equal(style.getPropertyValue('flex-grow'), '0', 'flex-grow is not 0 if flex: none;'); + test.equal(style.getPropertyValue('flex-shrink'), '0', 'flex-shrink is not 0 if flex: none;'); + test.equal( + style.getPropertyValue('flex-basis'), + 'auto', + 'flex-basis is not `auto` if flex: none;' + ); + style.removeProperty('flex'); + style.removeProperty('flex-basis'); + + style.setProperty('flex', 'auto'); + test.equal(style.getPropertyValue('flex-grow'), '', 'flex-grow is not empty if flex: auto;'); + test.equal( + style.getPropertyValue('flex-shrink'), + '', + 'flex-shrink is not empty if flex: auto;' + ); + test.equal( + style.getPropertyValue('flex-basis'), + 'auto', + 'flex-basis is not `auto` if flex: auto;' + ); + style.removeProperty('flex'); + + style.setProperty('flex', '0 1 250px'); + test.equal(style.getPropertyValue('flex'), '0 1 250px', 'flex value is not `0 1 250px`'); + test.equal(style.getPropertyValue('flex-grow'), '0', 'flex-grow is not 0'); + test.equal(style.getPropertyValue('flex-shrink'), '1', 'flex-shrink is not 1'); + test.equal(style.getPropertyValue('flex-basis'), '250px', 'flex-basis is not 250px'); + style.removeProperty('flex'); + + style.setProperty('flex', '2'); + test.equal(style.getPropertyValue('flex-grow'), '2', 'flex-grow is not 2'); + test.equal(style.getPropertyValue('flex-shrink'), '', 'flex-shrink is not empty'); + test.equal(style.getPropertyValue('flex-basis'), '', 'flex-basis is not empty'); + style.removeProperty('flex'); + + style.setProperty('flex', '20%'); + test.equal(style.getPropertyValue('flex-grow'), '', 'flex-grow is not empty'); + test.equal(style.getPropertyValue('flex-shrink'), '', 'flex-shrink is not empty'); + test.equal(style.getPropertyValue('flex-basis'), '20%', 'flex-basis is not 20%'); + style.removeProperty('flex'); + + style.setProperty('flex', '2 2'); + test.equal(style.getPropertyValue('flex-grow'), '2', 'flex-grow is not 2'); + test.equal(style.getPropertyValue('flex-shrink'), '2', 'flex-shrink is not 2'); + test.equal(style.getPropertyValue('flex-basis'), '', 'flex-basis is not empty'); + style.removeProperty('flex'); + + test.done(); + }, +}; diff --git a/docs/js/node_modules/dashdash/CHANGES.md b/docs/js/node_modules/dashdash/CHANGES.md new file mode 100644 index 000000000..d7c8f4ebe --- /dev/null +++ b/docs/js/node_modules/dashdash/CHANGES.md @@ -0,0 +1,364 @@ +# node-dashdash changelog + +## not yet released + +(nothing yet) + +## 1.14.1 + +- [issue #30] Change the output used by dashdash's Bash completion support to + indicate "there are no completions for this argument" to cope with different + sorting rules on different Bash/platforms. For example: + + $ triton -v -p test2 package get # before + ##-no -tritonpackage- completions-## + + $ triton -v -p test2 package get # after + ##-no-completion- -results-## + +## 1.14.0 + +- New `synopsisFromOpt(