| 1 | # R Functions | |
| 2 | ||
| 3 | Import the files in this directory into the application, which include: | |
| 4 | ||
| 5 | * bootstrap.R | |
| 6 | * pluralize.R | |
| 7 | * possessive.R | |
| 8 | * conversion.R | |
| 9 | * csv.R | |
| 10 | ||
| 11 | # bootstrap.R | |
| 12 | ||
| 13 | Copy the contents of `bootstrap.R` into the R script preferences, shown in the | |
| 14 | following figure, then restart the application: | |
| 15 | ||
| 16 | #  | |
| 17 | ||
| 18 | Setting the **Working Directory** allows the startup script to load files | |
| 19 | using a relative to said directory. | |
| 20 | ||
| 21 | # pluralize.R | |
| 22 | ||
| 23 | This file defines a function that implements most of Damian Conway's [An Algorithmic Approach to English Pluralization](http://blob.perl.org/tpc/1998/User_Applications/Algorithmic%20Approach%20Plurals/Algorithmic_Plurals.html). | |
| 24 | ||
| 25 | ## Usage | |
| 26 | ||
| 27 | Example usages of the pluralize function include: | |
| 28 | ||
| 29 | `r#pluralize( "mouse" )` - mice | |
| 30 | `r#pluralize( "buzz" )` - buzzes | |
| 31 | `r#pluralize( "bus" )` - busses | |
| 32 | ||
| 33 | # possessive.R | |
| 34 | ||
| 35 | This file defines a function that applies possessives to English words. | |
| 36 | ||
| 37 | ## Usage | |
| 38 | ||
| 39 | Example usages of the possessive function include: | |
| 40 | ||
| 41 | `r#pos( "Ross" )` - Ross' | |
| 42 | `r#pos( "Ruby" )` - Ruby's | |
| 43 | `r#pos( "Lois" )` - Lois' | |
| 44 | `r#pos( "my" )` - mine | |
| 45 | `r#pos( "Your" )` - Yours | |
| 46 | ||
| 1 | 47 |
| 1 | # R Functions | |
| 1 | # R Scripts | |
| 2 | 2 | |
| 3 | Import the files in this directory into the application, which include: | |
| 3 | These scripts illustrate how R can perform calculations using variables, to help automate repetitive tasks. Authors are free to write their own scripts. | |
| 4 | 4 | |
| 5 | * bootstrap.R | |
| 6 | * pluralize.R | |
| 7 | * possessive.R | |
| 8 | * conversion.R | |
| 9 | * csv.R | |
| 5 | ## Configuration | |
| 10 | 6 | |
| 11 | # bootstrap.R | |
| 7 | Configure the editor to use scripts as follows: | |
| 12 | 8 | |
| 13 | Copy the contents of this file into R script preferences, as shown in the | |
| 14 | following figure, then restart the application for the changes to take | |
| 15 | effect: | |
| 9 | 1. Copy the R scripts into same directory as your Markdown files. | |
| 10 | 1. Start the editor. | |
| 11 | 1. Click **Tools → R Script**. | |
| 12 | 1. Copy and paste the following: | |
| 16 | 13 | |
| 17 | #  | |
| 14 | assign( 'anchor', as.Date( '$date.anchor$', format='%Y-%m-%d' ), envir = .GlobalEnv ); | |
| 15 | setwd( '$application.r.working.directory$' ); | |
| 16 | source( 'pluralize.R' ); | |
| 17 | source( 'csv.R' ); | |
| 18 | source( 'conversion.R' ); | |
| 18 | 19 | |
| 19 | Setting the **Working Directory** allows the startup script to load files | |
| 20 | using a relative to said directory. | |
| 20 | 1. Click **File → New** to create a new file. | |
| 21 | 1. Click **File → Save As** to set a filename. | |
| 22 | 1. Set **Name** to: `variables.yaml` | |
| 23 | 1. Click **OK**. | |
| 24 | 1. Paste the following definitions: | |
| 21 | 25 | |
| 22 | # pluralize.R | |
| 26 | date: | |
| 27 | anchor: 2017-01-01 | |
| 28 | editor: | |
| 29 | examples: | |
| 30 | season: 2017-09-02 | |
| 31 | math: | |
| 32 | x: 1 | |
| 33 | y: $editor.examples.math.x$ + 1 | |
| 34 | z: $editor.examples.math.y$ + 1 | |
| 35 | name: | |
| 36 | given: Josephene | |
| 23 | 37 | |
| 24 | This file defines a function that implements most of Damian Conway's [An Algorithmic Approach to English Pluralization](http://blob.perl.org/tpc/1998/User_Applications/Algorithmic%20Approach%20Plurals/Algorithmic_Plurals.html). | |
| 38 | 1. Save and close the file. | |
| 39 | 1. Click **File → Open** | |
| 40 | 1. Change **Markdown Files** to **Definition Files**. | |
| 41 | 1. Select `variables.yaml`. | |
| 42 | 1. Click **Open**. | |
| 25 | 43 | |
| 26 | ## Usage | |
| 44 | R functionality is configured. | |
| 27 | 45 | |
| 28 | Example usages of the pluralize function include: | |
| 46 | ## Definitions | |
| 29 | 47 | |
| 30 | `r#pluralize( "mouse" )` - mice | |
| 31 | `r#pluralize( "buzz" )` - buzzes | |
| 32 | `r#pluralize( "bus" )` - busses | |
| 48 | The variables definitions within `variables.yaml` are available to R using the R syntax. An additional variable, `application.r.working.directory` is added to the list of variables. The value is set to the working directory of the file being edited. Hover the mouse cursor over the file tab in the editor to see the full path to the file. | |
| 33 | 49 | |
| 34 | # possessive.R | |
| 50 | ## Examples | |
| 35 | 51 | |
| 36 | This file defines a function that applies possessives to English words. | |
| 52 | This section demonstrates how to use the R functions when editing. Complete the following steps to begin: | |
| 37 | 53 | |
| 38 | ## Usage | |
| 54 | 1. Click **File → New** to create a new file. | |
| 55 | 1. Click **File → Save As** to set a filename. | |
| 56 | 1. Set **Name** to: `example.Rmd` | |
| 57 | 1. Click **OK**. | |
| 39 | 58 | |
| 40 | Example usages of the possessive function include: | |
| 59 | The examples are ready for use within the editor. | |
| 41 | 60 | |
| 42 | `r#pos( "Ross" )` - Ross' | |
| 43 | `r#pos( "Ruby" )` - Ruby's | |
| 44 | `r#pos( "Lois" )` - Lois' | |
| 45 | `r#pos( "my" )` - mine | |
| 46 | `r#pos( "Your" )` - Yours | |
| 61 | ### Arithmetic | |
| 62 | ||
| 63 | Type the following to perform a simple calculation: | |
| 64 | ||
| 65 | `r# 1+1` | |
| 66 | ||
| 67 | The preview pane shows `2.0`. | |
| 68 | ||
| 69 | ### Functions | |
| 70 | ||
| 71 | Call the [format](https://stat.ethz.ch/R-manual/R-devel/library/base/html/format.html) function to truncate unwanted decimal places as follows: | |
| 72 | ||
| 73 | `r# format(1+1,digits=1)` | |
| 74 | ||
| 75 | The preview pane shows `2`. | |
| 76 | ||
| 77 | ### Pluralize | |
| 78 | ||
| 79 | Many English words can be pluralized as follows: | |
| 80 | ||
| 81 | `r# pl('wolf',2)` | |
| 82 | ||
| 83 | The preview pane shows `wolves`. The `pluralize.R` file contains a partial implementation of Damian Conway's algorithmic approach to English pluralization. | |
| 84 | ||
| 85 | ### Chicago Manual of Style | |
| 86 | ||
| 87 | Apply the Chicago Manual of Style for words less than one-hundred as follows: | |
| 88 | ||
| 89 | `r# cms(1)` `r# cms(99)` `r# cms(101)` | |
| 90 | ||
| 91 | The preview pane shows numbers written out as `one` and `ninety-nine`, followed by the digits 101. | |
| 92 | ||
| 93 | ### Data Import | |
| 94 | ||
| 95 | Import and display information from a CSV file as follows: | |
| 96 | ||
| 97 | 1. Click **File → New** to create a new file. | |
| 98 | 1. Click **File → Save As** to rename the file. | |
| 99 | 1. Set the filename to: `data.csv` | |
| 100 | 1. Paste the following into `data.csv`: | |
| 101 | ||
| 102 | Animal,Quantity,Country | |
| 103 | Aardwolf,1,Africa | |
| 104 | Keel-billed toucan,1,Belize | |
| 105 | Beaver,2,Canada | |
| 106 | Mute swan,3,Denmark | |
| 107 | Lion,5,Ethiopia | |
| 108 | Brown bear,8,Finland | |
| 109 | Dolphin,13,Greece | |
| 110 | Turul,21,Hungary | |
| 111 | Gyrfalcon,34,Iceland | |
| 112 | Red-billed streamertail,55,Jamaica | |
| 113 | ||
| 114 | 1. Click the `example.Rmd` tab. | |
| 115 | 1. Type the following: | |
| 116 | ||
| 117 | `r# csv2md('data.csv',total=F)` | |
| 118 | ||
| 119 | 1. Type the following to calculate a total for all numeric columns: | |
| 120 | ||
| 121 | `r# csv2md('data.csv')` | |
| 122 | ||
| 123 | This imports the data from an external file and formats the information into a table, automatically. Update the data as follows: | |
| 124 | ||
| 125 | 1. Click the `data.csv` tab to edit the data. | |
| 126 | 1. Change the data by adding a new row. | |
| 127 | 1. Save the file. | |
| 128 | 1. Click the `example.Rmd` tab. | |
| 129 | ||
| 130 | The preview pane shows the revised contents. | |
| 131 | ||
| 132 | ### Elapsed Time | |
| 133 | ||
| 134 | The duration of a timeline, given in numbers of days, can be computed into English as follows: | |
| 135 | ||
| 136 | `r# elapsed(1,1)` | |
| 137 | ||
| 138 | The preview pane shows `same day`. Change the expression to: | |
| 139 | ||
| 140 | `r# elapsed(1,2)` | |
| 141 | ||
| 142 | The preview pane shows `one day`. Change the expression to: | |
| 143 | ||
| 144 | `r# elapsed(1,112358)` | |
| 145 | ||
| 146 | The preview pane shows `307 years, seven months, and sixteen days`, combined using the Chicago Manual of Style, the pluralization function, and a [serial comma](https://www.behance.net/gallery/19417363/The-Oxford-Comma). | |
| 147 | ||
| 148 | ### Variable Syntax | |
| 149 | ||
| 150 | The syntax for a variable changes when using an R Markdown file (denoted by the `.Rmd` filename extension), as opposed to a regular Markdown file (`.md`). Return to the example file and type the following: | |
| 151 | ||
| 152 | `r# v$date$anchor` | |
| 153 | ||
| 154 | The preview pane shows the date. | |
| 155 | ||
| 156 | ### Autocomplete | |
| 157 | ||
| 158 | Automatically insert a variable reference into the text as follows: | |
| 159 | ||
| 160 | 1. Type: `Jos` | |
| 161 | * Note the capital letter, matches are case sensitive. | |
| 162 | 1. Hold down the `Control` key. | |
| 163 | 1. Tap the `Spacebar` | |
| 164 | ||
| 165 | The editor shows: | |
| 166 | ||
| 167 | `r#x( v$editor$examples$name$given )` | |
| 168 | ||
| 169 | The preview pane shows: | |
| 170 | ||
| 171 | Josephine | |
| 172 | ||
| 173 | Here, the `x` function evaluates its parameter as an expression. This allows variables to include expressions in their definition. | |
| 174 | ||
| 175 | ### Variable Definition Expressions | |
| 176 | ||
| 177 | Definition file variables are have the ability to reference other definitions. Try the following: | |
| 178 | ||
| 179 | x = `r#x( v$editor$examples$math$x )`; | |
| 180 | y = `r#x( v$editor$examples$math$y )`; | |
| 181 | z = `r#x( v$editor$examples$math$z )` | |
| 182 | ||
| 183 | The preview pane shows: | |
| 184 | ||
| 185 | x = 1.0; y = 2.0; z = 3.0 | |
| 186 | ||
| 187 | ### Case | |
| 188 | ||
| 189 | Ensure words begin with a lowercase letter as follows: | |
| 190 | ||
| 191 | `r#lc( v$editor$examples$name$given )` | |
| 192 | ||
| 193 | The preview pane shows: | |
| 194 | ||
| 195 | josephine | |
| 196 | ||
| 197 | Similarly, ensure an uppercase letter as follows: | |
| 198 | ||
| 199 | `r#uc( 'hello, world!' )` | |
| 200 | ||
| 201 | The preview pane shows: | |
| 202 | ||
| 203 | Hello, world! | |
| 204 | ||
| 205 | ### Month | |
| 206 | ||
| 207 | Display the month name given a month number as follows: | |
| 208 | ||
| 209 | `r# month( 1 )` | |
| 210 | ||
| 211 | The preview pane shows: | |
| 212 | ||
| 213 | January | |
| 214 | ||
| 215 | ## Summary | |
| 47 | 216 | |
| 217 | Authors can inline R statements into documents, directly, so long as those statements generate text. Plots, graphs, and images must be referenced as external image files or URLs. | |
| 48 | 218 |
| 82 | 82 | } |
| 83 | 83 | else { |
| 84 | s <- 'their' | |
| 84 | s <- 'them' | |
| 85 | 85 | } |
| 86 | 86 | |
| ... | ||
| 102 | 102 | } |
| 103 | 103 | |
| 104 | pro.pos <- function( s ) { | |
| 104 | pro.adj <- function( s ) { | |
| 105 | 105 | if( s == 'm' ) { |
| 106 | s = 'his' | |
| 106 | s <- 'his' | |
| 107 | 107 | } |
| 108 | 108 | else if( s == 'f' ) { |
| 109 | 109 | s <- 'her' |
| 110 | } | |
| 111 | else { | |
| 112 | s <- 'their' | |
| 113 | } | |
| 114 | ||
| 115 | s | |
| 116 | } | |
| 117 | ||
| 118 | pro.pos <- function( s ) { | |
| 119 | if( s == 'm' ) { | |
| 120 | s <- 'his' | |
| 121 | } | |
| 122 | else if( s == 'f' ) { | |
| 123 | s <- 'hers' | |
| 110 | 124 | } |
| 111 | 125 | else { |
| ... | ||
| 118 | 132 | pro.noun <- function( s ) { |
| 119 | 133 | if( s == 'm' ) { |
| 120 | s = 'man' | |
| 134 | s <- 'man' | |
| 121 | 135 | } |
| 122 | 136 | else if( s == 'f' ) { |
| 51 | 51 | dependencies { |
| 52 | 52 | def v_junit = '5.8.2' |
| 53 | def v_flexmark = '0.62.2' | |
| 54 | def v_jackson = '2.13.1' | |
| 53 | def v_flexmark = '0.64.0' | |
| 54 | def v_jackson = '2.13.2' | |
| 55 | 55 | def v_batik = '1.14' |
| 56 | 56 | def v_wheatsheaf = '2.0.1' |
| 57 | 57 | |
| 58 | 58 | // JavaFX |
| 59 | 59 | // TODO: Reinstate when JDK 17-compatible release is published |
| 60 | 60 | //implementation 'org.controlsfx:controlsfx:11.1.0' |
| 61 | implementation 'org.fxmisc.richtext:richtextfx:0.10.7' | |
| 62 | implementation 'org.fxmisc.flowless:flowless:0.6.7' | |
| 61 | implementation 'org.fxmisc.richtext:richtextfx:0.10.9' | |
| 62 | implementation 'org.fxmisc.flowless:flowless:0.6.9' | |
| 63 | 63 | implementation 'org.fxmisc.wellbehaved:wellbehavedfx:0.3.3' |
| 64 | 64 | implementation 'com.miglayout:miglayout-javafx:11.0' |
| 65 | implementation 'com.dlsc.preferencesfx:preferencesfx-core:11.8.0' | |
| 65 | implementation 'com.dlsc.preferencesfx:preferencesfx-core:11.9.0' | |
| 66 | 66 | |
| 67 | 67 | // Pure JavaFX File Chooser |
| ... | ||
| 84 | 84 | implementation "com.fasterxml.jackson.core:jackson-annotations:${v_jackson}" |
| 85 | 85 | implementation "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:${v_jackson}" |
| 86 | implementation 'org.yaml:snakeyaml:1.29' | |
| 86 | implementation 'org.yaml:snakeyaml:1.30' | |
| 87 | 87 | |
| 88 | 88 | // XML |
| ... | ||
| 126 | 126 | |
| 127 | 127 | // Command-line parsing |
| 128 | implementation 'info.picocli:picocli:4.6.2' | |
| 128 | implementation 'info.picocli:picocli:4.6.3' | |
| 129 | 129 | |
| 130 | 130 | // Spelling, TeX, Docking, KeenQuotes |
| ... | ||
| 141 | 141 | testImplementation "org.testfx:testfx-junit5:4.0.16-alpha" |
| 142 | 142 | testImplementation "org.junit.jupiter:junit-jupiter-api:${v_junit}" |
| 143 | testImplementation "org.junit.jupiter:junit-jupiter-params:${v_junit}" | |
| 143 | 144 | testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine' |
| 144 | 145 | } |
| 23 | 23 | |
| 24 | 24 | import static com.keenwrite.events.StatusEvent.clue; |
| 25 | import static java.nio.charset.StandardCharsets.UTF_16; | |
| 25 | 26 | import static java.nio.charset.StandardCharsets.UTF_8; |
| 26 | 27 | import static javax.xml.transform.OutputKeys.*; |
| ... | ||
| 61 | 62 | sTransformer = TransformerFactory.newInstance().newTransformer(); |
| 62 | 63 | |
| 64 | // Ensure Unicode characters (emojis) are encoded correctly. | |
| 65 | sTransformer.setOutputProperty( ENCODING, UTF_16.toString() ); | |
| 63 | 66 | sTransformer.setOutputProperty( OMIT_XML_DECLARATION, "yes" ); |
| 64 | 67 | sTransformer.setOutputProperty( METHOD, "xml" ); |
| 65 | sTransformer.setOutputProperty( ENCODING, UTF_8.toString() ); | |
| 66 | 68 | sTransformer.setOutputProperty( INDENT, "yes" ); |
| 67 | 69 | sTransformer.setOutputProperty( INDENT_AMOUNT, "2" ); |
| 50 | 50 | |
| 51 | 51 | /** |
| 52 | * Answers whether this file type matches the given string, case insensitive | |
| 52 | * Answers whether this file type matches the given string, case-insensitive | |
| 53 | 53 | * comparison. |
| 54 | 54 | * |
| 88 | 88 | * Standard audio types. |
| 89 | 89 | */ |
| 90 | AUDIO_BASIC( AUDIO, "basic" ), | |
| 90 | AUDIO_SIMPLE( AUDIO, "basic" ), | |
| 91 | 91 | AUDIO_MP3( AUDIO, "mp3" ), |
| 92 | 92 | AUDIO_WAV( AUDIO, "x-wav" ), |
| 20 | 20 | |
| 21 | 21 | MEDIA_AUDIO_MP3( AUDIO_MP3 ), |
| 22 | MEDIA_AUDIO_BASIC( AUDIO_BASIC, of( "au" ) ), | |
| 22 | MEDIA_AUDIO_SIMPLE( AUDIO_SIMPLE, of( "au" ) ), | |
| 23 | 23 | MEDIA_AUDIO_WAV( AUDIO_WAV, of( "wav" ) ), |
| 24 | 24 | |
| ... | ||
| 47 | 47 | MEDIA_TEXT_R_MARKDOWN( TEXT_R_MARKDOWN, of( "Rmd" ) ), |
| 48 | 48 | MEDIA_TEXT_PROPERTIES( TEXT_PROPERTIES, of( "properties" ) ), |
| 49 | MEDIA_TEXT_XHTML( TEXT_XHTML, of( "html", "xhtml" ) ), | |
| 49 | MEDIA_TEXT_XHTML( TEXT_XHTML, of( "htm", "html", "xhtml" ) ), | |
| 50 | 50 | MEDIA_TEXT_XML( TEXT_XML ), |
| 51 | 51 | MEDIA_TEXT_YAML( TEXT_YAML, of( "yaml", "yml" ) ), |
| ... | ||
| 73 | 73 | * extension. The extension must not contain a period. |
| 74 | 74 | * |
| 75 | * @param extension File name extension, case insensitive, {@code null}-safe. | |
| 75 | * @param extension File name extension, case-insensitive, {@code null}-safe. | |
| 76 | 76 | * @return The associated {@link MediaType} as defined by IANA. |
| 77 | 77 | */ |
| 60 | 60 | FORMAT.put( ints( 0x23, 0x64, 0x65, 0x66 ), IMAGE_X_BITMAP ); |
| 61 | 61 | FORMAT.put( ints( 0x21, 0x20, 0x58, 0x50, 0x4D, 0x32 ), IMAGE_X_PIXMAP ); |
| 62 | FORMAT.put( ints( 0x2E, 0x73, 0x6E, 0x64 ), AUDIO_BASIC ); | |
| 63 | FORMAT.put( ints( 0x64, 0x6E, 0x73, 0x2E ), AUDIO_BASIC ); | |
| 62 | FORMAT.put( ints( 0x2E, 0x73, 0x6E, 0x64 ), AUDIO_SIMPLE ); | |
| 63 | FORMAT.put( ints( 0x64, 0x6E, 0x73, 0x2E ), AUDIO_SIMPLE ); | |
| 64 | 64 | FORMAT.put( ints( 0x52, 0x49, 0x46, 0x46 ), AUDIO_WAV ); |
| 65 | 65 | FORMAT.put( ints( 0x50, 0x4B ), APP_ZIP ); |
| 1 | # R Scripts | |
| 2 | ||
| 3 | These R scripts illustrate how R can be used within an application to perform calculations using variables. Authors are free to write their own scripts, of course. These scripts serve as an example of how to automate certain tasks while writing. | |
| 4 | ||
| 5 | ## Configuration | |
| 6 | ||
| 7 | Configure the editor to use the R scripts as follows: | |
| 8 | ||
| 9 | 1. Copy the R scripts into same directory as your Markdown files. | |
| 10 | 1. Start the editor. | |
| 11 | 1. Click **Tools → R Script**. | |
| 12 | 1. Copy and paste the following: | |
| 13 | ||
| 14 | assign( 'anchor', as.Date( '$date.anchor$', format='%Y-%m-%d' ), envir = .GlobalEnv ); | |
| 15 | setwd( '$application.r.working.directory$' ); | |
| 16 | source( 'pluralize.R' ); | |
| 17 | source( 'csv.R' ); | |
| 18 | source( 'conversion.R' ); | |
| 19 | ||
| 20 | 1. Click **File → New** to create a new file. | |
| 21 | 1. Click **File → Save As** to set a filename. | |
| 22 | 1. Set **Name** to: `variables.yaml` | |
| 23 | 1. Click **OK**. | |
| 24 | 1. Paste the following definitions: | |
| 25 | ||
| 26 | date: | |
| 27 | anchor: 2017-01-01 | |
| 28 | editor: | |
| 29 | examples: | |
| 30 | season: 2017-09-02 | |
| 31 | math: | |
| 32 | x: 1 | |
| 33 | y: $editor.examples.math.x$ + 1 | |
| 34 | z: $editor.examples.math.y$ + 1 | |
| 35 | name: | |
| 36 | given: Josephene | |
| 37 | ||
| 38 | 1. Save and close the file. | |
| 39 | 1. Click **File → Open** | |
| 40 | 1. Change **Markdown Files** to **Definition Files**. | |
| 41 | 1. Select `variables.yaml`. | |
| 42 | 1. Click **Open**. | |
| 43 | ||
| 44 | R functionality is configured. | |
| 45 | ||
| 46 | ## Definitions | |
| 47 | ||
| 48 | The variables definitions within `variables.yaml` are available to R using the R syntax. An additional variable, `application.r.working.directory` is added to the list of variables. The value is set to the working directory of the file being edited. Hover the mouse cursor over the file tab in the editor to see the full path to the file. | |
| 49 | ||
| 50 | ## Examples | |
| 51 | ||
| 52 | This section demonstrates how to use the R functions when editing. Complete the following steps to begin: | |
| 53 | ||
| 54 | 1. Click **File → New** to create a new file. | |
| 55 | 1. Click **File → Save As** to set a filename. | |
| 56 | 1. Set **Name** to: `example.Rmd` | |
| 57 | 1. Click **OK**. | |
| 58 | ||
| 59 | The examples are ready for use within the editor. | |
| 60 | ||
| 61 | ### Arithmetic | |
| 62 | ||
| 63 | Type the following to perform a simple calculation: | |
| 64 | ||
| 65 | `r# 1+1` | |
| 66 | ||
| 67 | The preview pane shows `2.0`. | |
| 68 | ||
| 69 | ### Functions | |
| 70 | ||
| 71 | Call the [format](https://stat.ethz.ch/R-manual/R-devel/library/base/html/format.html) function to truncate unwanted decimal places as follows: | |
| 72 | ||
| 73 | `r# format(1+1,digits=1)` | |
| 74 | ||
| 75 | The preview pane shows `2`. | |
| 76 | ||
| 77 | ### Pluralize | |
| 78 | ||
| 79 | Many English words can be pluralized as follows: | |
| 80 | ||
| 81 | `r# pl('wolf',2)` | |
| 82 | ||
| 83 | The preview pane shows `wolves`. The `pluralize.R` file contains a partial implementation of Damian Conway's algorithmic approach to English pluralization. | |
| 84 | ||
| 85 | ### Chicago Manual of Style | |
| 86 | ||
| 87 | Apply the Chicago Manual of Style for words less than one-hundred as follows: | |
| 88 | ||
| 89 | `r# cms(1)` `r# cms(99)` `r# cms(101)` | |
| 90 | ||
| 91 | The preview pane shows numbers written out as `one` and `ninety-nine`, followed by the digits 101. | |
| 92 | ||
| 93 | ### Data Import | |
| 94 | ||
| 95 | Import and display information from a CSV file as follows: | |
| 96 | ||
| 97 | 1. Click **File → New** to create a new file. | |
| 98 | 1. Click **File → Save As** to rename the file. | |
| 99 | 1. Set the filename to: `data.csv` | |
| 100 | 1. Paste the following into `data.csv`: | |
| 101 | ||
| 102 | Animal,Quantity,Country | |
| 103 | Aardwolf,1,Africa | |
| 104 | Keel-billed toucan,1,Belize | |
| 105 | Beaver,2,Canada | |
| 106 | Mute swan,3,Denmark | |
| 107 | Lion,5,Ethiopia | |
| 108 | Brown bear,8,Finland | |
| 109 | Dolphin,13,Greece | |
| 110 | Turul,21,Hungary | |
| 111 | Gyrfalcon,34,Iceland | |
| 112 | Red-billed streamertail,55,Jamaica | |
| 113 | ||
| 114 | 1. Click the `example.Rmd` tab. | |
| 115 | 1. Type the following: | |
| 116 | ||
| 117 | `r# csv2md('data.csv',total=F)` | |
| 118 | ||
| 119 | 1. Type the following to calculate a total for all numeric columns: | |
| 120 | ||
| 121 | `r# csv2md('data.csv')` | |
| 122 | ||
| 123 | This imports the data from an external file and formats the information into a table, automatically. Update the data as follows: | |
| 124 | ||
| 125 | 1. Click the `data.csv` tab to edit the data. | |
| 126 | 1. Change the data by adding a new row. | |
| 127 | 1. Save the file. | |
| 128 | 1. Click the `example.Rmd` tab. | |
| 129 | ||
| 130 | The preview pane shows the revised contents. | |
| 131 | ||
| 132 | ### Elapsed Time | |
| 133 | ||
| 134 | The duration of a timeline, given in numbers of days, can be computed into English as follows: | |
| 135 | ||
| 136 | `r# elapsed(1,1)` | |
| 137 | ||
| 138 | The preview pane shows `same day`. Change the expression to: | |
| 139 | ||
| 140 | `r# elapsed(1,2)` | |
| 141 | ||
| 142 | The preview pane shows `one day`. Change the expression to: | |
| 143 | ||
| 144 | `r# elapsed(1,112358)` | |
| 145 | ||
| 146 | The preview pane shows `307 years, seven months, and sixteen days`, combined using the Chicago Manual of Style, the pluralization function, and a [serial comma](https://www.behance.net/gallery/19417363/The-Oxford-Comma). | |
| 147 | ||
| 148 | ### Variable Syntax | |
| 149 | ||
| 150 | The syntax for a variable changes when using an R Markdown file (denoted by the `.Rmd` filename extension), as opposed to a regular Markdown file (`.md`). Return to the example file and type the following: | |
| 151 | ||
| 152 | `r# v$date$anchor` | |
| 153 | ||
| 154 | The preview pane shows the date. | |
| 155 | ||
| 156 | ### Autocomplete | |
| 157 | ||
| 158 | Automatically insert a variable reference into the text as follows: | |
| 159 | ||
| 160 | 1. Type: `Jos` | |
| 161 | * Note the capital letter, matches are case sensitive. | |
| 162 | 1. Hold down the `Control` key. | |
| 163 | 1. Tap the `Spacebar` | |
| 164 | ||
| 165 | The editor shows: | |
| 166 | ||
| 167 | `r#x( v$editor$examples$name$given )` | |
| 168 | ||
| 169 | The preview pane shows: | |
| 170 | ||
| 171 | Josephine | |
| 172 | ||
| 173 | Here, the `x` function evaluates its parameter as an expression. This allows variables to include expressions in their definition. | |
| 174 | ||
| 175 | ### Variable Definition Expressions | |
| 176 | ||
| 177 | Definition file variables are have the ability to reference other definitions. Try the following: | |
| 178 | ||
| 179 | x = `r#x( v$editor$examples$math$x )`; | |
| 180 | y = `r#x( v$editor$examples$math$y )`; | |
| 181 | z = `r#x( v$editor$examples$math$z )` | |
| 182 | ||
| 183 | The preview pane shows: | |
| 184 | ||
| 185 | x = 1.0; y = 2.0; z = 3.0 | |
| 186 | ||
| 187 | ### Case | |
| 188 | ||
| 189 | Ensure words begin with a lowercase letter as follows: | |
| 190 | ||
| 191 | `r#lc( v$editor$examples$name$given )` | |
| 192 | ||
| 193 | The preview pane shows: | |
| 194 | ||
| 195 | josephine | |
| 196 | ||
| 197 | Similarly, ensure an uppercase letter as follows: | |
| 198 | ||
| 199 | `r#uc( 'hello, world!' )` | |
| 200 | ||
| 201 | The preview pane shows: | |
| 202 | ||
| 203 | Hello, world! | |
| 204 | ||
| 205 | ### Month | |
| 206 | ||
| 207 | Display the month name given a month number as follows: | |
| 208 | ||
| 209 | `r# month( 1 )` | |
| 210 | ||
| 211 | The preview pane shows: | |
| 212 | ||
| 213 | January | |
| 214 | ||
| 215 | ## Summary | |
| 216 | ||
| 217 | Authors can inline R statements into documents, directly, so long as those statements generate text. Plots, graphs, and images must be referenced as external image files or URLs. | |
| 218 | 1 |
| 1 | # ######################################################################## | |
| 2 | # | |
| 3 | # Substitute R expressions in a document with their evaluated value. The | |
| 4 | # anchor variable must be set for functions that use relative dates. | |
| 5 | # | |
| 6 | # ######################################################################## | |
| 7 | ||
| 8 | # Evaluates an expression; writes s if there is no expression. | |
| 9 | x <- function( s ) { | |
| 10 | return( | |
| 11 | tryCatch({ | |
| 12 | r = eval( parse( text=s ) ) | |
| 13 | ||
| 14 | # If the result isn't primitive, then it was probably parsed into | |
| 15 | # an unprintable object (e.g., "gray" becomes a colour). In those | |
| 16 | # cases, return the original text string. Otherwise, an atomic | |
| 17 | # value means a primitive type (string, integer, etc.) that can be | |
| 18 | # written directly into the document. | |
| 19 | # | |
| 20 | # See: http://stackoverflow.com/a/19501276/59087 | |
| 21 | if( is.atomic( r ) ) { | |
| 22 | r | |
| 23 | } | |
| 24 | else { | |
| 25 | s | |
| 26 | } | |
| 27 | }, | |
| 28 | warning = function( w ) { | |
| 29 | s | |
| 30 | }, | |
| 31 | error = function( e ) { | |
| 32 | s | |
| 33 | }) | |
| 34 | ) | |
| 35 | } | |
| 36 | ||
| 37 | # Returns a date offset by a given number of days, relative to the given | |
| 38 | # date (d). This does not use the anchor, but is used to get the anchor's | |
| 39 | # value as a date. | |
| 40 | when <- function( d, n = 0, format = "%Y-%m-%d" ) { | |
| 41 | as.Date( d, format = format ) + x( n ) | |
| 42 | } | |
| 43 | ||
| 44 | # Full date (s) offset by an optional number of days before or after. | |
| 45 | # This will remove leading zeros (applying leading spaces instead, which | |
| 46 | # are ignored by any worthwhile typesetting engine). | |
| 47 | annal <- function( days = 0, format = "%Y-%m-%d", oformat = "%B %d, %Y" ) { | |
| 48 | format( when( anchor, days ), format = oformat ) | |
| 49 | } | |
| 50 | ||
| 51 | # Extracts the year from a date string. | |
| 52 | year <- function( days = 0, format = "%Y-%m-%d" ) { | |
| 53 | annal( days, format, "%Y" ) | |
| 54 | } | |
| 55 | ||
| 56 | # Day of the week (in days since the anchor date). | |
| 57 | weekday <- function( n ) { | |
| 58 | weekdays( when( anchor, n ) ) | |
| 59 | } | |
| 60 | ||
| 61 | # String concatenate function alias because paste0 is a terrible name. | |
| 62 | concat <- paste0 | |
| 63 | ||
| 64 | # Translates a number from digits to words using Chicago Manual of Style. | |
| 65 | # This does not translate numbers greater than one hundred. If ordinal | |
| 66 | # is TRUE, this will return the ordinal name. This will not produce ordinals | |
| 67 | # for numbers greater than 100 | |
| 68 | cms <- function( n, ordinal = FALSE ) { | |
| 69 | n <- x( n ) | |
| 70 | ||
| 71 | # We're done here. | |
| 72 | if( n == 0 ) { | |
| 73 | if( ordinal ) { | |
| 74 | return( "zeroth" ) | |
| 75 | } | |
| 76 | ||
| 77 | return( "zero" ) | |
| 78 | } | |
| 79 | ||
| 80 | # Concatenate this a little later. | |
| 81 | if( n < 0 ) { | |
| 82 | result = "negative " | |
| 83 | n = abs( n ) | |
| 84 | } | |
| 85 | ||
| 86 | # Do not spell out numbers greater than one hundred. | |
| 87 | if( n > 100 ) { | |
| 88 | # Comma-separated numbers. | |
| 89 | return( format( n, big.mark=",", trim=TRUE, scientific=FALSE ) ) | |
| 90 | } | |
| 91 | ||
| 92 | # Don't go beyond 100. | |
| 93 | if( n == 100 ) { | |
| 94 | if( ordinal ) { | |
| 95 | return( "one hundredth" ) | |
| 96 | } | |
| 97 | ||
| 98 | return( "one hundred" ) | |
| 99 | } | |
| 100 | ||
| 101 | # Samuel Langhorne Clemens noted English has too many exceptions. | |
| 102 | small = c( | |
| 103 | "one", "two", "three", "four", "five", | |
| 104 | "six", "seven", "eight", "nine", "ten", | |
| 105 | "eleven", "twelve", "thirteen", "fourteen", "fifteen", | |
| 106 | "sixteen", "seventeen", "eighteen", "nineteen" | |
| 107 | ) | |
| 108 | ||
| 109 | ord_small = c( | |
| 110 | "first", "second", "third", "fourth", "fifth", | |
| 111 | "sixth", "seventh", "eighth", "ninth", "tenth", | |
| 112 | "eleventh", "twelfth", "thirteenth", "fourteenth", "fifteenth", | |
| 113 | "sixteenth", "seventeenth", "eighteenth", "nineteenth", "twentieth" | |
| 114 | ) | |
| 115 | ||
| 116 | # After this, the number (n) is between 20 and 99. | |
| 117 | if( n < 20 ) { | |
| 118 | if( ordinal ) { | |
| 119 | return( .subset( ord_small, n %% 100 ) ) | |
| 120 | } | |
| 121 | ||
| 122 | return( .subset( small, n %% 100 ) ) | |
| 123 | } | |
| 124 | ||
| 125 | tens = c( "", | |
| 126 | "twenty", "thirty", "forty", "fifty", | |
| 127 | "sixty", "seventy", "eighty", "ninety" | |
| 128 | ) | |
| 129 | ||
| 130 | ord_tens = c( "", | |
| 131 | "twentieth", "thirtieth", "fortieth", "fiftieth", | |
| 132 | "sixtieth", "seventieth", "eightieth", "ninetieth" | |
| 133 | ) | |
| 134 | ||
| 135 | ones_index = n %% 10 | |
| 136 | n = n %/% 10 | |
| 137 | ||
| 138 | # No number in the ones column, so the number must be a multiple of ten. | |
| 139 | if( ones_index == 0 ) { | |
| 140 | if( ordinal ) { | |
| 141 | return( .subset( ord_tens, n ) ) | |
| 142 | } | |
| 143 | ||
| 144 | return( .subset( tens, n ) ) | |
| 145 | } | |
| 146 | ||
| 147 | # Find the value from the ones column. | |
| 148 | if( ordinal ) { | |
| 149 | unit_1 = .subset( ord_small, ones_index ) | |
| 150 | } | |
| 151 | else { | |
| 152 | unit_1 = .subset( small, ones_index ) | |
| 153 | } | |
| 154 | ||
| 155 | # Find the tens column. | |
| 156 | unit_10 = .subset( tens, n ) | |
| 157 | ||
| 158 | # Hyphenate the tens and the ones together. | |
| 159 | concat( unit_10, concat( "-", unit_1 ) ) | |
| 160 | } | |
| 161 | ||
| 162 | # Returns a human-readable string that provides the elapsed time between | |
| 163 | # two numbers in terms of years, months, and days. If any unit value is zero, | |
| 164 | # the unit is not included. The words (year, month, day) are pluralized | |
| 165 | # according to English grammar. The numbers are written out according to | |
| 166 | # Chicago Manual of Style. This applies the serial comma. | |
| 167 | # | |
| 168 | # Both numbers are offsets relative to the anchor date. | |
| 169 | # | |
| 170 | # If all unit values are zero, this returns s ("same day" by default). | |
| 171 | # | |
| 172 | # If the start date (began) is greater than end date (ended), the dates are | |
| 173 | # swapped before calculations are performed. This allows any two dates | |
| 174 | # to be compared and positive unit values are always returned. | |
| 175 | # | |
| 176 | elapsed <- function( began, ended, s = "same day" ) { | |
| 177 | began = when( anchor, began ) | |
| 178 | ended = when( anchor, ended ) | |
| 179 | ||
| 180 | # Swap the dates if the end date comes before the start date. | |
| 181 | if( as.integer( ended - began ) < 0 ) { | |
| 182 | tempd = began | |
| 183 | began = ended | |
| 184 | ended = tempd | |
| 185 | } | |
| 186 | ||
| 187 | # Calculate number of elapsed years. | |
| 188 | years = length( seq( from = began, to = ended, by = 'year' ) ) - 1 | |
| 189 | ||
| 190 | # Move the start date up by the number of elapsed years. | |
| 191 | if( years > 0 ) { | |
| 192 | began = seq( began, length = 2, by = concat( years, " years" ) )[2] | |
| 193 | years = pl.numeric( "year", years ) | |
| 194 | } | |
| 195 | else { | |
| 196 | # Zero years. | |
| 197 | years = "" | |
| 198 | } | |
| 199 | ||
| 200 | # Calculate number of elapsed months, excluding years. | |
| 201 | months = length( seq( from = began, to = ended, by = 'month' ) ) - 1 | |
| 202 | ||
| 203 | # Move the start date up by the number of elapsed months | |
| 204 | if( months > 0 ) { | |
| 205 | began = seq( began, length = 2, by = concat( months, " months" ) )[2] | |
| 206 | months = pl.numeric( "month", months ) | |
| 207 | } | |
| 208 | else { | |
| 209 | # Zero months | |
| 210 | months = "" | |
| 211 | } | |
| 212 | ||
| 213 | # Calculate number of elapsed days, excluding months and years. | |
| 214 | days = length( seq( from = began, to = ended, by = 'day' ) ) - 1 | |
| 215 | ||
| 216 | if( days > 0 ) { | |
| 217 | days = pl.numeric( "day", days ) | |
| 218 | } | |
| 219 | else { | |
| 220 | # Zero days | |
| 221 | days = "" | |
| 222 | } | |
| 223 | ||
| 224 | if( years <= 0 && months <= 0 && days <= 0 ) { | |
| 225 | return( s ) | |
| 226 | } | |
| 227 | ||
| 228 | # Put them all in a vector, then remove the empty values. | |
| 229 | s <- c( years, months, days ) | |
| 230 | s <- s[ s != "" ] | |
| 231 | ||
| 232 | r <- paste( s, collapse = ", " ) | |
| 233 | ||
| 234 | # If all three items are present, replace the last comma with ", and". | |
| 235 | if( length( s ) > 2 ) { | |
| 236 | return( gsub( "(.*),", "\\1, and", r ) ) | |
| 237 | } | |
| 238 | ||
| 239 | # Does nothing if no commas are present. | |
| 240 | gsub( "(.*),", "\\1 and", r ) | |
| 241 | } | |
| 242 | ||
| 243 | # Returns the number (n) in English followed by the plural or singular | |
| 244 | # form of the given string (s; resumably a noun), if applicable, according | |
| 245 | # to English grammar. That is, pl.numeric( "wolf", 5 ) will return | |
| 246 | # "five wolves". | |
| 247 | pl.numeric <- function( s, n ) { | |
| 248 | concat( cms( n ), concat( " ", pluralize( s, n ) ) ) | |
| 249 | } | |
| 250 | ||
| 251 | # Name of the season, starting with an capital letter. | |
| 252 | season <- function( n, format = "%Y-%m-%d" ) { | |
| 253 | WS <- as.Date("2016-12-15", "%Y-%m-%d") # Winter Solstice | |
| 254 | SE <- as.Date("2016-03-15", "%Y-%m-%d") # Spring Equinox | |
| 255 | SS <- as.Date("2016-06-15", "%Y-%m-%d") # Summer Solstice | |
| 256 | AE <- as.Date("2016-09-15", "%Y-%m-%d") # Autumn Equinox | |
| 257 | ||
| 258 | d <- when( anchor, n ) | |
| 259 | d <- as.Date( strftime( d, format="2016-%m-%d" ) ) | |
| 260 | ||
| 261 | ifelse( d >= WS | d < SE, "Winter", | |
| 262 | ifelse( d >= SE & d < SS, "Spring", | |
| 263 | ifelse( d >= SS & d < AE, "Summer", "Autumn" ) | |
| 264 | ) | |
| 265 | ) | |
| 266 | } | |
| 267 | ||
| 268 | # Converts the first letter in a string to lowercase | |
| 269 | lc <- function( s ) { | |
| 270 | concat( tolower( substr( s, 1, 1 ) ), substr( s, 2, nchar( s ) ) ) | |
| 271 | } | |
| 272 | ||
| 273 | # Converts the first letter in a string to uppercase | |
| 274 | uc <- function( s ) { | |
| 275 | concat( toupper( substr( s, 1, 1 ) ), substr( s, 2, nchar( s ) ) ) | |
| 276 | } | |
| 277 | ||
| 278 | # Returns the number of days between the given dates. | |
| 279 | days <- function( d1, d2, format = "%Y-%m-%d" ) { | |
| 280 | dates = c( d1, d2 ) | |
| 281 | dt = strptime( dates, format = format ) | |
| 282 | as.integer( difftime( dates[2], dates[1], units = "days" ) ) | |
| 283 | } | |
| 284 | ||
| 285 | # Returns the number of years elapsed. | |
| 286 | years <- function( began, ended ) { | |
| 287 | began = when( anchor, began ) | |
| 288 | ended = when( anchor, ended ) | |
| 289 | ||
| 290 | # Swap the dates if the end date comes before the start date. | |
| 291 | if( as.integer( ended - began ) < 0 ) { | |
| 292 | tempd = began | |
| 293 | began = ended | |
| 294 | ended = tempd | |
| 295 | } | |
| 296 | ||
| 297 | # Calculate number of elapsed years. | |
| 298 | length( seq( from = began, to = ended, by = 'year' ) ) - 1 | |
| 299 | } | |
| 300 | ||
| 301 | # Full name of the month, starting with a capital letter. | |
| 302 | month <- function( n ) { | |
| 303 | # Faster than month.name[ x( n ) ] | |
| 304 | .subset( month.name, x( n ) ) | |
| 305 | } | |
| 306 | ||
| 307 | money <- function( n ) { | |
| 308 | formatC( x( n ), format="d" ) | |
| 309 | } | |
| 310 | 1 |
| 1 | # ###################################################################### | |
| 2 | # | |
| 3 | # Copyright 2016, White Magic Software, Ltd. | |
| 4 | # | |
| 5 | # Permission is hereby granted, free of charge, to any person obtaining | |
| 6 | # a copy of this software and associated documentation files (the | |
| 7 | # "Software"), to deal in the Software without restriction, including | |
| 8 | # without limitation the rights to use, copy, modify, merge, publish, | |
| 9 | # distribute, sublicense, and/or sell copies of the Software, and to | |
| 10 | # permit persons to whom the Software is furnished to do so, subject to | |
| 11 | # the following conditions: | |
| 12 | # | |
| 13 | # The above copyright notice and this permission notice shall be | |
| 14 | # included in all copies or substantial portions of the Software. | |
| 15 | # | |
| 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |
| 17 | # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
| 18 | # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |
| 19 | # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | |
| 20 | # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | |
| 21 | # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |
| 22 | # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |
| 23 | # | |
| 24 | # ###################################################################### | |
| 25 | ||
| 26 | # ###################################################################### | |
| 27 | # | |
| 28 | # Converts CSV to Markdown. | |
| 29 | # | |
| 30 | # ###################################################################### | |
| 31 | ||
| 32 | # Reads a CSV file and converts the contents to a Markdown table. The | |
| 33 | # file must be in the working directory as specified by setwd. | |
| 34 | # | |
| 35 | # @param f The filename to convert. | |
| 36 | # @param decimals Rounded decimal places (default 1). | |
| 37 | # @param totals Include total sums (default TRUE). | |
| 38 | # @param align Right-align numbers (default TRUE). | |
| 39 | csv2md <- function( f, decimals = 1, totals = T, align = T ) { | |
| 40 | # Read the CVS data from the file; ensure strings become characters. | |
| 41 | df <- read.table( f, sep=',', header=T, stringsAsFactors=F ) | |
| 42 | ||
| 43 | if( totals ) { | |
| 44 | # Determine what columns can be summed. | |
| 45 | number <- which( unlist( lapply( df, is.numeric ) ) ) | |
| 46 | ||
| 47 | # Use colSums when more than one summable column exists. | |
| 48 | if( length( number ) > 1 ) { | |
| 49 | f.sum <- colSums | |
| 50 | } | |
| 51 | else { | |
| 52 | f.sum <- sum | |
| 53 | } | |
| 54 | ||
| 55 | # Calculate the sum of all the summable columns and insert the | |
| 56 | # results back into the data frame. | |
| 57 | df[ (nrow( df ) + 1), number ] <- f.sum( df[, number], na.rm=TRUE ) | |
| 58 | ||
| 59 | # pluralize would be heavyweight here. | |
| 60 | if( length( number ) > 1 ) { | |
| 61 | t <- "**Totals**" | |
| 62 | } | |
| 63 | else { | |
| 64 | t <- "**Total**" | |
| 65 | } | |
| 66 | ||
| 67 | # Change the first column of the last line to "Total(s)". | |
| 68 | df[ nrow( df ), 1 ] <- t | |
| 69 | ||
| 70 | # Don't clutter the output with "NA" text. | |
| 71 | df[ is.na( df ) ] <- "" | |
| 72 | } | |
| 73 | ||
| 74 | if( align ) { | |
| 75 | is.char <- vapply( df, is.character, logical( 1 ) ) | |
| 76 | dashes <- paste( ifelse( is.char, ':---', '---:' ), collapse='|' ) | |
| 77 | } | |
| 78 | else { | |
| 79 | dashes <- paste( rep( '---', length( df ) ), collapse = '|') | |
| 80 | } | |
| 81 | ||
| 82 | # Create a Markdown version of the data frame. | |
| 83 | paste( | |
| 84 | paste( names( df ), collapse = '|'), '\n', | |
| 85 | dashes, '\n', | |
| 86 | paste( | |
| 87 | Reduce( function( x, y ) { | |
| 88 | paste( x, format( y, digits = decimals ), sep = '|' ) | |
| 89 | }, df | |
| 90 | ), | |
| 91 | collapse = '|\n', sep='' | |
| 92 | ) | |
| 93 | ) | |
| 94 | } | |
| 95 | ||
| 96 | 1 |
| 1 | # ###################################################################### | |
| 2 | # | |
| 3 | # Copyright 2016, White Magic Software, Ltd. | |
| 4 | # | |
| 5 | # Permission is hereby granted, free of charge, to any person obtaining | |
| 6 | # a copy of this software and associated documentation files (the | |
| 7 | # "Software"), to deal in the Software without restriction, including | |
| 8 | # without limitation the rights to use, copy, modify, merge, publish, | |
| 9 | # distribute, sublicense, and/or sell copies of the Software, and to | |
| 10 | # permit persons to whom the Software is furnished to do so, subject to | |
| 11 | # the following conditions: | |
| 12 | # | |
| 13 | # The above copyright notice and this permission notice shall be | |
| 14 | # included in all copies or substantial portions of the Software. | |
| 15 | # | |
| 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |
| 17 | # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
| 18 | # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |
| 19 | # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | |
| 20 | # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | |
| 21 | # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |
| 22 | # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |
| 23 | # | |
| 24 | # ###################################################################### | |
| 25 | ||
| 26 | # ###################################################################### | |
| 27 | # | |
| 28 | # See Damian Conway's "An Algorithmic Approach to English Pluralization": | |
| 29 | # http://goo.gl/oRL4MP | |
| 30 | # See Oliver Glerke's Evo Inflector: https://github.com/atteo/evo-inflector/ | |
| 31 | # See Shevek's Pluralizer: https://github.com/shevek/linguistics/ | |
| 32 | # See also: http://www.freevectors.net/assets/files/plural.txt | |
| 33 | # | |
| 34 | # ###################################################################### | |
| 35 | ||
| 36 | pluralize <- function( s, n ) { | |
| 37 | result <- s | |
| 38 | ||
| 39 | # Partial implementation of Conway's algorithm for nouns. | |
| 40 | if( n != 1 ) { | |
| 41 | if( pl.noninflective( s ) || | |
| 42 | pl.suffix( "fish", s ) || | |
| 43 | pl.suffix( "ois", s ) || | |
| 44 | pl.suffix( "sheep", s ) || | |
| 45 | pl.suffix( "deer", s ) || | |
| 46 | pl.suffix( "pox", s ) || | |
| 47 | pl.suffix( "[A-Z].*ese", s ) || | |
| 48 | pl.suffix( "itis", s ) ) { | |
| 49 | # 1. Retain non-inflective user-mapped noun as is. | |
| 50 | # 2. Retain non-inflective plural as is. | |
| 51 | result <- s | |
| 52 | } | |
| 53 | else if( pl.is.irregular.pl( s ) ) { | |
| 54 | # 4. Change irregular plurals based on mapping. | |
| 55 | result <- pl.irregular.pl( s ) | |
| 56 | } | |
| 57 | else if( pl.is.irregular.es( s ) ) { | |
| 58 | # x. From Shevek's Pluralizer | |
| 59 | result <- pl.inflect( s, "", "es" ) | |
| 60 | } | |
| 61 | else if( pl.suffix( "man", s ) ) { | |
| 62 | # 5. For -man, change -an to -en | |
| 63 | result <- pl.inflect( s, "an", "en" ) | |
| 64 | } | |
| 65 | else if( pl.suffix( "[lm]ouse", s ) ) { | |
| 66 | # 5. For [lm]ouse, change -ouse to -ice | |
| 67 | result <- pl.inflect( s, "ouse", "ice" ) | |
| 68 | } | |
| 69 | else if( pl.suffix( "tooth", s ) ) { | |
| 70 | # 5. For -tooth, change -ooth to -eeth | |
| 71 | result <- pl.inflect( s, "ooth", "eeth" ) | |
| 72 | } | |
| 73 | else if( pl.suffix( "goose", s ) ) { | |
| 74 | # 5. For -goose, change -oose to -eese | |
| 75 | result <- pl.inflect( s, "oose", "eese" ) | |
| 76 | } | |
| 77 | else if( pl.suffix( "foot", s ) ) { | |
| 78 | # 5. For -foot, change -oot to -eet | |
| 79 | result <- pl.inflect( s, "oot", "eet" ) | |
| 80 | } | |
| 81 | else if( pl.suffix( "zoon", s ) ) { | |
| 82 | # 5. For -zoon, change -on to -a | |
| 83 | result <- pl.inflect( s, "on", "a" ) | |
| 84 | } | |
| 85 | else if( pl.suffix( "[csx]is", s ) ) { | |
| 86 | # 5. Change -cis, -sis, -xis to -es | |
| 87 | result <- pl.inflect( s, "is", "es" ) | |
| 88 | } | |
| 89 | else if( pl.suffix( "([cs]h|ss)", s ) ) { | |
| 90 | # 8. Change -ch, -sh, -ss to -es | |
| 91 | result <- pl.inflect( s, "", "es" ) | |
| 92 | } | |
| 93 | else if( pl.suffix( "([aeo]lf|[^d]eaf|arf)", s ) ) { | |
| 94 | # 9. Change -f to -ves | |
| 95 | result <- pl.inflect( s, "f", "ves" ) | |
| 96 | } | |
| 97 | else if( pl.suffix( "[nlw]ife", s ) ) { | |
| 98 | # 9. Change -fe to -ves | |
| 99 | result <- pl.inflect( s, "fe", "ves" ) | |
| 100 | } | |
| 101 | else if( pl.suffix( "([aeiou]y|[A-Z].*y)", s ) ) { | |
| 102 | # 10. Change -y to -ys. | |
| 103 | result <- pl.inflect( s, "", "s" ) | |
| 104 | } | |
| 105 | else if( pl.suffix( "y", s ) ) { | |
| 106 | # 10. Change -y to -ies. | |
| 107 | result <- pl.inflect( s, "y", "ies" ) | |
| 108 | } | |
| 109 | else { | |
| 110 | # 13. Default plural: add -s. | |
| 111 | result <- pl.inflect( s, "", "s" ) | |
| 112 | } | |
| 113 | } | |
| 114 | ||
| 115 | result | |
| 116 | } | |
| 117 | ||
| 118 | # Pluralize s if n is not equal to 1. | |
| 119 | pl <- function( s, n ) { | |
| 120 | pluralize( s, x( n ) ) | |
| 121 | } | |
| 122 | ||
| 123 | # Returns the given string (s) with its suffix replaced by r. | |
| 124 | pl.inflect <- function( s, suffix, r ) { | |
| 125 | gsub( paste( suffix, "$", sep="" ), r, s ) | |
| 126 | } | |
| 127 | ||
| 128 | # Answers whether the given string (s) has the given ending. | |
| 129 | pl.suffix <- function( ending, s ) { | |
| 130 | grepl( paste( ending, "$", sep="" ), s ) | |
| 131 | } | |
| 132 | ||
| 133 | # Answers whether the given string (s) is a noninflective noun. | |
| 134 | pl.noninflective <- function( s ) { | |
| 135 | v <- c( | |
| 136 | "aircraft", "Bhutanese", "bison", "bream", "breeches", "britches", | |
| 137 | "Burmese", "carp", "chassis", "Chinese", "clippers", "cod", "contretemps", | |
| 138 | "corps", "debris", "diabetes", "djinn", "eland", "elk", "flounder", | |
| 139 | "fracas", "gallows", "graffiti", "headquarters", "herpes", "high-jinks", | |
| 140 | "homework", "hovercraft", "innings", "jackanapes", "Japanese", | |
| 141 | "Lebanese", "mackerel", "means", "measles", "mews", "mumps", "news", | |
| 142 | "pincers", "pliers", "Portuguese", "proceedings", "rabies", "salmon", | |
| 143 | "scissors", "sea-bass", "Senegalese", "series", "shears", "Siamese", | |
| 144 | "Sinhalese", "spacecraft", "species", "swine", "trout", "tuna", | |
| 145 | "Vietnamese", "watercraft", "whiting", "wildebeest" | |
| 146 | ) | |
| 147 | ||
| 148 | is.element( s, v ) | |
| 149 | } | |
| 150 | ||
| 151 | # Answers whether the given string (s) is an irregular plural. | |
| 152 | pl.is.irregular.pl <- function( s ) { | |
| 153 | # Could be refactored with pl.irregular.pl... | |
| 154 | v <- c( | |
| 155 | "beef", "brother", "child", "cow", "ephemeris", "genie", "money", | |
| 156 | "mongoose", "mythos", "octopus", "ox", "soliloquy", "trilby" | |
| 157 | ) | |
| 158 | ||
| 159 | is.element( s, v ) | |
| 160 | } | |
| 161 | ||
| 162 | # Call to pluralize an irregular noun. Only call after confirming | |
| 163 | # the noun is irregular via pl.is.irregular.pl. | |
| 164 | pl.irregular.pl <- function( s ) { | |
| 165 | v <- list( | |
| 166 | "beef" = "beefs", | |
| 167 | "brother" = "brothers", | |
| 168 | "child" = "children", | |
| 169 | "cow" = "cows", | |
| 170 | "ephemeris" = "ephemerides", | |
| 171 | "genie" = "genies", | |
| 172 | "money" = "moneys", | |
| 173 | "mongoose" = "mongooses", | |
| 174 | "mythos" = "mythoi", | |
| 175 | "octopus" = "octopuses", | |
| 176 | "ox" = "oxen", | |
| 177 | "soliloquy" = "soliloquies", | |
| 178 | "trilby" = "trilbys" | |
| 179 | ) | |
| 180 | ||
| 181 | # Faster version of v[[ s ]] | |
| 182 | .subset2( v, s ) | |
| 183 | } | |
| 184 | ||
| 185 | # Answers whether the given string (s) pluralizes with -es. | |
| 186 | pl.is.irregular.es <- function( s ) { | |
| 187 | v <- c( | |
| 188 | "acropolis", "aegis", "alias", "asbestos", "bathos", "bias", "bronchitis", | |
| 189 | "bursitis", "caddis", "cannabis", "canvas", "chaos", "cosmos", "dais", | |
| 190 | "digitalis", "epidermis", "ethos", "eyas", "gas", "glottis", "hubris", | |
| 191 | "ibis", "lens", "mantis", "marquis", "metropolis", "pathos", "pelvis", | |
| 192 | "polis", "rhinoceros", "sassafrass", "trellis" | |
| 193 | ) | |
| 194 | ||
| 195 | is.element( s, v ) | |
| 196 | } | |
| 197 | ||
| 198 | 1 |
| 3 | 3 | /* Do not use points (pt): FlyingSaucer on Debian fails to render. */ |
| 4 | 4 | body { |
| 5 | color: #454545; | |
| 5 | 6 | background-color: #fff; |
| 6 | 7 | margin: 0 auto; |
| 7 | line-height: 1.6; | |
| 8 | color: #454545; | |
| 9 | 8 | padding: .5em; |
| 9 | line-height: 1.6; | |
| 10 | 10 | font-feature-settings: 'liga' 1; |
| 11 | 11 | font-variant-ligatures: normal; |
| ... | ||
| 233 | 233 | text-align: left; |
| 234 | 234 | text-indent: 0; |
| 235 | border-style: solid; | |
| 236 | border-width: 0.05em; | |
| 237 | border-radius: .25em; | |
| 238 | background-color: #f8f8f8; | |
| 239 | } | |
| 240 | ||
| 241 | /* TO DO ***/ | |
| 242 | div.todo:before { | |
| 243 | content: "TODO"; | |
| 244 | color: #c00; | |
| 245 | font-weight: bold; | |
| 246 | display: block; | |
| 247 | width: 100%; | |
| 248 | text-align: center; | |
| 249 | padding: 0; | |
| 250 | margin: 0; | |
| 251 | } | |
| 252 | ||
| 253 | div.todo { | |
| 254 | padding: .5em; | |
| 255 | padding-top: .25em; | |
| 256 | padding-bottom: .25em; | |
| 235 | 257 | border-style: solid; |
| 236 | 258 | border-width: 0.05em; |
| 237 | 259 | border-radius: .25em; |
| 260 | border-color: #c00; | |
| 238 | 261 | background-color: #f8f8f8; |
| 262 | } | |
| 263 | ||
| 264 | /* SPEECH BUBBLE ***/ | |
| 265 | div.bubblerx, div.bubbletx { | |
| 266 | display: table; | |
| 267 | padding: .5em; | |
| 268 | padding-top: .25em; | |
| 269 | padding-bottom: .25em; | |
| 270 | margin: 1em; | |
| 271 | position: relative; | |
| 272 | border-radius: .25em; | |
| 273 | background-color: #ccc; | |
| 274 | ||
| 275 | font-family: 'OpenSansEmoji', sans-serif; | |
| 276 | font-size: 95%; | |
| 277 | } | |
| 278 | ||
| 279 | /* Transmit bubble on the right. */ | |
| 280 | div.bubbletx { | |
| 281 | margin-left: auto; | |
| 282 | } | |
| 283 | ||
| 284 | div.bubblerx:after, div.bubbletx:after { | |
| 285 | content: ""; | |
| 286 | position: absolute; | |
| 287 | width: 0; | |
| 288 | height: 0; | |
| 289 | top: .5em; | |
| 290 | border-top: 1em solid transparent; | |
| 291 | border-bottom: 1em solid transparent; | |
| 292 | } | |
| 293 | ||
| 294 | div.bubblerx::after { | |
| 295 | left: -1em; | |
| 296 | right: auto; | |
| 297 | border-right: 1em solid #ccc; | |
| 298 | border-left: none; | |
| 299 | } | |
| 300 | ||
| 301 | div.bubbletx:after { | |
| 302 | right: -1em; | |
| 303 | border-left: 1em solid #ccc; | |
| 239 | 304 | } |
| 240 | 305 | |
| 11 | 11 | import com.vladsch.flexmark.util.data.MutableDataSet; |
| 12 | 12 | import com.vladsch.flexmark.util.misc.Extension; |
| 13 | import org.junit.jupiter.api.Test; | |
| 13 | import org.junit.jupiter.params.ParameterizedTest; | |
| 14 | import org.junit.jupiter.params.provider.Arguments; | |
| 15 | import org.junit.jupiter.params.provider.MethodSource; | |
| 14 | 16 | |
| 15 | 17 | import java.util.ArrayList; |
| 16 | 18 | import java.util.List; |
| 19 | import java.util.stream.Stream; | |
| 17 | 20 | |
| 18 | 21 | import static org.junit.jupiter.api.Assertions.assertEquals; |
| 19 | 22 | |
| 20 | 23 | /** |
| 21 | 24 | * Test that basic styles for conversion exports as expected. |
| 22 | 25 | */ |
| 23 | 26 | public class ParserTest { |
| 24 | ||
| 25 | @Test | |
| 26 | void test_Conversion_InlineStyles_ExportedAsHtml() { | |
| 27 | final var md = "*emphasis* _emphasis_ **strong**"; | |
| 28 | 27 | |
| 28 | @ParameterizedTest | |
| 29 | @MethodSource( "markdownParameters" ) | |
| 30 | void test_Conversion_Markdown_Html( final String md, final String expected ) { | |
| 29 | 31 | final var extensions = createExtensions(); |
| 30 | 32 | final var options = new MutableDataSet(); |
| ... | ||
| 40 | 42 | final var document = parser.parse( md ); |
| 41 | 43 | final var actual = renderer.render( document ); |
| 42 | final var expected = | |
| 43 | "<p><em>emphasis</em> <em>emphasis</em> <strong>strong</strong></p>\n"; | |
| 44 | 44 | |
| 45 | 45 | assertEquals( expected, actual ); |
| ... | ||
| 56 | 56 | |
| 57 | 57 | return extensions; |
| 58 | } | |
| 59 | ||
| 60 | private static Stream<Arguments> markdownParameters() { | |
| 61 | return Stream.of( | |
| 62 | Arguments.of( | |
| 63 | "*emphasis* _emphasis_ **strong**", | |
| 64 | "<p><em>emphasis</em> <em>emphasis</em> <strong>strong</strong></p>\n" | |
| 65 | ), | |
| 66 | Arguments.of( | |
| 67 | "the \uD83D\uDC4D emoji", | |
| 68 | "<p>the \uD83D\uDC4D emoji</p>\n" | |
| 69 | ) | |
| 70 | ); | |
| 58 | 71 | } |
| 59 | 72 | } |
| 17 | 17 | public class MediaTypeTest { |
| 18 | 18 | /** |
| 19 | * Test that {@link MediaType#equals(String, String)} is case insensitive. | |
| 19 | * Test that {@link MediaType#equals(String, String)} is case-insensitive. | |
| 20 | 20 | */ |
| 21 | 21 | @Test |
| 1 | package com.keenwrite.processors.html; | |
| 2 | ||
| 3 | import com.keenwrite.ExportFormat; | |
| 4 | import com.keenwrite.editors.common.Caret; | |
| 5 | import com.keenwrite.processors.ProcessorContext; | |
| 6 | import org.junit.jupiter.params.ParameterizedTest; | |
| 7 | import org.junit.jupiter.params.provider.Arguments; | |
| 8 | import org.junit.jupiter.params.provider.MethodSource; | |
| 9 | ||
| 10 | import java.io.File; | |
| 11 | import java.nio.file.Path; | |
| 12 | import java.util.HashMap; | |
| 13 | import java.util.stream.Stream; | |
| 14 | ||
| 15 | import static com.keenwrite.ExportFormat.HTML_TEX_DELIMITED; | |
| 16 | import static com.keenwrite.ExportFormat.XHTML_TEX; | |
| 17 | import static com.keenwrite.processors.ProcessorContext.builder; | |
| 18 | import static com.keenwrite.processors.ProcessorFactory.createProcessors; | |
| 19 | import static java.util.Locale.ENGLISH; | |
| 20 | import static org.junit.jupiter.api.Assertions.assertEquals; | |
| 21 | ||
| 22 | public class XhtmlProcessorTest { | |
| 23 | ||
| 24 | /** | |
| 25 | * Contains the thumbs up emoji. | |
| 26 | */ | |
| 27 | private static final String EMOJI_MARKDOWN = "the \uD83D\uDC4D emoji"; | |
| 28 | ||
| 29 | @ParameterizedTest | |
| 30 | @MethodSource( "formatParameters" ) | |
| 31 | void test_Conversion_EmojiInput_EncodedEmoji( | |
| 32 | final ExportFormat format, final String expected ) { | |
| 33 | final var context = createProcessorContext( format ); | |
| 34 | final var processor = createProcessors( context ); | |
| 35 | final var actual = processor.apply( EMOJI_MARKDOWN ); | |
| 36 | ||
| 37 | assertEquals( expected, actual ); | |
| 38 | } | |
| 39 | ||
| 40 | private static ProcessorContext createProcessorContext( | |
| 41 | final ExportFormat format ) { | |
| 42 | final var caret = Caret.builder().build(); | |
| 43 | return builder() | |
| 44 | .with( ProcessorContext.Mutator::setExportFormat, format ) | |
| 45 | .with( ProcessorContext.Mutator::setInputPath, Path.of( "f.md" ) ) | |
| 46 | .with( ProcessorContext.Mutator::setDefinitions, HashMap::new ) | |
| 47 | .with( ProcessorContext.Mutator::setLocale, () -> ENGLISH ) | |
| 48 | .with( ProcessorContext.Mutator::setMetadata, HashMap::new ) | |
| 49 | .with( ProcessorContext.Mutator::setThemePath, () -> Path.of( "b" ) ) | |
| 50 | .with( ProcessorContext.Mutator::setCaret, () -> caret ) | |
| 51 | .with( ProcessorContext.Mutator::setImageDir, () -> new File( "i" ) ) | |
| 52 | .with( ProcessorContext.Mutator::setImageOrder, () -> "" ) | |
| 53 | .with( ProcessorContext.Mutator::setImageServer, () -> "" ) | |
| 54 | .with( ProcessorContext.Mutator::setSigilBegan, () -> "" ) | |
| 55 | .with( ProcessorContext.Mutator::setSigilEnded, () -> "" ) | |
| 56 | .with( ProcessorContext.Mutator::setRScript, () -> "" ) | |
| 57 | .with( ProcessorContext.Mutator::setRWorkingDir, () -> Path.of( "r" ) ) | |
| 58 | .with( ProcessorContext.Mutator::setCurlQuotes, () -> true ) | |
| 59 | .with( ProcessorContext.Mutator::setAutoClean, () -> true ) | |
| 60 | .build(); | |
| 61 | } | |
| 62 | ||
| 63 | private static Stream<Arguments> formatParameters() { | |
| 64 | return Stream.of( | |
| 65 | Arguments.of( | |
| 66 | HTML_TEX_DELIMITED, | |
| 67 | "<p id=\"caret\">the \uD83D\uDC4D emoji</p>\n" | |
| 68 | ), | |
| 69 | Arguments.of( | |
| 70 | XHTML_TEX, | |
| 71 | """ | |
| 72 | <html> | |
| 73 | <head> | |
| 74 | <title> </title> | |
| 75 | <meta charset="utf8"/> | |
| 76 | <meta content="2" name="count"/> | |
| 77 | </head> | |
| 78 | <body> | |
| 79 | <p id="caret">the 👍 emoji</p> | |
| 80 | </body> | |
| 81 | </html> | |
| 82 | """ | |
| 83 | ) | |
| 84 | ); | |
| 85 | } | |
| 86 | } | |
| 1 | 87 |