├── .idea ├── .name ├── vcs.xml └── misc.xml ├── gradle.properties ├── site ├── .gitignore ├── .kobweb │ ├── site │ │ ├── favicon.ico │ │ ├── gtag.js │ │ ├── vs.css │ │ ├── lightfair.css │ │ ├── intellij-light.css │ │ ├── cupertino.css │ │ ├── google-light.css │ │ ├── FileSaver.js │ │ ├── logo.svg │ │ ├── index.html │ │ ├── highlight.min.js │ │ └── dom-to-image.js │ └── conf.yaml ├── src │ ├── jsMain │ │ ├── resources │ │ │ ├── public │ │ │ │ ├── favicon.ico │ │ │ │ ├── gtag.js │ │ │ │ ├── vs.css │ │ │ │ ├── lightfair.css │ │ │ │ ├── intellij-light.css │ │ │ │ ├── cupertino.css │ │ │ │ ├── google-light.css │ │ │ │ ├── FileSaver.js │ │ │ │ ├── logo.svg │ │ │ │ ├── highlight.min.js │ │ │ │ └── dom-to-image.js │ │ │ └── README.md │ │ └── kotlin │ │ │ └── com │ │ │ └── stevdza │ │ │ └── san │ │ │ ├── util │ │ │ └── Res.kt │ │ │ ├── model │ │ │ └── EditorTheme.kt │ │ │ ├── MyApp.kt │ │ │ ├── pages │ │ │ └── Index.kt │ │ │ └── components │ │ │ ├── Footer.kt │ │ │ ├── Editor.kt │ │ │ └── ControlsView.kt │ └── jvmMain │ │ └── kotlin │ │ └── com │ │ └── stevdza │ │ └── san │ │ └── api │ │ └── README.md └── build.gradle.kts ├── gradle ├── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties └── libs.versions.toml ├── README.md ├── settings.gradle.kts ├── .gitignore ├── gradlew.bat └── gradlew /.idea/.name: -------------------------------------------------------------------------------- 1 | san -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | kotlin.code.style=official 2 | -------------------------------------------------------------------------------- /site/.gitignore: -------------------------------------------------------------------------------- 1 | # Kobweb ignores 2 | .kobweb/* 3 | !.kobweb/conf.yaml 4 | !.kobweb/site 5 | -------------------------------------------------------------------------------- /site/.kobweb/site/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stevdza-san/EditorApp/HEAD/site/.kobweb/site/favicon.ico -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stevdza-san/EditorApp/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /site/src/jsMain/resources/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stevdza-san/EditorApp/HEAD/site/src/jsMain/resources/public/favicon.ico -------------------------------------------------------------------------------- /site/src/jvmMain/kotlin/com/stevdza/san/api/README.md: -------------------------------------------------------------------------------- 1 | Define API routes in here. 2 | 3 | See also: https://github.com/varabyte/kobweb#define-api-routes 4 | -------------------------------------------------------------------------------- /site/.kobweb/site/gtag.js: -------------------------------------------------------------------------------- 1 | window.dataLayer = window.dataLayer || []; 2 | function gtag(){dataLayer.push(arguments);} 3 | gtag('js', new Date()); 4 | gtag('config', 'G-M6NQ3C6B4S'); -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /site/src/jsMain/resources/public/gtag.js: -------------------------------------------------------------------------------- 1 | window.dataLayer = window.dataLayer || []; 2 | function gtag(){dataLayer.push(arguments);} 3 | gtag('js', new Date()); 4 | gtag('config', 'G-M6NQ3C6B4S'); -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /site/src/jsMain/resources/README.md: -------------------------------------------------------------------------------- 1 | Define markdown pages under a markdown/ folder, and 2 | they will get picked up and converted to html. 3 | 4 | You must have enabled markdown on this project for this 5 | feature to work. 6 | 7 | See also: https://github.com/varabyte/kobweb#markdown 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Pretty-KO - Web App built with Kotlin and Jetpack Compose (Kobweb framework) 2 |

3 | YouTube Video Tutorial 4 |

5 |

6 | 7 |

8 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | gradlePluginPortal() 4 | maven("https://maven.pkg.jetbrains.space/public/p/compose/dev") 5 | maven("https://us-central1-maven.pkg.dev/varabyte-repos/public") 6 | } 7 | } 8 | 9 | rootProject.name = "san" 10 | 11 | include(":site") 12 | -------------------------------------------------------------------------------- /site/.kobweb/conf.yaml: -------------------------------------------------------------------------------- 1 | site: 2 | title: "Pretty-KO" 3 | 4 | server: 5 | files: 6 | dev: 7 | contentRoot: "build/processedResources/js/main/public" 8 | script: "build/developmentExecutable/san.js" 9 | api: "build/libs/san.jar" 10 | prod: 11 | script: "build/distributions/san.js" 12 | siteRoot: ".kobweb/site" 13 | 14 | port: 8080 -------------------------------------------------------------------------------- /site/src/jsMain/kotlin/com/stevdza/san/util/Res.kt: -------------------------------------------------------------------------------- 1 | package com.stevdza.san.util 2 | 3 | object Res { 4 | object Image { 5 | const val logo = "logo.svg" 6 | } 7 | object Id { 8 | const val editor = "editor" 9 | const val fontSize = "font-size" 10 | const val lineHeight = "line-height" 11 | const val padding = "padding" 12 | } 13 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # General ignores 2 | .DS_Store 3 | build 4 | out 5 | kotlin-js-store 6 | 7 | # IntelliJ ignores 8 | *.iml 9 | /*.ipr 10 | 11 | /.idea/caches 12 | /.idea/libraries 13 | /.idea/modules.xml 14 | /.idea/workspace.xml 15 | /.idea/gradle.xml 16 | /.idea/navEditor.xml 17 | /.idea/assetWizardSettings.xml 18 | /.idea/artifacts 19 | /.idea/compiler.xml 20 | /.idea/jarRepositories.xml 21 | /.idea/*.iml 22 | /.idea/modules 23 | /.idea/libraries-with-intellij-classes.xml 24 | 25 | # Gradle ignores 26 | .gradle 27 | 28 | -------------------------------------------------------------------------------- /site/src/jsMain/kotlin/com/stevdza/san/model/EditorTheme.kt: -------------------------------------------------------------------------------- 1 | package com.stevdza.san.model 2 | 3 | import org.jetbrains.compose.web.css.CSSColorValue 4 | import org.jetbrains.compose.web.css.rgb 5 | 6 | enum class EditorTheme(val color: CSSColorValue) { 7 | RoyalBlue(color = rgb(r = 28, g = 181, b = 224)), 8 | SeaGreen(color = rgb(r = 28, g = 224, b = 153)), 9 | WickedRed(color = rgb(r = 224, g = 28, b = 28)), 10 | ModernPurple(color = rgb(r = 138, g = 28, b = 224)) 11 | } 12 | 13 | enum class Theme(val color: CSSColorValue) { 14 | Red(color = rgb(r = 255, g = 95, b = 86)), 15 | Yellow(color = rgb(r = 255, g = 189, b = 46)), 16 | Green(color = rgb(r = 39, g = 201, b = 63)) 17 | } -------------------------------------------------------------------------------- /site/src/jsMain/kotlin/com/stevdza/san/MyApp.kt: -------------------------------------------------------------------------------- 1 | package com.stevdza.san 2 | 3 | import androidx.compose.runtime.* 4 | import com.varabyte.kobweb.compose.ui.modifiers.* 5 | import com.varabyte.kobweb.core.App 6 | import com.varabyte.kobweb.silk.init.InitSilk 7 | import com.varabyte.kobweb.silk.init.InitSilkContext 8 | import com.varabyte.kobweb.silk.SilkApp 9 | import com.varabyte.kobweb.silk.components.layout.Surface 10 | import com.varabyte.kobweb.silk.components.style.common.SmoothColorStyle 11 | import com.varabyte.kobweb.silk.components.style.toModifier 12 | import org.jetbrains.compose.web.css.* 13 | 14 | @InitSilk 15 | fun updateTheme(ctx: InitSilkContext) { 16 | // Configure silk here 17 | } 18 | 19 | @App 20 | @Composable 21 | fun MyApp(content: @Composable () -> Unit) { 22 | SilkApp { 23 | Surface(SmoothColorStyle.toModifier().minWidth(100.vw).minHeight(100.vh)) { 24 | content() 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /gradle/libs.versions.toml: -------------------------------------------------------------------------------- 1 | [versions] 2 | jetbrains-compose = "1.4.0" 3 | kobweb = "0.13.0" 4 | kotlin = "1.8.20" 5 | 6 | [libraries] 7 | kobweb-api = { module = "com.varabyte.kobweb:kobweb-api", version.ref = "kobweb" } 8 | kobweb-core = { module = "com.varabyte.kobweb:kobweb-core ", version.ref = "kobweb" } 9 | kobweb-silk-core = { module = "com.varabyte.kobweb:kobweb-silk", version.ref = "kobweb" } 10 | kobweb-silk-icons-fa = { module = "com.varabyte.kobweb:kobweb-silk-icons-fa", version.ref = "kobweb" } 11 | kobwebx-markdown = { module = "com.varabyte.kobwebx:kobwebx-markdown", version.ref = "kobweb" } 12 | 13 | [plugins] 14 | jetbrains-compose = { id = "org.jetbrains.compose", version.ref = "jetbrains-compose" } 15 | kobweb-application = { id = "com.varabyte.kobweb.application", version.ref = "kobweb" } 16 | kobwebx-markdown = { id = "com.varabyte.kobwebx.markdown", version.ref = "kobweb" } 17 | kotlin-multiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" } -------------------------------------------------------------------------------- /site/.kobweb/site/vs.css: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Visual Studio-like style based on original C# coloring by Jason Diamond 4 | 5 | */ 6 | .hljs { 7 | background: white; 8 | color: black; 9 | } 10 | 11 | .hljs-comment, 12 | .hljs-quote, 13 | .hljs-variable { 14 | color: #008000; 15 | } 16 | 17 | .hljs-keyword, 18 | .hljs-selector-tag, 19 | .hljs-built_in, 20 | .hljs-name, 21 | .hljs-tag { 22 | color: #00f; 23 | } 24 | 25 | .hljs-string, 26 | .hljs-title, 27 | .hljs-section, 28 | .hljs-attribute, 29 | .hljs-literal, 30 | .hljs-template-tag, 31 | .hljs-template-variable, 32 | .hljs-type, 33 | .hljs-addition { 34 | color: #a31515; 35 | } 36 | 37 | .hljs-deletion, 38 | .hljs-selector-attr, 39 | .hljs-selector-pseudo, 40 | .hljs-meta { 41 | color: #2b91af; 42 | } 43 | 44 | .hljs-doctag { 45 | color: #808080; 46 | } 47 | 48 | .hljs-attr { 49 | color: #f00; 50 | } 51 | 52 | .hljs-symbol, 53 | .hljs-bullet, 54 | .hljs-link { 55 | color: #00b0e8; 56 | } 57 | 58 | 59 | .hljs-emphasis { 60 | font-style: italic; 61 | } 62 | 63 | .hljs-strong { 64 | font-weight: bold; 65 | } 66 | -------------------------------------------------------------------------------- /site/src/jsMain/resources/public/vs.css: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Visual Studio-like style based on original C# coloring by Jason Diamond 4 | 5 | */ 6 | .hljs { 7 | background: white; 8 | color: black; 9 | } 10 | 11 | .hljs-comment, 12 | .hljs-quote, 13 | .hljs-variable { 14 | color: #008000; 15 | } 16 | 17 | .hljs-keyword, 18 | .hljs-selector-tag, 19 | .hljs-built_in, 20 | .hljs-name, 21 | .hljs-tag { 22 | color: #00f; 23 | } 24 | 25 | .hljs-string, 26 | .hljs-title, 27 | .hljs-section, 28 | .hljs-attribute, 29 | .hljs-literal, 30 | .hljs-template-tag, 31 | .hljs-template-variable, 32 | .hljs-type, 33 | .hljs-addition { 34 | color: #a31515; 35 | } 36 | 37 | .hljs-deletion, 38 | .hljs-selector-attr, 39 | .hljs-selector-pseudo, 40 | .hljs-meta { 41 | color: #2b91af; 42 | } 43 | 44 | .hljs-doctag { 45 | color: #808080; 46 | } 47 | 48 | .hljs-attr { 49 | color: #f00; 50 | } 51 | 52 | .hljs-symbol, 53 | .hljs-bullet, 54 | .hljs-link { 55 | color: #00b0e8; 56 | } 57 | 58 | 59 | .hljs-emphasis { 60 | font-style: italic; 61 | } 62 | 63 | .hljs-strong { 64 | font-weight: bold; 65 | } 66 | -------------------------------------------------------------------------------- /site/.kobweb/site/lightfair.css: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Lightfair style (c) Tristian Kelly 4 | 5 | */ 6 | 7 | .hljs { 8 | color: #444; 9 | background: #fff; 10 | } 11 | 12 | .hljs-name { 13 | color:#01a3a3; 14 | } 15 | 16 | .hljs-tag,.hljs-meta { 17 | color:#778899; 18 | } 19 | 20 | .hljs-subst { 21 | /* default */ 22 | } 23 | 24 | .hljs-comment { 25 | color: #888888 26 | } 27 | 28 | .hljs-keyword, 29 | .hljs-attribute, 30 | .hljs-selector-tag, 31 | .hljs-meta .hljs-keyword, 32 | 33 | .hljs-doctag, 34 | .hljs-name { 35 | font-weight: bold 36 | } 37 | 38 | .hljs-type, 39 | .hljs-string, 40 | .hljs-number, 41 | .hljs-selector-id, 42 | .hljs-selector-class, 43 | .hljs-quote, 44 | .hljs-template-tag, 45 | .hljs-deletion { 46 | color: #4286f4 47 | } 48 | 49 | .hljs-title, 50 | .hljs-section { 51 | color: #4286f4; 52 | font-weight: bold 53 | } 54 | 55 | .hljs-regexp, 56 | .hljs-symbol, 57 | .hljs-variable, 58 | .hljs-template-variable, 59 | .hljs-link, 60 | .hljs-selector-attr, 61 | .hljs-selector-pseudo { 62 | color: #BC6060 63 | } 64 | 65 | .hljs-literal { 66 | color: #62bcbc 67 | } 68 | 69 | .hljs-built_in, 70 | .hljs-bullet, 71 | .hljs-code, 72 | .hljs-addition { 73 | color: #25c6c6 74 | } 75 | 76 | .hljs-meta .hljs-string { 77 | color: #4d99bf 78 | } 79 | 80 | .hljs-emphasis { 81 | font-style: italic 82 | } 83 | 84 | .hljs-strong { 85 | font-weight: bold 86 | } -------------------------------------------------------------------------------- /site/src/jsMain/resources/public/lightfair.css: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Lightfair style (c) Tristian Kelly 4 | 5 | */ 6 | 7 | .hljs { 8 | color: #444; 9 | background: #fff; 10 | } 11 | 12 | .hljs-name { 13 | color:#01a3a3; 14 | } 15 | 16 | .hljs-tag,.hljs-meta { 17 | color:#778899; 18 | } 19 | 20 | .hljs-subst { 21 | /* default */ 22 | } 23 | 24 | .hljs-comment { 25 | color: #888888 26 | } 27 | 28 | .hljs-keyword, 29 | .hljs-attribute, 30 | .hljs-selector-tag, 31 | .hljs-meta .hljs-keyword, 32 | 33 | .hljs-doctag, 34 | .hljs-name { 35 | font-weight: bold 36 | } 37 | 38 | .hljs-type, 39 | .hljs-string, 40 | .hljs-number, 41 | .hljs-selector-id, 42 | .hljs-selector-class, 43 | .hljs-quote, 44 | .hljs-template-tag, 45 | .hljs-deletion { 46 | color: #4286f4 47 | } 48 | 49 | .hljs-title, 50 | .hljs-section { 51 | color: #4286f4; 52 | font-weight: bold 53 | } 54 | 55 | .hljs-regexp, 56 | .hljs-symbol, 57 | .hljs-variable, 58 | .hljs-template-variable, 59 | .hljs-link, 60 | .hljs-selector-attr, 61 | .hljs-selector-pseudo { 62 | color: #BC6060 63 | } 64 | 65 | .hljs-literal { 66 | color: #62bcbc 67 | } 68 | 69 | .hljs-built_in, 70 | .hljs-bullet, 71 | .hljs-code, 72 | .hljs-addition { 73 | color: #25c6c6 74 | } 75 | 76 | .hljs-meta .hljs-string { 77 | color: #4d99bf 78 | } 79 | 80 | .hljs-emphasis { 81 | font-style: italic 82 | } 83 | 84 | .hljs-strong { 85 | font-weight: bold 86 | } -------------------------------------------------------------------------------- /site/src/jsMain/kotlin/com/stevdza/san/pages/Index.kt: -------------------------------------------------------------------------------- 1 | package com.stevdza.san.pages 2 | 3 | import androidx.compose.runtime.Composable 4 | import com.stevdza.san.components.Editor 5 | import com.stevdza.san.components.Footer 6 | import com.stevdza.san.util.Res 7 | import com.varabyte.kobweb.compose.foundation.layout.Arrangement 8 | import com.varabyte.kobweb.compose.foundation.layout.Box 9 | import com.varabyte.kobweb.compose.foundation.layout.Column 10 | import com.varabyte.kobweb.compose.ui.Alignment 11 | import com.varabyte.kobweb.compose.ui.Modifier 12 | import com.varabyte.kobweb.compose.ui.modifiers.* 13 | import com.varabyte.kobweb.core.Page 14 | import com.varabyte.kobweb.silk.components.graphics.Image 15 | import org.jetbrains.compose.web.css.percent 16 | import org.jetbrains.compose.web.css.px 17 | 18 | @Page 19 | @Composable 20 | fun HomePage() { 21 | Box( 22 | modifier = Modifier 23 | .fillMaxWidth() 24 | .minHeight(100.percent) 25 | ) { 26 | Column( 27 | modifier = Modifier.fillMaxSize(), 28 | verticalArrangement = Arrangement.Center, 29 | horizontalAlignment = Alignment.CenterHorizontally 30 | ) { 31 | Image(src = Res.Image.logo, modifier = Modifier.size(200.px)) 32 | Editor() 33 | } 34 | Footer( 35 | modifier = Modifier 36 | .gridRowStart(2) 37 | .gridRowEnd(6) 38 | ) 39 | } 40 | } -------------------------------------------------------------------------------- /site/src/jsMain/kotlin/com/stevdza/san/components/Footer.kt: -------------------------------------------------------------------------------- 1 | package com.stevdza.san.components 2 | 3 | import androidx.compose.runtime.Composable 4 | import com.stevdza.san.model.EditorTheme 5 | import com.varabyte.kobweb.compose.foundation.layout.Arrangement 6 | import com.varabyte.kobweb.compose.foundation.layout.Row 7 | import com.varabyte.kobweb.compose.ui.Alignment 8 | import com.varabyte.kobweb.compose.ui.Modifier 9 | import com.varabyte.kobweb.compose.ui.modifiers.* 10 | import com.varabyte.kobweb.navigation.OpenLinkStrategy 11 | import com.varabyte.kobweb.silk.components.navigation.Link 12 | import com.varabyte.kobweb.silk.components.style.* 13 | import org.jetbrains.compose.web.css.cssRem 14 | import org.jetbrains.compose.web.css.px 15 | import org.jetbrains.compose.web.dom.Text 16 | 17 | val FooterStyle by ComponentStyle.base { 18 | Modifier 19 | .fillMaxWidth() 20 | .margin(topBottom = 20.px) 21 | .fontSize(14.px) 22 | } 23 | 24 | val LinkStyle by ComponentStyle { 25 | anyLink { 26 | Modifier.color(EditorTheme.RoyalBlue.color) 27 | } 28 | } 29 | 30 | @Composable 31 | fun Footer(modifier: Modifier = Modifier) { 32 | Row( 33 | FooterStyle.toModifier().then(modifier), 34 | verticalAlignment = Alignment.CenterVertically, 35 | horizontalArrangement = Arrangement.Center 36 | ) { 37 | Text("Developed by") 38 | Link( 39 | modifier = LinkStyle.toModifier().margin(left = 6.px), 40 | path = "https://www.youtube.com/stevdza_san", 41 | text = "Stevdza-San", 42 | openExternalLinksStrategy = OpenLinkStrategy.IN_NEW_TAB 43 | ) 44 | } 45 | } -------------------------------------------------------------------------------- /site/.kobweb/site/intellij-light.css: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Intellij-light style (c) Pegasis 4 | 5 | */ 6 | 7 | .hljs { 8 | color: #000; 9 | background: #fff; 10 | } 11 | 12 | .hljs-subst, 13 | .hljs-title { 14 | font-weight: normal; 15 | color: #000; 16 | } 17 | 18 | .hljs-title.function_ { 19 | color: #7A7A43; 20 | } 21 | 22 | .hljs-code, 23 | .hljs-comment, 24 | .hljs-quote { 25 | color: #8C8C8C; 26 | font-style: italic; 27 | } 28 | 29 | .hljs-meta { 30 | color: #9E880D; 31 | } 32 | 33 | .hljs-section { 34 | color: #871094; 35 | } 36 | 37 | .hljs-variable.language_, 38 | .hljs-symbol, 39 | .hljs-selector-class, 40 | .hljs-selector-id, 41 | .hljs-selector-tag, 42 | .hljs-template-tag, 43 | .hljs-selector-attr, 44 | .hljs-selector-pseudo, 45 | .hljs-keyword, 46 | .hljs-meta .hljs-keyword, 47 | .hljs-literal, 48 | .hljs-name, 49 | .hljs-built_in, 50 | .hljs-type { 51 | color: #0033B3; 52 | } 53 | 54 | .hljs-property, 55 | .hljs-attr { 56 | color: #871094; 57 | } 58 | 59 | .hljs-attribute { 60 | color: #174AD4; 61 | } 62 | 63 | .hljs-number { 64 | color: #1750EB; 65 | } 66 | 67 | .hljs-regexp { 68 | color: #264EFF; 69 | } 70 | 71 | .hljs-link { 72 | text-decoration: underline; 73 | color: #006DCC; 74 | } 75 | 76 | .hljs-meta .hljs-string, 77 | .hljs-string { 78 | color: #067D17; 79 | } 80 | 81 | .hljs-char.escape_ { 82 | color: #0037A6; 83 | } 84 | 85 | .hljs-doctag { 86 | text-decoration: underline; 87 | } 88 | 89 | .hljs-template-variable { 90 | color: #248F8F; 91 | } 92 | 93 | .hljs-addition { 94 | background: #BEE6BE; 95 | } 96 | 97 | .hljs-deletion { 98 | background: #D6D6D6; 99 | } 100 | 101 | .hljs-emphasis { 102 | font-style: italic; 103 | } 104 | 105 | .hljs-strong { 106 | font-weight: bold; 107 | } 108 | 109 | .hljs-variable, 110 | .hljs-operator, 111 | .hljs-punctuation, 112 | .hljs-title.class_.inherited__, 113 | .hljs-title.class_, 114 | .hljs-params, 115 | .hljs-bullet, 116 | .hljs-formula, 117 | .hljs-tag { 118 | /* purposely ignored */ 119 | } 120 | -------------------------------------------------------------------------------- /site/src/jsMain/resources/public/intellij-light.css: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Intellij-light style (c) Pegasis 4 | 5 | */ 6 | 7 | .hljs { 8 | color: #000; 9 | background: #fff; 10 | } 11 | 12 | .hljs-subst, 13 | .hljs-title { 14 | font-weight: normal; 15 | color: #000; 16 | } 17 | 18 | .hljs-title.function_ { 19 | color: #7A7A43; 20 | } 21 | 22 | .hljs-code, 23 | .hljs-comment, 24 | .hljs-quote { 25 | color: #8C8C8C; 26 | font-style: italic; 27 | } 28 | 29 | .hljs-meta { 30 | color: #9E880D; 31 | } 32 | 33 | .hljs-section { 34 | color: #871094; 35 | } 36 | 37 | .hljs-variable.language_, 38 | .hljs-symbol, 39 | .hljs-selector-class, 40 | .hljs-selector-id, 41 | .hljs-selector-tag, 42 | .hljs-template-tag, 43 | .hljs-selector-attr, 44 | .hljs-selector-pseudo, 45 | .hljs-keyword, 46 | .hljs-meta .hljs-keyword, 47 | .hljs-literal, 48 | .hljs-name, 49 | .hljs-built_in, 50 | .hljs-type { 51 | color: #0033B3; 52 | } 53 | 54 | .hljs-property, 55 | .hljs-attr { 56 | color: #871094; 57 | } 58 | 59 | .hljs-attribute { 60 | color: #174AD4; 61 | } 62 | 63 | .hljs-number { 64 | color: #1750EB; 65 | } 66 | 67 | .hljs-regexp { 68 | color: #264EFF; 69 | } 70 | 71 | .hljs-link { 72 | text-decoration: underline; 73 | color: #006DCC; 74 | } 75 | 76 | .hljs-meta .hljs-string, 77 | .hljs-string { 78 | color: #067D17; 79 | } 80 | 81 | .hljs-char.escape_ { 82 | color: #0037A6; 83 | } 84 | 85 | .hljs-doctag { 86 | text-decoration: underline; 87 | } 88 | 89 | .hljs-template-variable { 90 | color: #248F8F; 91 | } 92 | 93 | .hljs-addition { 94 | background: #BEE6BE; 95 | } 96 | 97 | .hljs-deletion { 98 | background: #D6D6D6; 99 | } 100 | 101 | .hljs-emphasis { 102 | font-style: italic; 103 | } 104 | 105 | .hljs-strong { 106 | font-weight: bold; 107 | } 108 | 109 | .hljs-variable, 110 | .hljs-operator, 111 | .hljs-punctuation, 112 | .hljs-title.class_.inherited__, 113 | .hljs-title.class_, 114 | .hljs-params, 115 | .hljs-bullet, 116 | .hljs-formula, 117 | .hljs-tag { 118 | /* purposely ignored */ 119 | } 120 | -------------------------------------------------------------------------------- /site/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import com.varabyte.kobweb.gradle.application.util.configAsKobwebApplication 2 | import kotlinx.html.link 3 | import kotlinx.html.script 4 | 5 | @Suppress("DSL_SCOPE_VIOLATION") 6 | plugins { 7 | alias(libs.plugins.kotlin.multiplatform) 8 | alias(libs.plugins.jetbrains.compose) 9 | alias(libs.plugins.kobweb.application) 10 | alias(libs.plugins.kobwebx.markdown) 11 | } 12 | 13 | group = "com.stevdza.san" 14 | version = "1.0-SNAPSHOT" 15 | 16 | kobweb { 17 | app { 18 | index { 19 | description.set( 20 | "Web app that allows you to paste, apply colors and export your code as a beautiful image," + 21 | " that you can later share with everyone on social media!" 22 | ) 23 | 24 | head.add { 25 | script { 26 | async = true 27 | src = "https://www.googletagmanager.com/gtag/js?id=G-M6NQ3C6B4S" 28 | } 29 | script { 30 | src = "gtag.js" 31 | } 32 | script { 33 | src = "dom-to-image.js" 34 | } 35 | script { 36 | src = "FileSaver.js" 37 | } 38 | script { 39 | src = "highlight.min.js" 40 | } 41 | script { 42 | src = "https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/js/bootstrap.bundle.min.js" 43 | } 44 | link { 45 | rel = "stylesheet" 46 | href = "https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css" 47 | } 48 | link { 49 | rel = "stylesheet" 50 | href = "lightfair.css" 51 | } 52 | } 53 | } 54 | } 55 | } 56 | 57 | kotlin { 58 | configAsKobwebApplication("san", includeServer = true) 59 | tasks.withType { 60 | kotlinOptions.jvmTarget = "11" 61 | } 62 | 63 | sourceSets { 64 | val commonMain by getting { 65 | dependencies { 66 | implementation(compose.runtime) 67 | } 68 | } 69 | 70 | val jsMain by getting { 71 | dependencies { 72 | implementation(compose.web.core) 73 | implementation(libs.kobweb.core) 74 | implementation(libs.kobweb.silk.core) 75 | implementation(libs.kobweb.silk.icons.fa) 76 | implementation(libs.kobwebx.markdown) 77 | } 78 | } 79 | val jvmMain by getting { 80 | dependencies { 81 | implementation(libs.kobweb.api) 82 | } 83 | } 84 | } 85 | } -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /site/.kobweb/site/cupertino.css: -------------------------------------------------------------------------------- 1 | /*! 2 | Theme: Cupertino 3 | Author: Defman21 4 | License: ~ MIT (or more permissive) [via base16-schemes-source] 5 | Maintainer: @highlightjs/core-team 6 | Version: 2021.09.0 7 | */ 8 | 9 | /* 10 | WARNING: DO NOT EDIT THIS FILE DIRECTLY. 11 | 12 | This theme file was auto-generated from the Base16 scheme cupertino 13 | by the Highlight.js Base16 template builder. 14 | 15 | - https://github.com/highlightjs/base16-highlightjs 16 | */ 17 | 18 | /* 19 | base00 #ffffff Default Background 20 | base01 #c0c0c0 Lighter Background (Used for status bars, line number and folding marks) 21 | base02 #c0c0c0 Selection Background 22 | base03 #808080 Comments, Invisibles, Line Highlighting 23 | base04 #808080 Dark Foreground (Used for status bars) 24 | base05 #404040 Default Foreground, Caret, Delimiters, Operators 25 | base06 #404040 Light Foreground (Not often used) 26 | base07 #5e5e5e Light Background (Not often used) 27 | base08 #c41a15 Variables, XML Tags, Markup Link Text, Markup Lists, Diff Deleted 28 | base09 #eb8500 Integers, Boolean, Constants, XML Attributes, Markup Link Url 29 | base0A #826b28 Classes, Markup Bold, Search Text Background 30 | base0B #007400 Strings, Inherited Class, Markup Code, Diff Inserted 31 | base0C #318495 Support, Regular Expressions, Escape Characters, Markup Quotes 32 | base0D #0000ff Functions, Methods, Attribute IDs, Headings 33 | base0E #a90d91 Keywords, Storage, Selector, Markup Italic, Diff Changed 34 | base0F #826b28 Deprecated, Opening/Closing Embedded Language Tags, e.g. 35 | */ 36 | 37 | pre code.hljs { 38 | display: block; 39 | overflow-x: auto; 40 | padding: 1em; 41 | } 42 | 43 | code.hljs { 44 | padding: 3px 5px; 45 | } 46 | 47 | .hljs { 48 | color: #404040; 49 | background: #ffffff; 50 | } 51 | 52 | .hljs::selection, 53 | .hljs ::selection { 54 | background-color: #c0c0c0; 55 | color: #404040; 56 | } 57 | 58 | 59 | /* purposely do not highlight these things */ 60 | .hljs-formula, 61 | .hljs-params, 62 | .hljs-property 63 | {} 64 | 65 | /* base03 - #808080 - Comments, Invisibles, Line Highlighting */ 66 | .hljs-comment { 67 | color: #808080; 68 | } 69 | 70 | /* base04 - #808080 - Dark Foreground (Used for status bars) */ 71 | .hljs-tag { 72 | color: #808080; 73 | } 74 | 75 | /* base05 - #404040 - Default Foreground, Caret, Delimiters, Operators */ 76 | .hljs-subst, 77 | .hljs-punctuation, 78 | .hljs-operator { 79 | color: #404040; 80 | } 81 | 82 | .hljs-operator { 83 | opacity: 0.7; 84 | } 85 | 86 | /* base08 - Variables, XML Tags, Markup Link Text, Markup Lists, Diff Deleted */ 87 | .hljs-bullet, 88 | .hljs-variable, 89 | .hljs-template-variable, 90 | .hljs-selector-tag, 91 | .hljs-name, 92 | .hljs-deletion { 93 | color: #c41a15; 94 | } 95 | 96 | /* base09 - Integers, Boolean, Constants, XML Attributes, Markup Link Url */ 97 | .hljs-symbol, 98 | .hljs-number, 99 | .hljs-link, 100 | .hljs-attr, 101 | .hljs-variable.constant_, 102 | .hljs-literal { 103 | color: #eb8500; 104 | } 105 | 106 | /* base0A - Classes, Markup Bold, Search Text Background */ 107 | .hljs-title, 108 | .hljs-class .hljs-title, 109 | .hljs-title.class_ 110 | { 111 | color: #826b28; 112 | } 113 | 114 | .hljs-strong { 115 | font-weight:bold; 116 | color: #826b28; 117 | } 118 | 119 | /* base0B - Strings, Inherited Class, Markup Code, Diff Inserted */ 120 | .hljs-code, 121 | .hljs-addition, 122 | .hljs-title.class_.inherited__, 123 | .hljs-string { 124 | color: #007400; 125 | } 126 | 127 | /* base0C - Support, Regular Expressions, Escape Characters, Markup Quotes */ 128 | .hljs-built_in, 129 | .hljs-doctag, /* guessing */ 130 | .hljs-quote, 131 | .hljs-keyword.hljs-atrule, 132 | .hljs-regexp { 133 | color: #318495; 134 | } 135 | 136 | /* base0D - Functions, Methods, Attribute IDs, Headings */ 137 | .hljs-function .hljs-title, 138 | .hljs-attribute, 139 | .ruby .hljs-property, 140 | .hljs-title.function_, 141 | .hljs-section { 142 | color: #0000ff; 143 | } 144 | 145 | /* base0E - Keywords, Storage, Selector, Markup Italic, Diff Changed */ 146 | .hljs-type, 147 | /* .hljs-selector-id, */ 148 | /* .hljs-selector-class, */ 149 | /* .hljs-selector-attr, */ 150 | /* .hljs-selector-pseudo, */ 151 | .hljs-template-tag, 152 | .diff .hljs-meta, 153 | .hljs-keyword { 154 | color: #a90d91; 155 | } 156 | .hljs-emphasis { 157 | color: #a90d91; 158 | font-style: italic; 159 | } 160 | 161 | /* base0F - Deprecated, Opening/Closing Embedded Language Tags, e.g. */ 162 | .hljs-meta, 163 | /* 164 | prevent top level .keyword and .string scopes 165 | from leaking into meta by accident 166 | */ 167 | .hljs-meta .hljs-keyword, 168 | .hljs-meta .hljs-string 169 | { 170 | color: #826b28; 171 | } 172 | 173 | .hljs-meta .hljs-keyword, 174 | /* for v10 compatible themes */ 175 | .hljs-meta-keyword { 176 | font-weight: bold; 177 | } 178 | -------------------------------------------------------------------------------- /site/src/jsMain/resources/public/cupertino.css: -------------------------------------------------------------------------------- 1 | /*! 2 | Theme: Cupertino 3 | Author: Defman21 4 | License: ~ MIT (or more permissive) [via base16-schemes-source] 5 | Maintainer: @highlightjs/core-team 6 | Version: 2021.09.0 7 | */ 8 | 9 | /* 10 | WARNING: DO NOT EDIT THIS FILE DIRECTLY. 11 | 12 | This theme file was auto-generated from the Base16 scheme cupertino 13 | by the Highlight.js Base16 template builder. 14 | 15 | - https://github.com/highlightjs/base16-highlightjs 16 | */ 17 | 18 | /* 19 | base00 #ffffff Default Background 20 | base01 #c0c0c0 Lighter Background (Used for status bars, line number and folding marks) 21 | base02 #c0c0c0 Selection Background 22 | base03 #808080 Comments, Invisibles, Line Highlighting 23 | base04 #808080 Dark Foreground (Used for status bars) 24 | base05 #404040 Default Foreground, Caret, Delimiters, Operators 25 | base06 #404040 Light Foreground (Not often used) 26 | base07 #5e5e5e Light Background (Not often used) 27 | base08 #c41a15 Variables, XML Tags, Markup Link Text, Markup Lists, Diff Deleted 28 | base09 #eb8500 Integers, Boolean, Constants, XML Attributes, Markup Link Url 29 | base0A #826b28 Classes, Markup Bold, Search Text Background 30 | base0B #007400 Strings, Inherited Class, Markup Code, Diff Inserted 31 | base0C #318495 Support, Regular Expressions, Escape Characters, Markup Quotes 32 | base0D #0000ff Functions, Methods, Attribute IDs, Headings 33 | base0E #a90d91 Keywords, Storage, Selector, Markup Italic, Diff Changed 34 | base0F #826b28 Deprecated, Opening/Closing Embedded Language Tags, e.g. 35 | */ 36 | 37 | pre code.hljs { 38 | display: block; 39 | overflow-x: auto; 40 | padding: 1em; 41 | } 42 | 43 | code.hljs { 44 | padding: 3px 5px; 45 | } 46 | 47 | .hljs { 48 | color: #404040; 49 | background: #ffffff; 50 | } 51 | 52 | .hljs::selection, 53 | .hljs ::selection { 54 | background-color: #c0c0c0; 55 | color: #404040; 56 | } 57 | 58 | 59 | /* purposely do not highlight these things */ 60 | .hljs-formula, 61 | .hljs-params, 62 | .hljs-property 63 | {} 64 | 65 | /* base03 - #808080 - Comments, Invisibles, Line Highlighting */ 66 | .hljs-comment { 67 | color: #808080; 68 | } 69 | 70 | /* base04 - #808080 - Dark Foreground (Used for status bars) */ 71 | .hljs-tag { 72 | color: #808080; 73 | } 74 | 75 | /* base05 - #404040 - Default Foreground, Caret, Delimiters, Operators */ 76 | .hljs-subst, 77 | .hljs-punctuation, 78 | .hljs-operator { 79 | color: #404040; 80 | } 81 | 82 | .hljs-operator { 83 | opacity: 0.7; 84 | } 85 | 86 | /* base08 - Variables, XML Tags, Markup Link Text, Markup Lists, Diff Deleted */ 87 | .hljs-bullet, 88 | .hljs-variable, 89 | .hljs-template-variable, 90 | .hljs-selector-tag, 91 | .hljs-name, 92 | .hljs-deletion { 93 | color: #c41a15; 94 | } 95 | 96 | /* base09 - Integers, Boolean, Constants, XML Attributes, Markup Link Url */ 97 | .hljs-symbol, 98 | .hljs-number, 99 | .hljs-link, 100 | .hljs-attr, 101 | .hljs-variable.constant_, 102 | .hljs-literal { 103 | color: #eb8500; 104 | } 105 | 106 | /* base0A - Classes, Markup Bold, Search Text Background */ 107 | .hljs-title, 108 | .hljs-class .hljs-title, 109 | .hljs-title.class_ 110 | { 111 | color: #826b28; 112 | } 113 | 114 | .hljs-strong { 115 | font-weight:bold; 116 | color: #826b28; 117 | } 118 | 119 | /* base0B - Strings, Inherited Class, Markup Code, Diff Inserted */ 120 | .hljs-code, 121 | .hljs-addition, 122 | .hljs-title.class_.inherited__, 123 | .hljs-string { 124 | color: #007400; 125 | } 126 | 127 | /* base0C - Support, Regular Expressions, Escape Characters, Markup Quotes */ 128 | .hljs-built_in, 129 | .hljs-doctag, /* guessing */ 130 | .hljs-quote, 131 | .hljs-keyword.hljs-atrule, 132 | .hljs-regexp { 133 | color: #318495; 134 | } 135 | 136 | /* base0D - Functions, Methods, Attribute IDs, Headings */ 137 | .hljs-function .hljs-title, 138 | .hljs-attribute, 139 | .ruby .hljs-property, 140 | .hljs-title.function_, 141 | .hljs-section { 142 | color: #0000ff; 143 | } 144 | 145 | /* base0E - Keywords, Storage, Selector, Markup Italic, Diff Changed */ 146 | .hljs-type, 147 | /* .hljs-selector-id, */ 148 | /* .hljs-selector-class, */ 149 | /* .hljs-selector-attr, */ 150 | /* .hljs-selector-pseudo, */ 151 | .hljs-template-tag, 152 | .diff .hljs-meta, 153 | .hljs-keyword { 154 | color: #a90d91; 155 | } 156 | .hljs-emphasis { 157 | color: #a90d91; 158 | font-style: italic; 159 | } 160 | 161 | /* base0F - Deprecated, Opening/Closing Embedded Language Tags, e.g. */ 162 | .hljs-meta, 163 | /* 164 | prevent top level .keyword and .string scopes 165 | from leaking into meta by accident 166 | */ 167 | .hljs-meta .hljs-keyword, 168 | .hljs-meta .hljs-string 169 | { 170 | color: #826b28; 171 | } 172 | 173 | .hljs-meta .hljs-keyword, 174 | /* for v10 compatible themes */ 175 | .hljs-meta-keyword { 176 | font-weight: bold; 177 | } 178 | -------------------------------------------------------------------------------- /site/.kobweb/site/google-light.css: -------------------------------------------------------------------------------- 1 | /*! 2 | Theme: Google Light 3 | Author: Seth Wright (http://sethawright.com) 4 | License: ~ MIT (or more permissive) [via base16-schemes-source] 5 | Maintainer: @highlightjs/core-team 6 | Version: 2021.09.0 7 | */ 8 | 9 | /* 10 | WARNING: DO NOT EDIT THIS FILE DIRECTLY. 11 | 12 | This theme file was auto-generated from the Base16 scheme google-light 13 | by the Highlight.js Base16 template builder. 14 | 15 | - https://github.com/highlightjs/base16-highlightjs 16 | */ 17 | 18 | /* 19 | base00 #ffffff Default Background 20 | base01 #e0e0e0 Lighter Background (Used for status bars, line number and folding marks) 21 | base02 #c5c8c6 Selection Background 22 | base03 #b4b7b4 Comments, Invisibles, Line Highlighting 23 | base04 #969896 Dark Foreground (Used for status bars) 24 | base05 #373b41 Default Foreground, Caret, Delimiters, Operators 25 | base06 #282a2e Light Foreground (Not often used) 26 | base07 #1d1f21 Light Background (Not often used) 27 | base08 #CC342B Variables, XML Tags, Markup Link Text, Markup Lists, Diff Deleted 28 | base09 #F96A38 Integers, Boolean, Constants, XML Attributes, Markup Link Url 29 | base0A #FBA922 Classes, Markup Bold, Search Text Background 30 | base0B #198844 Strings, Inherited Class, Markup Code, Diff Inserted 31 | base0C #3971ED Support, Regular Expressions, Escape Characters, Markup Quotes 32 | base0D #3971ED Functions, Methods, Attribute IDs, Headings 33 | base0E #A36AC7 Keywords, Storage, Selector, Markup Italic, Diff Changed 34 | base0F #3971ED Deprecated, Opening/Closing Embedded Language Tags, e.g. 35 | */ 36 | 37 | pre code.hljs { 38 | display: block; 39 | overflow-x: auto; 40 | padding: 1em; 41 | } 42 | 43 | code.hljs { 44 | padding: 3px 5px; 45 | } 46 | 47 | .hljs { 48 | color: #373b41; 49 | background: #ffffff; 50 | } 51 | 52 | .hljs::selection, 53 | .hljs ::selection { 54 | background-color: #c5c8c6; 55 | color: #373b41; 56 | } 57 | 58 | 59 | /* purposely do not highlight these things */ 60 | .hljs-formula, 61 | .hljs-params, 62 | .hljs-property 63 | {} 64 | 65 | /* base03 - #b4b7b4 - Comments, Invisibles, Line Highlighting */ 66 | .hljs-comment { 67 | color: #b4b7b4; 68 | } 69 | 70 | /* base04 - #969896 - Dark Foreground (Used for status bars) */ 71 | .hljs-tag { 72 | color: #969896; 73 | } 74 | 75 | /* base05 - #373b41 - Default Foreground, Caret, Delimiters, Operators */ 76 | .hljs-subst, 77 | .hljs-punctuation, 78 | .hljs-operator { 79 | color: #373b41; 80 | } 81 | 82 | .hljs-operator { 83 | opacity: 0.7; 84 | } 85 | 86 | /* base08 - Variables, XML Tags, Markup Link Text, Markup Lists, Diff Deleted */ 87 | .hljs-bullet, 88 | .hljs-variable, 89 | .hljs-template-variable, 90 | .hljs-selector-tag, 91 | .hljs-name, 92 | .hljs-deletion { 93 | color: #CC342B; 94 | } 95 | 96 | /* base09 - Integers, Boolean, Constants, XML Attributes, Markup Link Url */ 97 | .hljs-symbol, 98 | .hljs-number, 99 | .hljs-link, 100 | .hljs-attr, 101 | .hljs-variable.constant_, 102 | .hljs-literal { 103 | color: #F96A38; 104 | } 105 | 106 | /* base0A - Classes, Markup Bold, Search Text Background */ 107 | .hljs-title, 108 | .hljs-class .hljs-title, 109 | .hljs-title.class_ 110 | { 111 | color: #FBA922; 112 | } 113 | 114 | .hljs-strong { 115 | font-weight:bold; 116 | color: #FBA922; 117 | } 118 | 119 | /* base0B - Strings, Inherited Class, Markup Code, Diff Inserted */ 120 | .hljs-code, 121 | .hljs-addition, 122 | .hljs-title.class_.inherited__, 123 | .hljs-string { 124 | color: #198844; 125 | } 126 | 127 | /* base0C - Support, Regular Expressions, Escape Characters, Markup Quotes */ 128 | .hljs-built_in, 129 | .hljs-doctag, /* guessing */ 130 | .hljs-quote, 131 | .hljs-keyword.hljs-atrule, 132 | .hljs-regexp { 133 | color: #3971ED; 134 | } 135 | 136 | /* base0D - Functions, Methods, Attribute IDs, Headings */ 137 | .hljs-function .hljs-title, 138 | .hljs-attribute, 139 | .ruby .hljs-property, 140 | .hljs-title.function_, 141 | .hljs-section { 142 | color: #3971ED; 143 | } 144 | 145 | /* base0E - Keywords, Storage, Selector, Markup Italic, Diff Changed */ 146 | .hljs-type, 147 | /* .hljs-selector-id, */ 148 | /* .hljs-selector-class, */ 149 | /* .hljs-selector-attr, */ 150 | /* .hljs-selector-pseudo, */ 151 | .hljs-template-tag, 152 | .diff .hljs-meta, 153 | .hljs-keyword { 154 | color: #A36AC7; 155 | } 156 | .hljs-emphasis { 157 | color: #A36AC7; 158 | font-style: italic; 159 | } 160 | 161 | /* base0F - Deprecated, Opening/Closing Embedded Language Tags, e.g. */ 162 | .hljs-meta, 163 | /* 164 | prevent top level .keyword and .string scopes 165 | from leaking into meta by accident 166 | */ 167 | .hljs-meta .hljs-keyword, 168 | .hljs-meta .hljs-string 169 | { 170 | color: #3971ED; 171 | } 172 | 173 | .hljs-meta .hljs-keyword, 174 | /* for v10 compatible themes */ 175 | .hljs-meta-keyword { 176 | font-weight: bold; 177 | } 178 | -------------------------------------------------------------------------------- /site/src/jsMain/resources/public/google-light.css: -------------------------------------------------------------------------------- 1 | /*! 2 | Theme: Google Light 3 | Author: Seth Wright (http://sethawright.com) 4 | License: ~ MIT (or more permissive) [via base16-schemes-source] 5 | Maintainer: @highlightjs/core-team 6 | Version: 2021.09.0 7 | */ 8 | 9 | /* 10 | WARNING: DO NOT EDIT THIS FILE DIRECTLY. 11 | 12 | This theme file was auto-generated from the Base16 scheme google-light 13 | by the Highlight.js Base16 template builder. 14 | 15 | - https://github.com/highlightjs/base16-highlightjs 16 | */ 17 | 18 | /* 19 | base00 #ffffff Default Background 20 | base01 #e0e0e0 Lighter Background (Used for status bars, line number and folding marks) 21 | base02 #c5c8c6 Selection Background 22 | base03 #b4b7b4 Comments, Invisibles, Line Highlighting 23 | base04 #969896 Dark Foreground (Used for status bars) 24 | base05 #373b41 Default Foreground, Caret, Delimiters, Operators 25 | base06 #282a2e Light Foreground (Not often used) 26 | base07 #1d1f21 Light Background (Not often used) 27 | base08 #CC342B Variables, XML Tags, Markup Link Text, Markup Lists, Diff Deleted 28 | base09 #F96A38 Integers, Boolean, Constants, XML Attributes, Markup Link Url 29 | base0A #FBA922 Classes, Markup Bold, Search Text Background 30 | base0B #198844 Strings, Inherited Class, Markup Code, Diff Inserted 31 | base0C #3971ED Support, Regular Expressions, Escape Characters, Markup Quotes 32 | base0D #3971ED Functions, Methods, Attribute IDs, Headings 33 | base0E #A36AC7 Keywords, Storage, Selector, Markup Italic, Diff Changed 34 | base0F #3971ED Deprecated, Opening/Closing Embedded Language Tags, e.g. 35 | */ 36 | 37 | pre code.hljs { 38 | display: block; 39 | overflow-x: auto; 40 | padding: 1em; 41 | } 42 | 43 | code.hljs { 44 | padding: 3px 5px; 45 | } 46 | 47 | .hljs { 48 | color: #373b41; 49 | background: #ffffff; 50 | } 51 | 52 | .hljs::selection, 53 | .hljs ::selection { 54 | background-color: #c5c8c6; 55 | color: #373b41; 56 | } 57 | 58 | 59 | /* purposely do not highlight these things */ 60 | .hljs-formula, 61 | .hljs-params, 62 | .hljs-property 63 | {} 64 | 65 | /* base03 - #b4b7b4 - Comments, Invisibles, Line Highlighting */ 66 | .hljs-comment { 67 | color: #b4b7b4; 68 | } 69 | 70 | /* base04 - #969896 - Dark Foreground (Used for status bars) */ 71 | .hljs-tag { 72 | color: #969896; 73 | } 74 | 75 | /* base05 - #373b41 - Default Foreground, Caret, Delimiters, Operators */ 76 | .hljs-subst, 77 | .hljs-punctuation, 78 | .hljs-operator { 79 | color: #373b41; 80 | } 81 | 82 | .hljs-operator { 83 | opacity: 0.7; 84 | } 85 | 86 | /* base08 - Variables, XML Tags, Markup Link Text, Markup Lists, Diff Deleted */ 87 | .hljs-bullet, 88 | .hljs-variable, 89 | .hljs-template-variable, 90 | .hljs-selector-tag, 91 | .hljs-name, 92 | .hljs-deletion { 93 | color: #CC342B; 94 | } 95 | 96 | /* base09 - Integers, Boolean, Constants, XML Attributes, Markup Link Url */ 97 | .hljs-symbol, 98 | .hljs-number, 99 | .hljs-link, 100 | .hljs-attr, 101 | .hljs-variable.constant_, 102 | .hljs-literal { 103 | color: #F96A38; 104 | } 105 | 106 | /* base0A - Classes, Markup Bold, Search Text Background */ 107 | .hljs-title, 108 | .hljs-class .hljs-title, 109 | .hljs-title.class_ 110 | { 111 | color: #FBA922; 112 | } 113 | 114 | .hljs-strong { 115 | font-weight:bold; 116 | color: #FBA922; 117 | } 118 | 119 | /* base0B - Strings, Inherited Class, Markup Code, Diff Inserted */ 120 | .hljs-code, 121 | .hljs-addition, 122 | .hljs-title.class_.inherited__, 123 | .hljs-string { 124 | color: #198844; 125 | } 126 | 127 | /* base0C - Support, Regular Expressions, Escape Characters, Markup Quotes */ 128 | .hljs-built_in, 129 | .hljs-doctag, /* guessing */ 130 | .hljs-quote, 131 | .hljs-keyword.hljs-atrule, 132 | .hljs-regexp { 133 | color: #3971ED; 134 | } 135 | 136 | /* base0D - Functions, Methods, Attribute IDs, Headings */ 137 | .hljs-function .hljs-title, 138 | .hljs-attribute, 139 | .ruby .hljs-property, 140 | .hljs-title.function_, 141 | .hljs-section { 142 | color: #3971ED; 143 | } 144 | 145 | /* base0E - Keywords, Storage, Selector, Markup Italic, Diff Changed */ 146 | .hljs-type, 147 | /* .hljs-selector-id, */ 148 | /* .hljs-selector-class, */ 149 | /* .hljs-selector-attr, */ 150 | /* .hljs-selector-pseudo, */ 151 | .hljs-template-tag, 152 | .diff .hljs-meta, 153 | .hljs-keyword { 154 | color: #A36AC7; 155 | } 156 | .hljs-emphasis { 157 | color: #A36AC7; 158 | font-style: italic; 159 | } 160 | 161 | /* base0F - Deprecated, Opening/Closing Embedded Language Tags, e.g. */ 162 | .hljs-meta, 163 | /* 164 | prevent top level .keyword and .string scopes 165 | from leaking into meta by accident 166 | */ 167 | .hljs-meta .hljs-keyword, 168 | .hljs-meta .hljs-string 169 | { 170 | color: #3971ED; 171 | } 172 | 173 | .hljs-meta .hljs-keyword, 174 | /* for v10 compatible themes */ 175 | .hljs-meta-keyword { 176 | font-weight: bold; 177 | } 178 | -------------------------------------------------------------------------------- /site/src/jsMain/kotlin/com/stevdza/san/components/Editor.kt: -------------------------------------------------------------------------------- 1 | package com.stevdza.san.components 2 | 3 | import androidx.compose.runtime.* 4 | import com.stevdza.san.model.EditorTheme 5 | import com.stevdza.san.model.Theme 6 | import com.stevdza.san.util.Res 7 | import com.varabyte.kobweb.compose.css.Overflow 8 | import com.varabyte.kobweb.compose.foundation.layout.Box 9 | import com.varabyte.kobweb.compose.foundation.layout.Column 10 | import com.varabyte.kobweb.compose.foundation.layout.Row 11 | import com.varabyte.kobweb.compose.ui.Modifier 12 | import com.varabyte.kobweb.compose.ui.graphics.Colors 13 | import com.varabyte.kobweb.compose.ui.modifiers.* 14 | import com.varabyte.kobweb.compose.ui.styleModifier 15 | import com.varabyte.kobweb.compose.ui.toAttrs 16 | import com.varabyte.kobweb.silk.components.layout.Surface 17 | import com.varabyte.kobweb.silk.components.style.breakpoint.Breakpoint 18 | import com.varabyte.kobweb.silk.theme.breakpoint.rememberBreakpoint 19 | import com.varabyte.kobweb.silk.theme.shapes.Circle 20 | import com.varabyte.kobweb.silk.theme.shapes.clip 21 | import kotlinx.browser.document 22 | import org.jetbrains.compose.web.css.* 23 | import org.jetbrains.compose.web.dom.Code 24 | import org.jetbrains.compose.web.dom.Pre 25 | import org.jetbrains.compose.web.dom.Text 26 | 27 | @Composable 28 | fun Editor() { 29 | var padding by remember { mutableStateOf(50) } 30 | var fontSize by remember { mutableStateOf(20) } 31 | var lineHeight by remember { mutableStateOf(20) } 32 | var editorTheme by remember { mutableStateOf(EditorTheme.RoyalBlue) } 33 | val breakpoint by rememberBreakpoint() 34 | 35 | ControlsView( 36 | defaultPadding = padding, 37 | defaultFontSize = fontSize, 38 | defaultLineHeight = lineHeight, 39 | defaultTheme = editorTheme.name, 40 | onPaddingChanged = { value -> 41 | padding = if (value in 10..99) 42 | value else 100 43 | }, 44 | onFontSizeChanged = { value -> 45 | fontSize = if (value in 14..29) 46 | value else 30 47 | }, 48 | onLineHeightChanged = { value -> 49 | lineHeight = if (value in 16..39) 50 | value else 40 51 | }, 52 | onThemeChanged = { editorTheme = it }, 53 | ) 54 | Column( 55 | modifier = Modifier 56 | .id(Res.Id.editor) 57 | .padding(all = padding.px) 58 | .backgroundColor(editorTheme.color) 59 | ) { 60 | EditorHeader() 61 | EditorBody( 62 | fontSize = fontSize, 63 | lineHeight = lineHeight, 64 | padding = padding, 65 | breakpoint = breakpoint 66 | ) 67 | } 68 | } 69 | 70 | @Composable 71 | private fun EditorHeader() { 72 | Surface( 73 | modifier = Modifier 74 | .fillMaxWidth() 75 | .padding(all = 20.px) 76 | .backgroundColor(Colors.White) 77 | .borderRadius( 78 | topLeft = 6.px, 79 | topRight = 6.px, 80 | bottomLeft = 0.px, 81 | bottomRight = 0.px 82 | ) 83 | ) { 84 | Row { 85 | repeat(3) { index -> 86 | Box( 87 | modifier = Modifier 88 | .backgroundColor(Theme.values()[index].color) 89 | .size(14.px) 90 | .clip(Circle()) 91 | .margin(right = if (index != 2) 10.px else 0.px), 92 | ) 93 | } 94 | } 95 | } 96 | } 97 | 98 | @Composable 99 | private fun EditorBody( 100 | fontSize: Int, 101 | lineHeight: Int, 102 | padding: Int, 103 | breakpoint: Breakpoint 104 | ) { 105 | Pre(attrs = Modifier 106 | .width(if (breakpoint <= Breakpoint.MD) 100.vw - padding.px * 2 else 740.px) 107 | .height(350.px) 108 | .minWidth(112.px) 109 | .padding(all = 20.px) 110 | .margin(bottom = 0.px) 111 | .backgroundColor(Colors.White) 112 | .borderRadius( 113 | bottomLeft = 6.px, 114 | bottomRight = 6.px, 115 | topLeft = 0.px, 116 | topRight = 0.px 117 | ) 118 | .border(width = 0.px) 119 | .outline(style = LineStyle.None) 120 | .overflow(overflow = Overflow.Hidden) 121 | .styleModifier { 122 | property("resize", "both") 123 | } 124 | .toAttrs() 125 | ) { 126 | Code(attrs = Modifier 127 | .contentEditable(true) 128 | .spellCheck(false) 129 | .classNames("language-kotlin") 130 | .outline(style = LineStyle.None) 131 | .styleModifier { 132 | fontFamily("Roboto", "sans-serif") 133 | fontSize(fontSize.px) 134 | lineHeight(lineHeight.px) 135 | } 136 | .toAttrs { 137 | onPaste { 138 | it.preventDefault() 139 | val text = it.clipboardData?.getData("Text") 140 | if (text != null) { 141 | document.execCommand( 142 | "insertHtml", 143 | false, 144 | "" + text + "" 145 | ) 146 | } 147 | } 148 | } 149 | ) { 150 | Text( 151 | """ 152 | private val credits = "Kobweb Framework" 153 | """.trimIndent() 154 | ) 155 | } 156 | } 157 | } -------------------------------------------------------------------------------- /site/.kobweb/site/FileSaver.js: -------------------------------------------------------------------------------- 1 | /* 2 | * FileSaver.js 3 | * A saveAs() FileSaver implementation. 4 | * 5 | * By Eli Grey, http://eligrey.com 6 | * 7 | * License : https://github.com/eligrey/FileSaver.js/blob/master/LICENSE.md (MIT) 8 | * source : http://purl.eligrey.com/github/FileSaver.js 9 | */ 10 | 11 | // The one and only way of getting global scope in all environments 12 | // https://stackoverflow.com/q/3277182/1008999 13 | var _global = typeof window === 'object' && window.window === window 14 | ? window : typeof self === 'object' && self.self === self 15 | ? self : typeof global === 'object' && global.global === global 16 | ? global 17 | : this 18 | 19 | function bom (blob, opts) { 20 | if (typeof opts === 'undefined') opts = { autoBom: false } 21 | else if (typeof opts !== 'object') { 22 | console.warn('Deprecated: Expected third argument to be a object') 23 | opts = { autoBom: !opts } 24 | } 25 | 26 | // prepend BOM for UTF-8 XML and text/* types (including HTML) 27 | // note: your browser will automatically convert UTF-16 U+FEFF to EF BB BF 28 | if (opts.autoBom && /^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(blob.type)) { 29 | return new Blob([String.fromCharCode(0xFEFF), blob], { type: blob.type }) 30 | } 31 | return blob 32 | } 33 | 34 | function download (url, name, opts) { 35 | var xhr = new XMLHttpRequest() 36 | xhr.open('GET', url) 37 | xhr.responseType = 'blob' 38 | xhr.onload = function () { 39 | saveAs(xhr.response, name, opts) 40 | } 41 | xhr.onerror = function () { 42 | console.error('could not download file') 43 | } 44 | xhr.send() 45 | } 46 | 47 | function corsEnabled (url) { 48 | var xhr = new XMLHttpRequest() 49 | // use sync to avoid popup blocker 50 | xhr.open('HEAD', url, false) 51 | try { 52 | xhr.send() 53 | } catch (e) {} 54 | return xhr.status >= 200 && xhr.status <= 299 55 | } 56 | 57 | // `a.click()` doesn't work for all browsers (#465) 58 | function click (node) { 59 | try { 60 | node.dispatchEvent(new MouseEvent('click')) 61 | } catch (e) { 62 | var evt = document.createEvent('MouseEvents') 63 | evt.initMouseEvent('click', true, true, window, 0, 0, 0, 80, 64 | 20, false, false, false, false, 0, null) 65 | node.dispatchEvent(evt) 66 | } 67 | } 68 | 69 | // Detect WebView inside a native macOS app by ruling out all browsers 70 | // We just need to check for 'Safari' because all other browsers (besides Firefox) include that too 71 | // https://www.whatismybrowser.com/guides/the-latest-user-agent/macos 72 | var isMacOSWebView = _global.navigator && /Macintosh/.test(navigator.userAgent) && /AppleWebKit/.test(navigator.userAgent) && !/Safari/.test(navigator.userAgent) 73 | 74 | var saveAs = _global.saveAs || ( 75 | // probably in some web worker 76 | (typeof window !== 'object' || window !== _global) 77 | ? function saveAs () { /* noop */ } 78 | 79 | // Use download attribute first if possible (#193 Lumia mobile) unless this is a macOS WebView 80 | : ('download' in HTMLAnchorElement.prototype && !isMacOSWebView) 81 | ? function saveAs (blob, name, opts) { 82 | var URL = _global.URL || _global.webkitURL 83 | // Namespace is used to prevent conflict w/ Chrome Poper Blocker extension (Issue #561) 84 | var a = document.createElementNS('http://www.w3.org/1999/xhtml', 'a') 85 | name = name || blob.name || 'download' 86 | 87 | a.download = name 88 | a.rel = 'noopener' // tabnabbing 89 | 90 | // TODO: detect chrome extensions & packaged apps 91 | // a.target = '_blank' 92 | 93 | if (typeof blob === 'string') { 94 | // Support regular links 95 | a.href = blob 96 | if (a.origin !== location.origin) { 97 | corsEnabled(a.href) 98 | ? download(blob, name, opts) 99 | : click(a, a.target = '_blank') 100 | } else { 101 | click(a) 102 | } 103 | } else { 104 | // Support blobs 105 | a.href = URL.createObjectURL(blob) 106 | setTimeout(function () { URL.revokeObjectURL(a.href) }, 4E4) // 40s 107 | setTimeout(function () { click(a) }, 0) 108 | } 109 | } 110 | 111 | // Use msSaveOrOpenBlob as a second approach 112 | : 'msSaveOrOpenBlob' in navigator 113 | ? function saveAs (blob, name, opts) { 114 | name = name || blob.name || 'download' 115 | 116 | if (typeof blob === 'string') { 117 | if (corsEnabled(blob)) { 118 | download(blob, name, opts) 119 | } else { 120 | var a = document.createElement('a') 121 | a.href = blob 122 | a.target = '_blank' 123 | setTimeout(function () { click(a) }) 124 | } 125 | } else { 126 | navigator.msSaveOrOpenBlob(bom(blob, opts), name) 127 | } 128 | } 129 | 130 | // Fallback to using FileReader and a popup 131 | : function saveAs (blob, name, opts, popup) { 132 | // Open a popup immediately do go around popup blocker 133 | // Mostly only available on user interaction and the fileReader is async so... 134 | popup = popup || open('', '_blank') 135 | if (popup) { 136 | popup.document.title = 137 | popup.document.body.innerText = 'downloading...' 138 | } 139 | 140 | if (typeof blob === 'string') return download(blob, name, opts) 141 | 142 | var force = blob.type === 'application/octet-stream' 143 | var isSafari = /constructor/i.test(_global.HTMLElement) || _global.safari 144 | var isChromeIOS = /CriOS\/[\d]+/.test(navigator.userAgent) 145 | 146 | if ((isChromeIOS || (force && isSafari) || isMacOSWebView) && typeof FileReader !== 'undefined') { 147 | // Safari doesn't allow downloading of blob URLs 148 | var reader = new FileReader() 149 | reader.onloadend = function () { 150 | var url = reader.result 151 | url = isChromeIOS ? url : url.replace(/^data:[^;]*;/, 'data:attachment/file;') 152 | if (popup) popup.location.href = url 153 | else location = url 154 | popup = null // reverse-tabnabbing #460 155 | } 156 | reader.readAsDataURL(blob) 157 | } else { 158 | var URL = _global.URL || _global.webkitURL 159 | var url = URL.createObjectURL(blob) 160 | if (popup) popup.location = url 161 | else location.href = url 162 | popup = null // reverse-tabnabbing #460 163 | setTimeout(function () { URL.revokeObjectURL(url) }, 4E4) // 40s 164 | } 165 | } 166 | ) 167 | 168 | _global.saveAs = saveAs.saveAs = saveAs 169 | 170 | if (typeof module !== 'undefined') { 171 | module.exports = saveAs; 172 | } 173 | -------------------------------------------------------------------------------- /site/src/jsMain/resources/public/FileSaver.js: -------------------------------------------------------------------------------- 1 | /* 2 | * FileSaver.js 3 | * A saveAs() FileSaver implementation. 4 | * 5 | * By Eli Grey, http://eligrey.com 6 | * 7 | * License : https://github.com/eligrey/FileSaver.js/blob/master/LICENSE.md (MIT) 8 | * source : http://purl.eligrey.com/github/FileSaver.js 9 | */ 10 | 11 | // The one and only way of getting global scope in all environments 12 | // https://stackoverflow.com/q/3277182/1008999 13 | var _global = typeof window === 'object' && window.window === window 14 | ? window : typeof self === 'object' && self.self === self 15 | ? self : typeof global === 'object' && global.global === global 16 | ? global 17 | : this 18 | 19 | function bom (blob, opts) { 20 | if (typeof opts === 'undefined') opts = { autoBom: false } 21 | else if (typeof opts !== 'object') { 22 | console.warn('Deprecated: Expected third argument to be a object') 23 | opts = { autoBom: !opts } 24 | } 25 | 26 | // prepend BOM for UTF-8 XML and text/* types (including HTML) 27 | // note: your browser will automatically convert UTF-16 U+FEFF to EF BB BF 28 | if (opts.autoBom && /^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(blob.type)) { 29 | return new Blob([String.fromCharCode(0xFEFF), blob], { type: blob.type }) 30 | } 31 | return blob 32 | } 33 | 34 | function download (url, name, opts) { 35 | var xhr = new XMLHttpRequest() 36 | xhr.open('GET', url) 37 | xhr.responseType = 'blob' 38 | xhr.onload = function () { 39 | saveAs(xhr.response, name, opts) 40 | } 41 | xhr.onerror = function () { 42 | console.error('could not download file') 43 | } 44 | xhr.send() 45 | } 46 | 47 | function corsEnabled (url) { 48 | var xhr = new XMLHttpRequest() 49 | // use sync to avoid popup blocker 50 | xhr.open('HEAD', url, false) 51 | try { 52 | xhr.send() 53 | } catch (e) {} 54 | return xhr.status >= 200 && xhr.status <= 299 55 | } 56 | 57 | // `a.click()` doesn't work for all browsers (#465) 58 | function click (node) { 59 | try { 60 | node.dispatchEvent(new MouseEvent('click')) 61 | } catch (e) { 62 | var evt = document.createEvent('MouseEvents') 63 | evt.initMouseEvent('click', true, true, window, 0, 0, 0, 80, 64 | 20, false, false, false, false, 0, null) 65 | node.dispatchEvent(evt) 66 | } 67 | } 68 | 69 | // Detect WebView inside a native macOS app by ruling out all browsers 70 | // We just need to check for 'Safari' because all other browsers (besides Firefox) include that too 71 | // https://www.whatismybrowser.com/guides/the-latest-user-agent/macos 72 | var isMacOSWebView = _global.navigator && /Macintosh/.test(navigator.userAgent) && /AppleWebKit/.test(navigator.userAgent) && !/Safari/.test(navigator.userAgent) 73 | 74 | var saveAs = _global.saveAs || ( 75 | // probably in some web worker 76 | (typeof window !== 'object' || window !== _global) 77 | ? function saveAs () { /* noop */ } 78 | 79 | // Use download attribute first if possible (#193 Lumia mobile) unless this is a macOS WebView 80 | : ('download' in HTMLAnchorElement.prototype && !isMacOSWebView) 81 | ? function saveAs (blob, name, opts) { 82 | var URL = _global.URL || _global.webkitURL 83 | // Namespace is used to prevent conflict w/ Chrome Poper Blocker extension (Issue #561) 84 | var a = document.createElementNS('http://www.w3.org/1999/xhtml', 'a') 85 | name = name || blob.name || 'download' 86 | 87 | a.download = name 88 | a.rel = 'noopener' // tabnabbing 89 | 90 | // TODO: detect chrome extensions & packaged apps 91 | // a.target = '_blank' 92 | 93 | if (typeof blob === 'string') { 94 | // Support regular links 95 | a.href = blob 96 | if (a.origin !== location.origin) { 97 | corsEnabled(a.href) 98 | ? download(blob, name, opts) 99 | : click(a, a.target = '_blank') 100 | } else { 101 | click(a) 102 | } 103 | } else { 104 | // Support blobs 105 | a.href = URL.createObjectURL(blob) 106 | setTimeout(function () { URL.revokeObjectURL(a.href) }, 4E4) // 40s 107 | setTimeout(function () { click(a) }, 0) 108 | } 109 | } 110 | 111 | // Use msSaveOrOpenBlob as a second approach 112 | : 'msSaveOrOpenBlob' in navigator 113 | ? function saveAs (blob, name, opts) { 114 | name = name || blob.name || 'download' 115 | 116 | if (typeof blob === 'string') { 117 | if (corsEnabled(blob)) { 118 | download(blob, name, opts) 119 | } else { 120 | var a = document.createElement('a') 121 | a.href = blob 122 | a.target = '_blank' 123 | setTimeout(function () { click(a) }) 124 | } 125 | } else { 126 | navigator.msSaveOrOpenBlob(bom(blob, opts), name) 127 | } 128 | } 129 | 130 | // Fallback to using FileReader and a popup 131 | : function saveAs (blob, name, opts, popup) { 132 | // Open a popup immediately do go around popup blocker 133 | // Mostly only available on user interaction and the fileReader is async so... 134 | popup = popup || open('', '_blank') 135 | if (popup) { 136 | popup.document.title = 137 | popup.document.body.innerText = 'downloading...' 138 | } 139 | 140 | if (typeof blob === 'string') return download(blob, name, opts) 141 | 142 | var force = blob.type === 'application/octet-stream' 143 | var isSafari = /constructor/i.test(_global.HTMLElement) || _global.safari 144 | var isChromeIOS = /CriOS\/[\d]+/.test(navigator.userAgent) 145 | 146 | if ((isChromeIOS || (force && isSafari) || isMacOSWebView) && typeof FileReader !== 'undefined') { 147 | // Safari doesn't allow downloading of blob URLs 148 | var reader = new FileReader() 149 | reader.onloadend = function () { 150 | var url = reader.result 151 | url = isChromeIOS ? url : url.replace(/^data:[^;]*;/, 'data:attachment/file;') 152 | if (popup) popup.location.href = url 153 | else location = url 154 | popup = null // reverse-tabnabbing #460 155 | } 156 | reader.readAsDataURL(blob) 157 | } else { 158 | var URL = _global.URL || _global.webkitURL 159 | var url = URL.createObjectURL(blob) 160 | if (popup) popup.location = url 161 | else location.href = url 162 | popup = null // reverse-tabnabbing #460 163 | setTimeout(function () { URL.revokeObjectURL(url) }, 4E4) // 40s 164 | } 165 | } 166 | ) 167 | 168 | _global.saveAs = saveAs.saveAs = saveAs 169 | 170 | if (typeof module !== 'undefined') { 171 | module.exports = saveAs; 172 | } 173 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MSYS* | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | 86 | # Determine the Java command to use to start the JVM. 87 | if [ -n "$JAVA_HOME" ] ; then 88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 89 | # IBM's JDK on AIX uses strange locations for the executables 90 | JAVACMD="$JAVA_HOME/jre/sh/java" 91 | else 92 | JAVACMD="$JAVA_HOME/bin/java" 93 | fi 94 | if [ ! -x "$JAVACMD" ] ; then 95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 96 | 97 | Please set the JAVA_HOME variable in your environment to match the 98 | location of your Java installation." 99 | fi 100 | else 101 | JAVACMD="java" 102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 103 | 104 | Please set the JAVA_HOME variable in your environment to match the 105 | location of your Java installation." 106 | fi 107 | 108 | # Increase the maximum file descriptors if we can. 109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 110 | MAX_FD_LIMIT=`ulimit -H -n` 111 | if [ $? -eq 0 ] ; then 112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 113 | MAX_FD="$MAX_FD_LIMIT" 114 | fi 115 | ulimit -n $MAX_FD 116 | if [ $? -ne 0 ] ; then 117 | warn "Could not set maximum file descriptor limit: $MAX_FD" 118 | fi 119 | else 120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 121 | fi 122 | fi 123 | 124 | # For Darwin, add options to specify how the application appears in the dock 125 | if $darwin; then 126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 127 | fi 128 | 129 | # For Cygwin or MSYS, switch paths to Windows format before running java 130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 133 | 134 | JAVACMD=`cygpath --unix "$JAVACMD"` 135 | 136 | # We build the pattern for arguments to be converted via cygpath 137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 138 | SEP="" 139 | for dir in $ROOTDIRSRAW ; do 140 | ROOTDIRS="$ROOTDIRS$SEP$dir" 141 | SEP="|" 142 | done 143 | OURCYGPATTERN="(^($ROOTDIRS))" 144 | # Add a user-defined pattern to the cygpath arguments 145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 147 | fi 148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 149 | i=0 150 | for arg in "$@" ; do 151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 153 | 154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 156 | else 157 | eval `echo args$i`="\"$arg\"" 158 | fi 159 | i=`expr $i + 1` 160 | done 161 | case $i in 162 | 0) set -- ;; 163 | 1) set -- "$args0" ;; 164 | 2) set -- "$args0" "$args1" ;; 165 | 3) set -- "$args0" "$args1" "$args2" ;; 166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 172 | esac 173 | fi 174 | 175 | # Escape application args 176 | save () { 177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 178 | echo " " 179 | } 180 | APP_ARGS=`save "$@"` 181 | 182 | # Collect all arguments for the java command, following the shell quoting and substitution rules 183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 184 | 185 | exec "$JAVACMD" "$@" 186 | -------------------------------------------------------------------------------- /site/src/jsMain/kotlin/com/stevdza/san/components/ControlsView.kt: -------------------------------------------------------------------------------- 1 | package com.stevdza.san.components 2 | 3 | import androidx.compose.runtime.Composable 4 | import com.stevdza.san.model.EditorTheme 5 | import com.stevdza.san.util.Res 6 | import com.varabyte.kobweb.compose.css.Cursor 7 | import com.varabyte.kobweb.compose.ui.Modifier 8 | import com.varabyte.kobweb.compose.ui.modifiers.* 9 | import com.varabyte.kobweb.compose.ui.toAttrs 10 | import com.varabyte.kobweb.silk.components.layout.SimpleGrid 11 | import com.varabyte.kobweb.silk.components.layout.numColumns 12 | import com.varabyte.kobweb.silk.components.text.SpanText 13 | import org.jetbrains.compose.web.attributes.* 14 | import org.jetbrains.compose.web.css.AlignSelf 15 | import org.jetbrains.compose.web.css.px 16 | import org.jetbrains.compose.web.dom.* 17 | 18 | @Composable 19 | fun ControlsView( 20 | defaultPadding: Int, 21 | defaultFontSize: Int, 22 | defaultLineHeight: Int, 23 | defaultTheme: String, 24 | onPaddingChanged: (Int) -> Unit, 25 | onFontSizeChanged: (Int) -> Unit, 26 | onLineHeightChanged: (Int) -> Unit, 27 | onThemeChanged: (EditorTheme) -> Unit, 28 | ) { 29 | SimpleGrid( 30 | modifier = Modifier.margin(bottom = 20.px), 31 | numColumns = numColumns(base = 2, md = 3) 32 | ) { 33 | Div( 34 | attrs = Modifier 35 | .classNames("form-floating", "my-1", "mx-1") 36 | .toAttrs() 37 | ) { 38 | Input( 39 | attrs = Modifier 40 | .id(Res.Id.fontSize) 41 | .classNames("form-control") 42 | .width(150.px) 43 | .toAttrs { 44 | name("font-size") 45 | placeholder("Font-Size") 46 | min("14") 47 | max("30") 48 | value(defaultFontSize) 49 | onInput { 50 | val inputValue = it.value?.toInt() ?: 14 51 | onFontSizeChanged(inputValue) 52 | } 53 | }, 54 | type = InputType.Number 55 | ) 56 | Label(forId = Res.Id.fontSize) { 57 | Text("Font-Size") 58 | } 59 | } 60 | Div( 61 | attrs = Modifier 62 | .classNames("form-floating", "my-1", "mx-1") 63 | .toAttrs() 64 | ) { 65 | Input( 66 | attrs = Modifier 67 | .id(Res.Id.lineHeight) 68 | .classNames("form-control") 69 | .width(150.px) 70 | .toAttrs { 71 | name("line-height") 72 | placeholder("Line-Height") 73 | min("16") 74 | max("40") 75 | value(defaultLineHeight) 76 | onInput { 77 | val inputValue = it.value?.toInt() ?: 20 78 | onLineHeightChanged(inputValue) 79 | } 80 | }, 81 | type = InputType.Number 82 | ) 83 | Label(forId = Res.Id.lineHeight) { 84 | Text("Line-Height") 85 | } 86 | } 87 | Div( 88 | attrs = Modifier 89 | .classNames("form-floating", "my-1", "mx-1") 90 | .toAttrs() 91 | ) { 92 | Input( 93 | attrs = Modifier 94 | .id(Res.Id.padding) 95 | .classNames("form-control") 96 | .width(150.px) 97 | .toAttrs { 98 | name("padding") 99 | placeholder("Padding") 100 | min("10") 101 | max("100") 102 | value(defaultPadding) 103 | onInput { 104 | val inputValue = it.value?.toInt() ?: 10 105 | onPaddingChanged(inputValue) 106 | } 107 | }, 108 | type = InputType.Number 109 | ) 110 | Label(forId = Res.Id.padding) { 111 | Text("Padding") 112 | } 113 | } 114 | DropDown( 115 | selectedTheme = defaultTheme, 116 | onThemeChanged = onThemeChanged 117 | ) 118 | Button(attrs = Modifier 119 | .classNames("btn", "btn-primary", "btn-md", "my-1", "mx-1") 120 | .onClick { 121 | js("hljs.highlightAll()") as Unit 122 | } 123 | .toAttrs() 124 | ) { 125 | Text("Apply Colors") 126 | } 127 | Button(attrs = Modifier 128 | .classNames("btn", "btn-primary", "btn-md", "my-1", "mx-1") 129 | .onClick { saveImage() } 130 | .toAttrs() 131 | ) { 132 | Text("Export") 133 | } 134 | } 135 | } 136 | 137 | @Composable 138 | private fun DropDown( 139 | selectedTheme: String, 140 | onThemeChanged: (EditorTheme) -> Unit 141 | ) { 142 | Div( 143 | attrs = Modifier 144 | .classNames("dropdown", "my-1", "mx-1") 145 | .fillMaxWidth() 146 | .alignSelf(AlignSelf.Center) 147 | .toAttrs() 148 | ) { 149 | Button( 150 | attrs = Modifier 151 | .classNames("btn", "btn-light", "dropdown-toggle", "btn-md") 152 | .toAttrs { 153 | attr("data-bs-toggle", "dropdown") 154 | } 155 | ) { 156 | Text(selectedTheme) 157 | } 158 | Ul( 159 | attrs = Modifier 160 | .classNames("dropdown-menu") 161 | .cursor(Cursor.Pointer) 162 | .toAttrs() 163 | ) { 164 | EditorTheme.values().forEach { theme -> 165 | Li( 166 | attrs = Modifier 167 | .classNames("dropdown-item") 168 | .onClick { onThemeChanged(theme) } 169 | .toAttrs() 170 | ) { 171 | SpanText(text = theme.name) 172 | } 173 | } 174 | } 175 | } 176 | } 177 | 178 | private fun saveImage() { 179 | js( 180 | "var node = document.getElementById('editor');\n" + 181 | "\n" + 182 | "var options = {\n" + 183 | " quality: 0.99,\n" + 184 | " width: node.clientWidth*4,\n" + 185 | " height: node.clientHeight*4,\n" + 186 | " style: {\n" + 187 | " 'transform': 'scale(4)',\n" + 188 | " 'transform-origin': 'top left',\n" + 189 | "}\n" + 190 | "};\n" + 191 | "\n" + 192 | "domtoimage.toBlob(node, options).then(function(blob) {\n" + 193 | " window.saveAs(blob, 'pretty-ko.png');\n" + 194 | "});" 195 | ) 196 | } -------------------------------------------------------------------------------- /site/.kobweb/site/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /site/src/jsMain/resources/public/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /site/.kobweb/site/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Pretty-KO 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |
21 | 22 | 23 | 24 |
25 |
26 |
27 | 28 |
29 |
30 | 31 |
32 |
33 | 34 |
35 |
36 | 37 |
38 | 47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
private val credits = "Kobweb Framework"
57 |
58 |
59 | 62 |
63 |
64 |
65 | 66 | 67 | -------------------------------------------------------------------------------- /site/.kobweb/site/highlight.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | Highlight.js v11.3.1 (git: 2a972d8658) 3 | (c) 2006-2021 Ivan Sagalaev and other contributors 4 | License: BSD-3-Clause 5 | */ 6 | var hljs=function(){"use strict";var e={exports:{}};function t(e){ 7 | return e instanceof Map?e.clear=e.delete=e.set=()=>{ 8 | throw Error("map is read-only")}:e instanceof Set&&(e.add=e.clear=e.delete=()=>{ 9 | throw Error("set is read-only") 10 | }),Object.freeze(e),Object.getOwnPropertyNames(e).forEach((n=>{var i=e[n] 11 | ;"object"!=typeof i||Object.isFrozen(i)||t(i)})),e} 12 | e.exports=t,e.exports.default=t;var n=e.exports;class i{constructor(e){ 13 | void 0===e.data&&(e.data={}),this.data=e.data,this.isMatchIgnored=!1} 14 | ignoreMatch(){this.isMatchIgnored=!0}}function r(e){ 15 | return e.replace(/&/g,"&").replace(//g,">").replace(/"/g,""").replace(/'/g,"'") 16 | }function s(e,...t){const n=Object.create(null);for(const t in e)n[t]=e[t] 17 | ;return t.forEach((e=>{for(const t in e)n[t]=e[t]})),n}const o=e=>!!e.kind 18 | ;class a{constructor(e,t){ 19 | this.buffer="",this.classPrefix=t.classPrefix,e.walk(this)}addText(e){ 20 | this.buffer+=r(e)}openNode(e){if(!o(e))return;let t=e.kind 21 | ;t=e.sublanguage?"language-"+t:((e,{prefix:t})=>{if(e.includes(".")){ 22 | const n=e.split(".") 23 | ;return[`${t}${n.shift()}`,...n.map(((e,t)=>`${e}${"_".repeat(t+1)}`))].join(" ") 24 | }return`${t}${e}`})(t,{prefix:this.classPrefix}),this.span(t)}closeNode(e){ 25 | o(e)&&(this.buffer+="")}value(){return this.buffer}span(e){ 26 | this.buffer+=``}}class c{constructor(){this.rootNode={ 27 | children:[]},this.stack=[this.rootNode]}get top(){ 28 | return this.stack[this.stack.length-1]}get root(){return this.rootNode}add(e){ 29 | this.top.children.push(e)}openNode(e){const t={kind:e,children:[]} 30 | ;this.add(t),this.stack.push(t)}closeNode(){ 31 | if(this.stack.length>1)return this.stack.pop()}closeAllNodes(){ 32 | for(;this.closeNode(););}toJSON(){return JSON.stringify(this.rootNode,null,4)} 33 | walk(e){return this.constructor._walk(e,this.rootNode)}static _walk(e,t){ 34 | return"string"==typeof t?e.addText(t):t.children&&(e.openNode(t), 35 | t.children.forEach((t=>this._walk(e,t))),e.closeNode(t)),e}static _collapse(e){ 36 | "string"!=typeof e&&e.children&&(e.children.every((e=>"string"==typeof e))?e.children=[e.children.join("")]:e.children.forEach((e=>{ 37 | c._collapse(e)})))}}class l extends c{constructor(e){super(),this.options=e} 38 | addKeyword(e,t){""!==e&&(this.openNode(t),this.addText(e),this.closeNode())} 39 | addText(e){""!==e&&this.add(e)}addSublanguage(e,t){const n=e.root 40 | ;n.kind=t,n.sublanguage=!0,this.add(n)}toHTML(){ 41 | return new a(this,this.options).value()}finalize(){return!0}}function g(e){ 42 | return e?"string"==typeof e?e:e.source:null}function d(e){return f("(?=",e,")")} 43 | function u(e){return f("(?:",e,")*")}function h(e){return f("(?:",e,")?")} 44 | function f(...e){return e.map((e=>g(e))).join("")}function p(...e){const t=(e=>{ 45 | const t=e[e.length-1] 46 | ;return"object"==typeof t&&t.constructor===Object?(e.splice(e.length-1,1),t):{} 47 | })(e);return"("+(t.capture?"":"?:")+e.map((e=>g(e))).join("|")+")"} 48 | function b(e){return RegExp(e.toString()+"|").exec("").length-1} 49 | const m=/\[(?:[^\\\]]|\\.)*\]|\(\??|\\([1-9][0-9]*)|\\./ 50 | ;function E(e,{joinWith:t}){let n=0;return e.map((e=>{n+=1;const t=n 51 | ;let i=g(e),r="";for(;i.length>0;){const e=m.exec(i);if(!e){r+=i;break} 52 | r+=i.substring(0,e.index), 53 | i=i.substring(e.index+e[0].length),"\\"===e[0][0]&&e[1]?r+="\\"+(Number(e[1])+t):(r+=e[0], 54 | "("===e[0]&&n++)}return r})).map((e=>`(${e})`)).join(t)} 55 | const x="[a-zA-Z]\\w*",w="[a-zA-Z_]\\w*",y="\\b\\d+(\\.\\d+)?",_="(-?)(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)",v="\\b(0b[01]+)",O={ 56 | begin:"\\\\[\\s\\S]",relevance:0},k={scope:"string",begin:"'",end:"'", 57 | illegal:"\\n",contains:[O]},N={scope:"string",begin:'"',end:'"',illegal:"\\n", 58 | contains:[O]},M=(e,t,n={})=>{const i=s({scope:"comment",begin:e,end:t, 59 | contains:[]},n);i.contains.push({scope:"doctag", 60 | begin:"[ ]*(?=(TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):)", 61 | end:/(TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):/,excludeBegin:!0,relevance:0}) 62 | ;const r=p("I","a","is","so","us","to","at","if","in","it","on",/[A-Za-z]+['](d|ve|re|ll|t|s|n)/,/[A-Za-z]+[-][a-z]+/,/[A-Za-z][a-z]{2,}/) 63 | ;return i.contains.push({begin:f(/[ ]+/,"(",r,/[.]?[:]?([.][ ]|[ ])/,"){3}")}),i 64 | },S=M("//","$"),R=M("/\\*","\\*/"),j=M("#","$");var A=Object.freeze({ 65 | __proto__:null,MATCH_NOTHING_RE:/\b\B/,IDENT_RE:x,UNDERSCORE_IDENT_RE:w, 66 | NUMBER_RE:y,C_NUMBER_RE:_,BINARY_NUMBER_RE:v, 67 | RE_STARTERS_RE:"!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~", 68 | SHEBANG:(e={})=>{const t=/^#![ ]*\// 69 | ;return e.binary&&(e.begin=f(t,/.*\b/,e.binary,/\b.*/)),s({scope:"meta",begin:t, 70 | end:/$/,relevance:0,"on:begin":(e,t)=>{0!==e.index&&t.ignoreMatch()}},e)}, 71 | BACKSLASH_ESCAPE:O,APOS_STRING_MODE:k,QUOTE_STRING_MODE:N,PHRASAL_WORDS_MODE:{ 72 | begin:/\b(a|an|the|are|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such|will|you|your|they|like|more)\b/ 73 | },COMMENT:M,C_LINE_COMMENT_MODE:S,C_BLOCK_COMMENT_MODE:R,HASH_COMMENT_MODE:j, 74 | NUMBER_MODE:{scope:"number",begin:y,relevance:0},C_NUMBER_MODE:{scope:"number", 75 | begin:_,relevance:0},BINARY_NUMBER_MODE:{scope:"number",begin:v,relevance:0}, 76 | REGEXP_MODE:{begin:/(?=\/[^/\n]*\/)/,contains:[{scope:"regexp",begin:/\//, 77 | end:/\/[gimuy]*/,illegal:/\n/,contains:[O,{begin:/\[/,end:/\]/,relevance:0, 78 | contains:[O]}]}]},TITLE_MODE:{scope:"title",begin:x,relevance:0}, 79 | UNDERSCORE_TITLE_MODE:{scope:"title",begin:w,relevance:0},METHOD_GUARD:{ 80 | begin:"\\.\\s*[a-zA-Z_]\\w*",relevance:0},END_SAME_AS_BEGIN:e=>Object.assign(e,{ 81 | "on:begin":(e,t)=>{t.data._beginMatch=e[1]},"on:end":(e,t)=>{ 82 | t.data._beginMatch!==e[1]&&t.ignoreMatch()}})});function I(e,t){ 83 | "."===e.input[e.index-1]&&t.ignoreMatch()}function T(e,t){ 84 | void 0!==e.className&&(e.scope=e.className,delete e.className)}function L(e,t){ 85 | t&&e.beginKeywords&&(e.begin="\\b("+e.beginKeywords.split(" ").join("|")+")(?!\\.)(?=\\b|\\s)", 86 | e.__beforeBegin=I,e.keywords=e.keywords||e.beginKeywords,delete e.beginKeywords, 87 | void 0===e.relevance&&(e.relevance=0))}function B(e,t){ 88 | Array.isArray(e.illegal)&&(e.illegal=p(...e.illegal))}function D(e,t){ 89 | if(e.match){ 90 | if(e.begin||e.end)throw Error("begin & end are not supported with match") 91 | ;e.begin=e.match,delete e.match}}function P(e,t){ 92 | void 0===e.relevance&&(e.relevance=1)}const H=(e,t)=>{if(!e.beforeMatch)return 93 | ;if(e.starts)throw Error("beforeMatch cannot be used with starts") 94 | ;const n=Object.assign({},e);Object.keys(e).forEach((t=>{delete e[t] 95 | })),e.keywords=n.keywords,e.begin=f(n.beforeMatch,d(n.begin)),e.starts={ 96 | relevance:0,contains:[Object.assign(n,{endsParent:!0})] 97 | },e.relevance=0,delete n.beforeMatch 98 | },C=["of","and","for","in","not","or","if","then","parent","list","value"] 99 | ;function $(e,t,n="keyword"){const i=Object.create(null) 100 | ;return"string"==typeof e?r(n,e.split(" ")):Array.isArray(e)?r(n,e):Object.keys(e).forEach((n=>{ 101 | Object.assign(i,$(e[n],t,n))})),i;function r(e,n){ 102 | t&&(n=n.map((e=>e.toLowerCase()))),n.forEach((t=>{const n=t.split("|") 103 | ;i[n[0]]=[e,U(n[0],n[1])]}))}}function U(e,t){ 104 | return t?Number(t):(e=>C.includes(e.toLowerCase()))(e)?0:1}const z={},K=e=>{ 105 | console.error(e)},W=(e,...t)=>{console.log("WARN: "+e,...t)},X=(e,t)=>{ 106 | z[`${e}/${t}`]||(console.log(`Deprecated as of ${e}. ${t}`),z[`${e}/${t}`]=!0) 107 | },G=Error();function Z(e,t,{key:n}){let i=0;const r=e[n],s={},o={} 108 | ;for(let e=1;e<=t.length;e++)o[e+i]=r[e],s[e+i]=!0,i+=b(t[e-1]) 109 | ;e[n]=o,e[n]._emit=s,e[n]._multi=!0}function F(e){(e=>{ 110 | e.scope&&"object"==typeof e.scope&&null!==e.scope&&(e.beginScope=e.scope, 111 | delete e.scope)})(e),"string"==typeof e.beginScope&&(e.beginScope={ 112 | _wrap:e.beginScope}),"string"==typeof e.endScope&&(e.endScope={_wrap:e.endScope 113 | }),(e=>{if(Array.isArray(e.begin)){ 114 | if(e.skip||e.excludeBegin||e.returnBegin)throw K("skip, excludeBegin, returnBegin not compatible with beginScope: {}"), 115 | G 116 | ;if("object"!=typeof e.beginScope||null===e.beginScope)throw K("beginScope must be object"), 117 | G;Z(e,e.begin,{key:"beginScope"}),e.begin=E(e.begin,{joinWith:""})}})(e),(e=>{ 118 | if(Array.isArray(e.end)){ 119 | if(e.skip||e.excludeEnd||e.returnEnd)throw K("skip, excludeEnd, returnEnd not compatible with endScope: {}"), 120 | G 121 | ;if("object"!=typeof e.endScope||null===e.endScope)throw K("endScope must be object"), 122 | G;Z(e,e.end,{key:"endScope"}),e.end=E(e.end,{joinWith:""})}})(e)}function V(e){ 123 | function t(t,n){ 124 | return RegExp(g(t),"m"+(e.case_insensitive?"i":"")+(e.unicodeRegex?"u":"")+(n?"g":"")) 125 | }class n{constructor(){ 126 | this.matchIndexes={},this.regexes=[],this.matchAt=1,this.position=0} 127 | addRule(e,t){ 128 | t.position=this.position++,this.matchIndexes[this.matchAt]=t,this.regexes.push([t,e]), 129 | this.matchAt+=b(e)+1}compile(){0===this.regexes.length&&(this.exec=()=>null) 130 | ;const e=this.regexes.map((e=>e[1]));this.matcherRe=t(E(e,{joinWith:"|" 131 | }),!0),this.lastIndex=0}exec(e){this.matcherRe.lastIndex=this.lastIndex 132 | ;const t=this.matcherRe.exec(e);if(!t)return null 133 | ;const n=t.findIndex(((e,t)=>t>0&&void 0!==e)),i=this.matchIndexes[n] 134 | ;return t.splice(0,n),Object.assign(t,i)}}class i{constructor(){ 135 | this.rules=[],this.multiRegexes=[], 136 | this.count=0,this.lastIndex=0,this.regexIndex=0}getMatcher(e){ 137 | if(this.multiRegexes[e])return this.multiRegexes[e];const t=new n 138 | ;return this.rules.slice(e).forEach((([e,n])=>t.addRule(e,n))), 139 | t.compile(),this.multiRegexes[e]=t,t}resumingScanAtSamePosition(){ 140 | return 0!==this.regexIndex}considerAll(){this.regexIndex=0}addRule(e,t){ 141 | this.rules.push([e,t]),"begin"===t.type&&this.count++}exec(e){ 142 | const t=this.getMatcher(this.regexIndex);t.lastIndex=this.lastIndex 143 | ;let n=t.exec(e) 144 | ;if(this.resumingScanAtSamePosition())if(n&&n.index===this.lastIndex);else{ 145 | const t=this.getMatcher(0);t.lastIndex=this.lastIndex+1,n=t.exec(e)} 146 | return n&&(this.regexIndex+=n.position+1, 147 | this.regexIndex===this.count&&this.considerAll()),n}} 148 | if(e.compilerExtensions||(e.compilerExtensions=[]), 149 | e.contains&&e.contains.includes("self"))throw Error("ERR: contains `self` is not supported at the top-level of a language. See documentation.") 150 | ;return e.classNameAliases=s(e.classNameAliases||{}),function n(r,o){const a=r 151 | ;if(r.isCompiled)return a 152 | ;[T,D,F,H].forEach((e=>e(r,o))),e.compilerExtensions.forEach((e=>e(r,o))), 153 | r.__beforeBegin=null,[L,B,P].forEach((e=>e(r,o))),r.isCompiled=!0;let c=null 154 | ;return"object"==typeof r.keywords&&r.keywords.$pattern&&(r.keywords=Object.assign({},r.keywords), 155 | c=r.keywords.$pattern, 156 | delete r.keywords.$pattern),c=c||/\w+/,r.keywords&&(r.keywords=$(r.keywords,e.case_insensitive)), 157 | a.keywordPatternRe=t(c,!0), 158 | o&&(r.begin||(r.begin=/\B|\b/),a.beginRe=t(a.begin),r.end||r.endsWithParent||(r.end=/\B|\b/), 159 | r.end&&(a.endRe=t(a.end)), 160 | a.terminatorEnd=g(a.end)||"",r.endsWithParent&&o.terminatorEnd&&(a.terminatorEnd+=(r.end?"|":"")+o.terminatorEnd)), 161 | r.illegal&&(a.illegalRe=t(r.illegal)), 162 | r.contains||(r.contains=[]),r.contains=[].concat(...r.contains.map((e=>(e=>(e.variants&&!e.cachedVariants&&(e.cachedVariants=e.variants.map((t=>s(e,{ 163 | variants:null},t)))),e.cachedVariants?e.cachedVariants:q(e)?s(e,{ 164 | starts:e.starts?s(e.starts):null 165 | }):Object.isFrozen(e)?s(e):e))("self"===e?r:e)))),r.contains.forEach((e=>{n(e,a) 166 | })),r.starts&&n(r.starts,o),a.matcher=(e=>{const t=new i 167 | ;return e.contains.forEach((e=>t.addRule(e.begin,{rule:e,type:"begin" 168 | }))),e.terminatorEnd&&t.addRule(e.terminatorEnd,{type:"end" 169 | }),e.illegal&&t.addRule(e.illegal,{type:"illegal"}),t})(a),a}(e)}function q(e){ 170 | return!!e&&(e.endsWithParent||q(e.starts))}class J extends Error{ 171 | constructor(e,t){super(e),this.name="HTMLInjectionError",this.html=t}} 172 | const Y=r,Q=s,ee=Symbol("nomatch");var te=(e=>{ 173 | const t=Object.create(null),r=Object.create(null),s=[];let o=!0 174 | ;const a="Could not find the language '{}', did you forget to load/include a language module?",c={ 175 | disableAutodetect:!0,name:"Plain text",contains:[]};let g={ 176 | ignoreUnescapedHTML:!1,throwUnescapedHTML:!1,noHighlightRe:/^(no-?highlight)$/i, 177 | languageDetectRe:/\blang(?:uage)?-([\w-]+)\b/i,classPrefix:"hljs-", 178 | cssSelector:"pre code",languages:null,__emitter:l};function b(e){ 179 | return g.noHighlightRe.test(e)}function m(e,t,n){let i="",r="" 180 | ;"object"==typeof t?(i=e, 181 | n=t.ignoreIllegals,r=t.language):(X("10.7.0","highlight(lang, code, ...args) has been deprecated."), 182 | X("10.7.0","Please use highlight(code, options) instead.\nhttps://github.com/highlightjs/highlight.js/issues/2277"), 183 | r=e,i=t),void 0===n&&(n=!0);const s={code:i,language:r};N("before:highlight",s) 184 | ;const o=s.result?s.result:E(s.language,s.code,n) 185 | ;return o.code=s.code,N("after:highlight",o),o}function E(e,n,r,s){ 186 | const c=Object.create(null);function l(){if(!k.keywords)return void M.addText(S) 187 | ;let e=0;k.keywordPatternRe.lastIndex=0;let t=k.keywordPatternRe.exec(S),n="" 188 | ;for(;t;){n+=S.substring(e,t.index) 189 | ;const r=y.case_insensitive?t[0].toLowerCase():t[0],s=(i=r,k.keywords[i]);if(s){ 190 | const[e,i]=s 191 | ;if(M.addText(n),n="",c[r]=(c[r]||0)+1,c[r]<=7&&(R+=i),e.startsWith("_"))n+=t[0];else{ 192 | const n=y.classNameAliases[e]||e;M.addKeyword(t[0],n)}}else n+=t[0] 193 | ;e=k.keywordPatternRe.lastIndex,t=k.keywordPatternRe.exec(S)}var i 194 | ;n+=S.substr(e),M.addText(n)}function d(){null!=k.subLanguage?(()=>{ 195 | if(""===S)return;let e=null;if("string"==typeof k.subLanguage){ 196 | if(!t[k.subLanguage])return void M.addText(S) 197 | ;e=E(k.subLanguage,S,!0,N[k.subLanguage]),N[k.subLanguage]=e._top 198 | }else e=x(S,k.subLanguage.length?k.subLanguage:null) 199 | ;k.relevance>0&&(R+=e.relevance),M.addSublanguage(e._emitter,e.language) 200 | })():l(),S=""}function u(e,t){let n=1;for(;void 0!==t[n];){if(!e._emit[n]){n++ 201 | ;continue}const i=y.classNameAliases[e[n]]||e[n],r=t[n] 202 | ;i?M.addKeyword(r,i):(S=r,l(),S=""),n++}}function h(e,t){ 203 | return e.scope&&"string"==typeof e.scope&&M.openNode(y.classNameAliases[e.scope]||e.scope), 204 | e.beginScope&&(e.beginScope._wrap?(M.addKeyword(S,y.classNameAliases[e.beginScope._wrap]||e.beginScope._wrap), 205 | S=""):e.beginScope._multi&&(u(e.beginScope,t),S="")),k=Object.create(e,{parent:{ 206 | value:k}}),k}function f(e,t,n){let r=((e,t)=>{const n=e&&e.exec(t) 207 | ;return n&&0===n.index})(e.endRe,n);if(r){if(e["on:end"]){const n=new i(e) 208 | ;e["on:end"](t,n),n.isMatchIgnored&&(r=!1)}if(r){ 209 | for(;e.endsParent&&e.parent;)e=e.parent;return e}} 210 | if(e.endsWithParent)return f(e.parent,t,n)}function p(e){ 211 | return 0===k.matcher.regexIndex?(S+=e[0],1):(I=!0,0)}function b(e){ 212 | const t=e[0],i=n.substr(e.index),r=f(k,e,i);if(!r)return ee;const s=k 213 | ;k.endScope&&k.endScope._wrap?(d(), 214 | M.addKeyword(t,k.endScope._wrap)):k.endScope&&k.endScope._multi?(d(), 215 | u(k.endScope,e)):s.skip?S+=t:(s.returnEnd||s.excludeEnd||(S+=t), 216 | d(),s.excludeEnd&&(S=t));do{ 217 | k.scope&&M.closeNode(),k.skip||k.subLanguage||(R+=k.relevance),k=k.parent 218 | }while(k!==r.parent);return r.starts&&h(r.starts,e),s.returnEnd?0:t.length} 219 | let m={};function w(t,s){const a=s&&s[0];if(S+=t,null==a)return d(),0 220 | ;if("begin"===m.type&&"end"===s.type&&m.index===s.index&&""===a){ 221 | if(S+=n.slice(s.index,s.index+1),!o){const t=Error(`0 width match regex (${e})`) 222 | ;throw t.languageName=e,t.badRule=m.rule,t}return 1} 223 | if(m=s,"begin"===s.type)return(e=>{ 224 | const t=e[0],n=e.rule,r=new i(n),s=[n.__beforeBegin,n["on:begin"]] 225 | ;for(const n of s)if(n&&(n(e,r),r.isMatchIgnored))return p(t) 226 | ;return n.skip?S+=t:(n.excludeBegin&&(S+=t), 227 | d(),n.returnBegin||n.excludeBegin||(S=t)),h(n,e),n.returnBegin?0:t.length})(s) 228 | ;if("illegal"===s.type&&!r){ 229 | const e=Error('Illegal lexeme "'+a+'" for mode "'+(k.scope||"")+'"') 230 | ;throw e.mode=k,e}if("end"===s.type){const e=b(s);if(e!==ee)return e} 231 | if("illegal"===s.type&&""===a)return 1 232 | ;if(A>1e5&&A>3*s.index)throw Error("potential infinite loop, way more iterations than matches") 233 | ;return S+=a,a.length}const y=v(e) 234 | ;if(!y)throw K(a.replace("{}",e)),Error('Unknown language: "'+e+'"') 235 | ;const _=V(y);let O="",k=s||_;const N={},M=new g.__emitter(g);(()=>{const e=[] 236 | ;for(let t=k;t!==y;t=t.parent)t.scope&&e.unshift(t.scope) 237 | ;e.forEach((e=>M.openNode(e)))})();let S="",R=0,j=0,A=0,I=!1;try{ 238 | for(k.matcher.considerAll();;){ 239 | A++,I?I=!1:k.matcher.considerAll(),k.matcher.lastIndex=j 240 | ;const e=k.matcher.exec(n);if(!e)break;const t=w(n.substring(j,e.index),e) 241 | ;j=e.index+t}return w(n.substr(j)),M.closeAllNodes(),M.finalize(),O=M.toHTML(),{ 242 | language:e,value:O,relevance:R,illegal:!1,_emitter:M,_top:k}}catch(t){ 243 | if(t.message&&t.message.includes("Illegal"))return{language:e,value:Y(n), 244 | illegal:!0,relevance:0,_illegalBy:{message:t.message,index:j, 245 | context:n.slice(j-100,j+100),mode:t.mode,resultSoFar:O},_emitter:M};if(o)return{ 246 | language:e,value:Y(n),illegal:!1,relevance:0,errorRaised:t,_emitter:M,_top:k} 247 | ;throw t}}function x(e,n){n=n||g.languages||Object.keys(t);const i=(e=>{ 248 | const t={value:Y(e),illegal:!1,relevance:0,_top:c,_emitter:new g.__emitter(g)} 249 | ;return t._emitter.addText(e),t})(e),r=n.filter(v).filter(k).map((t=>E(t,e,!1))) 250 | ;r.unshift(i);const s=r.sort(((e,t)=>{ 251 | if(e.relevance!==t.relevance)return t.relevance-e.relevance 252 | ;if(e.language&&t.language){if(v(e.language).supersetOf===t.language)return 1 253 | ;if(v(t.language).supersetOf===e.language)return-1}return 0})),[o,a]=s,l=o 254 | ;return l.secondBest=a,l}function w(e){let t=null;const n=(e=>{ 255 | let t=e.className+" ";t+=e.parentNode?e.parentNode.className:"" 256 | ;const n=g.languageDetectRe.exec(t);if(n){const t=v(n[1]) 257 | ;return t||(W(a.replace("{}",n[1])), 258 | W("Falling back to no-highlight mode for this block.",e)),t?n[1]:"no-highlight"} 259 | return t.split(/\s+/).find((e=>b(e)||v(e)))})(e);if(b(n))return 260 | ;if(N("before:highlightElement",{el:e,language:n 261 | }),e.children.length>0&&(g.ignoreUnescapedHTML||(console.warn("One of your code blocks includes unescaped HTML. This is a potentially serious security risk."), 262 | console.warn("https://github.com/highlightjs/highlight.js/issues/2886"), 263 | console.warn(e)), 264 | g.throwUnescapedHTML))throw new J("One of your code blocks includes unescaped HTML.",e.innerHTML) 265 | ;t=e;const i=t.textContent,s=n?m(i,{language:n,ignoreIllegals:!0}):x(i) 266 | ;e.innerHTML=s.value,((e,t,n)=>{const i=t&&r[t]||n 267 | ;e.classList.add("hljs"),e.classList.add("language-"+i) 268 | })(e,n,s.language),e.result={language:s.language,re:s.relevance, 269 | relevance:s.relevance},s.secondBest&&(e.secondBest={ 270 | language:s.secondBest.language,relevance:s.secondBest.relevance 271 | }),N("after:highlightElement",{el:e,result:s,text:i})}let y=!1;function _(){ 272 | "loading"!==document.readyState?document.querySelectorAll(g.cssSelector).forEach(w):y=!0 273 | }function v(e){return e=(e||"").toLowerCase(),t[e]||t[r[e]]} 274 | function O(e,{languageName:t}){"string"==typeof e&&(e=[e]),e.forEach((e=>{ 275 | r[e.toLowerCase()]=t}))}function k(e){const t=v(e) 276 | ;return t&&!t.disableAutodetect}function N(e,t){const n=e;s.forEach((e=>{ 277 | e[n]&&e[n](t)}))} 278 | "undefined"!=typeof window&&window.addEventListener&&window.addEventListener("DOMContentLoaded",(()=>{ 279 | y&&_()}),!1),Object.assign(e,{highlight:m,highlightAuto:x,highlightAll:_, 280 | highlightElement:w, 281 | highlightBlock:e=>(X("10.7.0","highlightBlock will be removed entirely in v12.0"), 282 | X("10.7.0","Please use highlightElement now."),w(e)),configure:e=>{g=Q(g,e)}, 283 | initHighlighting:()=>{ 284 | _(),X("10.6.0","initHighlighting() deprecated. Use highlightAll() now.")}, 285 | initHighlightingOnLoad:()=>{ 286 | _(),X("10.6.0","initHighlightingOnLoad() deprecated. Use highlightAll() now.") 287 | },registerLanguage:(n,i)=>{let r=null;try{r=i(e)}catch(e){ 288 | if(K("Language definition for '{}' could not be registered.".replace("{}",n)), 289 | !o)throw e;K(e),r=c} 290 | r.name||(r.name=n),t[n]=r,r.rawDefinition=i.bind(null,e),r.aliases&&O(r.aliases,{ 291 | languageName:n})},unregisterLanguage:e=>{delete t[e] 292 | ;for(const t of Object.keys(r))r[t]===e&&delete r[t]}, 293 | listLanguages:()=>Object.keys(t),getLanguage:v,registerAliases:O, 294 | autoDetection:k,inherit:Q,addPlugin:e=>{(e=>{ 295 | e["before:highlightBlock"]&&!e["before:highlightElement"]&&(e["before:highlightElement"]=t=>{ 296 | e["before:highlightBlock"](Object.assign({block:t.el},t)) 297 | }),e["after:highlightBlock"]&&!e["after:highlightElement"]&&(e["after:highlightElement"]=t=>{ 298 | e["after:highlightBlock"](Object.assign({block:t.el},t))})})(e),s.push(e)} 299 | }),e.debugMode=()=>{o=!1},e.safeMode=()=>{o=!0 300 | },e.versionString="11.3.1",e.regex={concat:f,lookahead:d,either:p,optional:h, 301 | anyNumberOfTimes:u};for(const e in A)"object"==typeof A[e]&&n(A[e]) 302 | ;return Object.assign(e,A),e})({});return te}() 303 | ;"object"==typeof exports&&"undefined"!=typeof module&&(module.exports=hljs);/*! `kotlin` grammar compiled for Highlight.js 11.3.1 */ 304 | (()=>{var e=(()=>{"use strict" 305 | ;var e="\\.([0-9](_*[0-9])*)",n="[0-9a-fA-F](_*[0-9a-fA-F])*",a={ 306 | className:"number",variants:[{ 307 | begin:`(\\b([0-9](_*[0-9])*)((${e})|\\.)?|(${e}))[eE][+-]?([0-9](_*[0-9])*)[fFdD]?\\b` 308 | },{begin:`\\b([0-9](_*[0-9])*)((${e})[fFdD]?\\b|\\.([fFdD]\\b)?)`},{ 309 | begin:`(${e})[fFdD]?\\b`},{begin:"\\b([0-9](_*[0-9])*)[fFdD]\\b"},{ 310 | begin:`\\b0[xX]((${n})\\.?|(${n})?\\.(${n}))[pP][+-]?([0-9](_*[0-9])*)[fFdD]?\\b` 311 | },{begin:"\\b(0|[1-9](_*[0-9])*)[lL]?\\b"},{begin:`\\b0[xX](${n})[lL]?\\b`},{ 312 | begin:"\\b0(_*[0-7])*[lL]?\\b"},{begin:"\\b0[bB][01](_*[01])*[lL]?\\b"}], 313 | relevance:0};return e=>{const n={ 314 | keyword:"abstract as val var vararg get set class object open private protected public noinline crossinline dynamic final enum if else do while for when throw try catch finally import package is in fun override companion reified inline lateinit init interface annotation data sealed internal infix operator out by constructor super tailrec where const inner suspend typealias external expect actual", 315 | built_in:"Byte Short Char Int Long Boolean Float Double Void Unit Nothing", 316 | literal:"true false null"},i={className:"symbol",begin:e.UNDERSCORE_IDENT_RE+"@" 317 | },s={className:"subst",begin:/\$\{/,end:/\}/,contains:[e.C_NUMBER_MODE]},t={ 318 | className:"variable",begin:"\\$"+e.UNDERSCORE_IDENT_RE},r={className:"string", 319 | variants:[{begin:'"""',end:'"""(?=[^"])',contains:[t,s]},{begin:"'",end:"'", 320 | illegal:/\n/,contains:[e.BACKSLASH_ESCAPE]},{begin:'"',end:'"',illegal:/\n/, 321 | contains:[e.BACKSLASH_ESCAPE,t,s]}]};s.contains.push(r);const l={ 322 | className:"meta", 323 | begin:"@(?:file|property|field|get|set|receiver|param|setparam|delegate)\\s*:(?:\\s*"+e.UNDERSCORE_IDENT_RE+")?" 324 | },c={className:"meta",begin:"@"+e.UNDERSCORE_IDENT_RE,contains:[{begin:/\(/, 325 | end:/\)/,contains:[e.inherit(r,{className:"string"})]}] 326 | },o=a,b=e.COMMENT("/\\*","\\*/",{contains:[e.C_BLOCK_COMMENT_MODE]}),E={ 327 | variants:[{className:"type",begin:e.UNDERSCORE_IDENT_RE},{begin:/\(/,end:/\)/, 328 | contains:[]}]},d=E;return d.variants[1].contains=[E],E.variants[1].contains=[d], 329 | {name:"Kotlin",aliases:["kt","kts"],keywords:n, 330 | contains:[e.COMMENT("/\\*\\*","\\*/",{relevance:0,contains:[{className:"doctag", 331 | begin:"@[A-Za-z]+"}]}),e.C_LINE_COMMENT_MODE,b,{className:"keyword", 332 | begin:/\b(break|continue|return|this)\b/,starts:{contains:[{className:"symbol", 333 | begin:/@\w+/}]}},i,l,c,{className:"function",beginKeywords:"fun",end:"[(]|$", 334 | returnBegin:!0,excludeEnd:!0,keywords:n,relevance:5,contains:[{ 335 | begin:e.UNDERSCORE_IDENT_RE+"\\s*\\(",returnBegin:!0,relevance:0, 336 | contains:[e.UNDERSCORE_TITLE_MODE]},{className:"type",begin://, 337 | keywords:"reified",relevance:0},{className:"params",begin:/\(/,end:/\)/, 338 | endsParent:!0,keywords:n,relevance:0,contains:[{begin:/:/,end:/[=,\/]/, 339 | endsWithParent:!0,contains:[E,e.C_LINE_COMMENT_MODE,b],relevance:0 340 | },e.C_LINE_COMMENT_MODE,b,l,c,r,e.C_NUMBER_MODE]},b]},{className:"class", 341 | beginKeywords:"class interface trait",end:/[:\{(]|$/,excludeEnd:!0, 342 | illegal:"extends implements",contains:[{ 343 | beginKeywords:"public protected internal private constructor" 344 | },e.UNDERSCORE_TITLE_MODE,{className:"type",begin://,excludeBegin:!0, 345 | excludeEnd:!0,relevance:0},{className:"type",begin:/[,:]\s*/,end:/[<\(,]|$/, 346 | excludeBegin:!0,returnEnd:!0},l,c]},r,{className:"meta",begin:"^#!/usr/bin/env", 347 | end:"$",illegal:"\n"},o]}}})();hljs.registerLanguage("kotlin",e)})(); -------------------------------------------------------------------------------- /site/src/jsMain/resources/public/highlight.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | Highlight.js v11.3.1 (git: 2a972d8658) 3 | (c) 2006-2021 Ivan Sagalaev and other contributors 4 | License: BSD-3-Clause 5 | */ 6 | var hljs=function(){"use strict";var e={exports:{}};function t(e){ 7 | return e instanceof Map?e.clear=e.delete=e.set=()=>{ 8 | throw Error("map is read-only")}:e instanceof Set&&(e.add=e.clear=e.delete=()=>{ 9 | throw Error("set is read-only") 10 | }),Object.freeze(e),Object.getOwnPropertyNames(e).forEach((n=>{var i=e[n] 11 | ;"object"!=typeof i||Object.isFrozen(i)||t(i)})),e} 12 | e.exports=t,e.exports.default=t;var n=e.exports;class i{constructor(e){ 13 | void 0===e.data&&(e.data={}),this.data=e.data,this.isMatchIgnored=!1} 14 | ignoreMatch(){this.isMatchIgnored=!0}}function r(e){ 15 | return e.replace(/&/g,"&").replace(//g,">").replace(/"/g,""").replace(/'/g,"'") 16 | }function s(e,...t){const n=Object.create(null);for(const t in e)n[t]=e[t] 17 | ;return t.forEach((e=>{for(const t in e)n[t]=e[t]})),n}const o=e=>!!e.kind 18 | ;class a{constructor(e,t){ 19 | this.buffer="",this.classPrefix=t.classPrefix,e.walk(this)}addText(e){ 20 | this.buffer+=r(e)}openNode(e){if(!o(e))return;let t=e.kind 21 | ;t=e.sublanguage?"language-"+t:((e,{prefix:t})=>{if(e.includes(".")){ 22 | const n=e.split(".") 23 | ;return[`${t}${n.shift()}`,...n.map(((e,t)=>`${e}${"_".repeat(t+1)}`))].join(" ") 24 | }return`${t}${e}`})(t,{prefix:this.classPrefix}),this.span(t)}closeNode(e){ 25 | o(e)&&(this.buffer+="")}value(){return this.buffer}span(e){ 26 | this.buffer+=``}}class c{constructor(){this.rootNode={ 27 | children:[]},this.stack=[this.rootNode]}get top(){ 28 | return this.stack[this.stack.length-1]}get root(){return this.rootNode}add(e){ 29 | this.top.children.push(e)}openNode(e){const t={kind:e,children:[]} 30 | ;this.add(t),this.stack.push(t)}closeNode(){ 31 | if(this.stack.length>1)return this.stack.pop()}closeAllNodes(){ 32 | for(;this.closeNode(););}toJSON(){return JSON.stringify(this.rootNode,null,4)} 33 | walk(e){return this.constructor._walk(e,this.rootNode)}static _walk(e,t){ 34 | return"string"==typeof t?e.addText(t):t.children&&(e.openNode(t), 35 | t.children.forEach((t=>this._walk(e,t))),e.closeNode(t)),e}static _collapse(e){ 36 | "string"!=typeof e&&e.children&&(e.children.every((e=>"string"==typeof e))?e.children=[e.children.join("")]:e.children.forEach((e=>{ 37 | c._collapse(e)})))}}class l extends c{constructor(e){super(),this.options=e} 38 | addKeyword(e,t){""!==e&&(this.openNode(t),this.addText(e),this.closeNode())} 39 | addText(e){""!==e&&this.add(e)}addSublanguage(e,t){const n=e.root 40 | ;n.kind=t,n.sublanguage=!0,this.add(n)}toHTML(){ 41 | return new a(this,this.options).value()}finalize(){return!0}}function g(e){ 42 | return e?"string"==typeof e?e:e.source:null}function d(e){return f("(?=",e,")")} 43 | function u(e){return f("(?:",e,")*")}function h(e){return f("(?:",e,")?")} 44 | function f(...e){return e.map((e=>g(e))).join("")}function p(...e){const t=(e=>{ 45 | const t=e[e.length-1] 46 | ;return"object"==typeof t&&t.constructor===Object?(e.splice(e.length-1,1),t):{} 47 | })(e);return"("+(t.capture?"":"?:")+e.map((e=>g(e))).join("|")+")"} 48 | function b(e){return RegExp(e.toString()+"|").exec("").length-1} 49 | const m=/\[(?:[^\\\]]|\\.)*\]|\(\??|\\([1-9][0-9]*)|\\./ 50 | ;function E(e,{joinWith:t}){let n=0;return e.map((e=>{n+=1;const t=n 51 | ;let i=g(e),r="";for(;i.length>0;){const e=m.exec(i);if(!e){r+=i;break} 52 | r+=i.substring(0,e.index), 53 | i=i.substring(e.index+e[0].length),"\\"===e[0][0]&&e[1]?r+="\\"+(Number(e[1])+t):(r+=e[0], 54 | "("===e[0]&&n++)}return r})).map((e=>`(${e})`)).join(t)} 55 | const x="[a-zA-Z]\\w*",w="[a-zA-Z_]\\w*",y="\\b\\d+(\\.\\d+)?",_="(-?)(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)",v="\\b(0b[01]+)",O={ 56 | begin:"\\\\[\\s\\S]",relevance:0},k={scope:"string",begin:"'",end:"'", 57 | illegal:"\\n",contains:[O]},N={scope:"string",begin:'"',end:'"',illegal:"\\n", 58 | contains:[O]},M=(e,t,n={})=>{const i=s({scope:"comment",begin:e,end:t, 59 | contains:[]},n);i.contains.push({scope:"doctag", 60 | begin:"[ ]*(?=(TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):)", 61 | end:/(TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):/,excludeBegin:!0,relevance:0}) 62 | ;const r=p("I","a","is","so","us","to","at","if","in","it","on",/[A-Za-z]+['](d|ve|re|ll|t|s|n)/,/[A-Za-z]+[-][a-z]+/,/[A-Za-z][a-z]{2,}/) 63 | ;return i.contains.push({begin:f(/[ ]+/,"(",r,/[.]?[:]?([.][ ]|[ ])/,"){3}")}),i 64 | },S=M("//","$"),R=M("/\\*","\\*/"),j=M("#","$");var A=Object.freeze({ 65 | __proto__:null,MATCH_NOTHING_RE:/\b\B/,IDENT_RE:x,UNDERSCORE_IDENT_RE:w, 66 | NUMBER_RE:y,C_NUMBER_RE:_,BINARY_NUMBER_RE:v, 67 | RE_STARTERS_RE:"!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~", 68 | SHEBANG:(e={})=>{const t=/^#![ ]*\// 69 | ;return e.binary&&(e.begin=f(t,/.*\b/,e.binary,/\b.*/)),s({scope:"meta",begin:t, 70 | end:/$/,relevance:0,"on:begin":(e,t)=>{0!==e.index&&t.ignoreMatch()}},e)}, 71 | BACKSLASH_ESCAPE:O,APOS_STRING_MODE:k,QUOTE_STRING_MODE:N,PHRASAL_WORDS_MODE:{ 72 | begin:/\b(a|an|the|are|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such|will|you|your|they|like|more)\b/ 73 | },COMMENT:M,C_LINE_COMMENT_MODE:S,C_BLOCK_COMMENT_MODE:R,HASH_COMMENT_MODE:j, 74 | NUMBER_MODE:{scope:"number",begin:y,relevance:0},C_NUMBER_MODE:{scope:"number", 75 | begin:_,relevance:0},BINARY_NUMBER_MODE:{scope:"number",begin:v,relevance:0}, 76 | REGEXP_MODE:{begin:/(?=\/[^/\n]*\/)/,contains:[{scope:"regexp",begin:/\//, 77 | end:/\/[gimuy]*/,illegal:/\n/,contains:[O,{begin:/\[/,end:/\]/,relevance:0, 78 | contains:[O]}]}]},TITLE_MODE:{scope:"title",begin:x,relevance:0}, 79 | UNDERSCORE_TITLE_MODE:{scope:"title",begin:w,relevance:0},METHOD_GUARD:{ 80 | begin:"\\.\\s*[a-zA-Z_]\\w*",relevance:0},END_SAME_AS_BEGIN:e=>Object.assign(e,{ 81 | "on:begin":(e,t)=>{t.data._beginMatch=e[1]},"on:end":(e,t)=>{ 82 | t.data._beginMatch!==e[1]&&t.ignoreMatch()}})});function I(e,t){ 83 | "."===e.input[e.index-1]&&t.ignoreMatch()}function T(e,t){ 84 | void 0!==e.className&&(e.scope=e.className,delete e.className)}function L(e,t){ 85 | t&&e.beginKeywords&&(e.begin="\\b("+e.beginKeywords.split(" ").join("|")+")(?!\\.)(?=\\b|\\s)", 86 | e.__beforeBegin=I,e.keywords=e.keywords||e.beginKeywords,delete e.beginKeywords, 87 | void 0===e.relevance&&(e.relevance=0))}function B(e,t){ 88 | Array.isArray(e.illegal)&&(e.illegal=p(...e.illegal))}function D(e,t){ 89 | if(e.match){ 90 | if(e.begin||e.end)throw Error("begin & end are not supported with match") 91 | ;e.begin=e.match,delete e.match}}function P(e,t){ 92 | void 0===e.relevance&&(e.relevance=1)}const H=(e,t)=>{if(!e.beforeMatch)return 93 | ;if(e.starts)throw Error("beforeMatch cannot be used with starts") 94 | ;const n=Object.assign({},e);Object.keys(e).forEach((t=>{delete e[t] 95 | })),e.keywords=n.keywords,e.begin=f(n.beforeMatch,d(n.begin)),e.starts={ 96 | relevance:0,contains:[Object.assign(n,{endsParent:!0})] 97 | },e.relevance=0,delete n.beforeMatch 98 | },C=["of","and","for","in","not","or","if","then","parent","list","value"] 99 | ;function $(e,t,n="keyword"){const i=Object.create(null) 100 | ;return"string"==typeof e?r(n,e.split(" ")):Array.isArray(e)?r(n,e):Object.keys(e).forEach((n=>{ 101 | Object.assign(i,$(e[n],t,n))})),i;function r(e,n){ 102 | t&&(n=n.map((e=>e.toLowerCase()))),n.forEach((t=>{const n=t.split("|") 103 | ;i[n[0]]=[e,U(n[0],n[1])]}))}}function U(e,t){ 104 | return t?Number(t):(e=>C.includes(e.toLowerCase()))(e)?0:1}const z={},K=e=>{ 105 | console.error(e)},W=(e,...t)=>{console.log("WARN: "+e,...t)},X=(e,t)=>{ 106 | z[`${e}/${t}`]||(console.log(`Deprecated as of ${e}. ${t}`),z[`${e}/${t}`]=!0) 107 | },G=Error();function Z(e,t,{key:n}){let i=0;const r=e[n],s={},o={} 108 | ;for(let e=1;e<=t.length;e++)o[e+i]=r[e],s[e+i]=!0,i+=b(t[e-1]) 109 | ;e[n]=o,e[n]._emit=s,e[n]._multi=!0}function F(e){(e=>{ 110 | e.scope&&"object"==typeof e.scope&&null!==e.scope&&(e.beginScope=e.scope, 111 | delete e.scope)})(e),"string"==typeof e.beginScope&&(e.beginScope={ 112 | _wrap:e.beginScope}),"string"==typeof e.endScope&&(e.endScope={_wrap:e.endScope 113 | }),(e=>{if(Array.isArray(e.begin)){ 114 | if(e.skip||e.excludeBegin||e.returnBegin)throw K("skip, excludeBegin, returnBegin not compatible with beginScope: {}"), 115 | G 116 | ;if("object"!=typeof e.beginScope||null===e.beginScope)throw K("beginScope must be object"), 117 | G;Z(e,e.begin,{key:"beginScope"}),e.begin=E(e.begin,{joinWith:""})}})(e),(e=>{ 118 | if(Array.isArray(e.end)){ 119 | if(e.skip||e.excludeEnd||e.returnEnd)throw K("skip, excludeEnd, returnEnd not compatible with endScope: {}"), 120 | G 121 | ;if("object"!=typeof e.endScope||null===e.endScope)throw K("endScope must be object"), 122 | G;Z(e,e.end,{key:"endScope"}),e.end=E(e.end,{joinWith:""})}})(e)}function V(e){ 123 | function t(t,n){ 124 | return RegExp(g(t),"m"+(e.case_insensitive?"i":"")+(e.unicodeRegex?"u":"")+(n?"g":"")) 125 | }class n{constructor(){ 126 | this.matchIndexes={},this.regexes=[],this.matchAt=1,this.position=0} 127 | addRule(e,t){ 128 | t.position=this.position++,this.matchIndexes[this.matchAt]=t,this.regexes.push([t,e]), 129 | this.matchAt+=b(e)+1}compile(){0===this.regexes.length&&(this.exec=()=>null) 130 | ;const e=this.regexes.map((e=>e[1]));this.matcherRe=t(E(e,{joinWith:"|" 131 | }),!0),this.lastIndex=0}exec(e){this.matcherRe.lastIndex=this.lastIndex 132 | ;const t=this.matcherRe.exec(e);if(!t)return null 133 | ;const n=t.findIndex(((e,t)=>t>0&&void 0!==e)),i=this.matchIndexes[n] 134 | ;return t.splice(0,n),Object.assign(t,i)}}class i{constructor(){ 135 | this.rules=[],this.multiRegexes=[], 136 | this.count=0,this.lastIndex=0,this.regexIndex=0}getMatcher(e){ 137 | if(this.multiRegexes[e])return this.multiRegexes[e];const t=new n 138 | ;return this.rules.slice(e).forEach((([e,n])=>t.addRule(e,n))), 139 | t.compile(),this.multiRegexes[e]=t,t}resumingScanAtSamePosition(){ 140 | return 0!==this.regexIndex}considerAll(){this.regexIndex=0}addRule(e,t){ 141 | this.rules.push([e,t]),"begin"===t.type&&this.count++}exec(e){ 142 | const t=this.getMatcher(this.regexIndex);t.lastIndex=this.lastIndex 143 | ;let n=t.exec(e) 144 | ;if(this.resumingScanAtSamePosition())if(n&&n.index===this.lastIndex);else{ 145 | const t=this.getMatcher(0);t.lastIndex=this.lastIndex+1,n=t.exec(e)} 146 | return n&&(this.regexIndex+=n.position+1, 147 | this.regexIndex===this.count&&this.considerAll()),n}} 148 | if(e.compilerExtensions||(e.compilerExtensions=[]), 149 | e.contains&&e.contains.includes("self"))throw Error("ERR: contains `self` is not supported at the top-level of a language. See documentation.") 150 | ;return e.classNameAliases=s(e.classNameAliases||{}),function n(r,o){const a=r 151 | ;if(r.isCompiled)return a 152 | ;[T,D,F,H].forEach((e=>e(r,o))),e.compilerExtensions.forEach((e=>e(r,o))), 153 | r.__beforeBegin=null,[L,B,P].forEach((e=>e(r,o))),r.isCompiled=!0;let c=null 154 | ;return"object"==typeof r.keywords&&r.keywords.$pattern&&(r.keywords=Object.assign({},r.keywords), 155 | c=r.keywords.$pattern, 156 | delete r.keywords.$pattern),c=c||/\w+/,r.keywords&&(r.keywords=$(r.keywords,e.case_insensitive)), 157 | a.keywordPatternRe=t(c,!0), 158 | o&&(r.begin||(r.begin=/\B|\b/),a.beginRe=t(a.begin),r.end||r.endsWithParent||(r.end=/\B|\b/), 159 | r.end&&(a.endRe=t(a.end)), 160 | a.terminatorEnd=g(a.end)||"",r.endsWithParent&&o.terminatorEnd&&(a.terminatorEnd+=(r.end?"|":"")+o.terminatorEnd)), 161 | r.illegal&&(a.illegalRe=t(r.illegal)), 162 | r.contains||(r.contains=[]),r.contains=[].concat(...r.contains.map((e=>(e=>(e.variants&&!e.cachedVariants&&(e.cachedVariants=e.variants.map((t=>s(e,{ 163 | variants:null},t)))),e.cachedVariants?e.cachedVariants:q(e)?s(e,{ 164 | starts:e.starts?s(e.starts):null 165 | }):Object.isFrozen(e)?s(e):e))("self"===e?r:e)))),r.contains.forEach((e=>{n(e,a) 166 | })),r.starts&&n(r.starts,o),a.matcher=(e=>{const t=new i 167 | ;return e.contains.forEach((e=>t.addRule(e.begin,{rule:e,type:"begin" 168 | }))),e.terminatorEnd&&t.addRule(e.terminatorEnd,{type:"end" 169 | }),e.illegal&&t.addRule(e.illegal,{type:"illegal"}),t})(a),a}(e)}function q(e){ 170 | return!!e&&(e.endsWithParent||q(e.starts))}class J extends Error{ 171 | constructor(e,t){super(e),this.name="HTMLInjectionError",this.html=t}} 172 | const Y=r,Q=s,ee=Symbol("nomatch");var te=(e=>{ 173 | const t=Object.create(null),r=Object.create(null),s=[];let o=!0 174 | ;const a="Could not find the language '{}', did you forget to load/include a language module?",c={ 175 | disableAutodetect:!0,name:"Plain text",contains:[]};let g={ 176 | ignoreUnescapedHTML:!1,throwUnescapedHTML:!1,noHighlightRe:/^(no-?highlight)$/i, 177 | languageDetectRe:/\blang(?:uage)?-([\w-]+)\b/i,classPrefix:"hljs-", 178 | cssSelector:"pre code",languages:null,__emitter:l};function b(e){ 179 | return g.noHighlightRe.test(e)}function m(e,t,n){let i="",r="" 180 | ;"object"==typeof t?(i=e, 181 | n=t.ignoreIllegals,r=t.language):(X("10.7.0","highlight(lang, code, ...args) has been deprecated."), 182 | X("10.7.0","Please use highlight(code, options) instead.\nhttps://github.com/highlightjs/highlight.js/issues/2277"), 183 | r=e,i=t),void 0===n&&(n=!0);const s={code:i,language:r};N("before:highlight",s) 184 | ;const o=s.result?s.result:E(s.language,s.code,n) 185 | ;return o.code=s.code,N("after:highlight",o),o}function E(e,n,r,s){ 186 | const c=Object.create(null);function l(){if(!k.keywords)return void M.addText(S) 187 | ;let e=0;k.keywordPatternRe.lastIndex=0;let t=k.keywordPatternRe.exec(S),n="" 188 | ;for(;t;){n+=S.substring(e,t.index) 189 | ;const r=y.case_insensitive?t[0].toLowerCase():t[0],s=(i=r,k.keywords[i]);if(s){ 190 | const[e,i]=s 191 | ;if(M.addText(n),n="",c[r]=(c[r]||0)+1,c[r]<=7&&(R+=i),e.startsWith("_"))n+=t[0];else{ 192 | const n=y.classNameAliases[e]||e;M.addKeyword(t[0],n)}}else n+=t[0] 193 | ;e=k.keywordPatternRe.lastIndex,t=k.keywordPatternRe.exec(S)}var i 194 | ;n+=S.substr(e),M.addText(n)}function d(){null!=k.subLanguage?(()=>{ 195 | if(""===S)return;let e=null;if("string"==typeof k.subLanguage){ 196 | if(!t[k.subLanguage])return void M.addText(S) 197 | ;e=E(k.subLanguage,S,!0,N[k.subLanguage]),N[k.subLanguage]=e._top 198 | }else e=x(S,k.subLanguage.length?k.subLanguage:null) 199 | ;k.relevance>0&&(R+=e.relevance),M.addSublanguage(e._emitter,e.language) 200 | })():l(),S=""}function u(e,t){let n=1;for(;void 0!==t[n];){if(!e._emit[n]){n++ 201 | ;continue}const i=y.classNameAliases[e[n]]||e[n],r=t[n] 202 | ;i?M.addKeyword(r,i):(S=r,l(),S=""),n++}}function h(e,t){ 203 | return e.scope&&"string"==typeof e.scope&&M.openNode(y.classNameAliases[e.scope]||e.scope), 204 | e.beginScope&&(e.beginScope._wrap?(M.addKeyword(S,y.classNameAliases[e.beginScope._wrap]||e.beginScope._wrap), 205 | S=""):e.beginScope._multi&&(u(e.beginScope,t),S="")),k=Object.create(e,{parent:{ 206 | value:k}}),k}function f(e,t,n){let r=((e,t)=>{const n=e&&e.exec(t) 207 | ;return n&&0===n.index})(e.endRe,n);if(r){if(e["on:end"]){const n=new i(e) 208 | ;e["on:end"](t,n),n.isMatchIgnored&&(r=!1)}if(r){ 209 | for(;e.endsParent&&e.parent;)e=e.parent;return e}} 210 | if(e.endsWithParent)return f(e.parent,t,n)}function p(e){ 211 | return 0===k.matcher.regexIndex?(S+=e[0],1):(I=!0,0)}function b(e){ 212 | const t=e[0],i=n.substr(e.index),r=f(k,e,i);if(!r)return ee;const s=k 213 | ;k.endScope&&k.endScope._wrap?(d(), 214 | M.addKeyword(t,k.endScope._wrap)):k.endScope&&k.endScope._multi?(d(), 215 | u(k.endScope,e)):s.skip?S+=t:(s.returnEnd||s.excludeEnd||(S+=t), 216 | d(),s.excludeEnd&&(S=t));do{ 217 | k.scope&&M.closeNode(),k.skip||k.subLanguage||(R+=k.relevance),k=k.parent 218 | }while(k!==r.parent);return r.starts&&h(r.starts,e),s.returnEnd?0:t.length} 219 | let m={};function w(t,s){const a=s&&s[0];if(S+=t,null==a)return d(),0 220 | ;if("begin"===m.type&&"end"===s.type&&m.index===s.index&&""===a){ 221 | if(S+=n.slice(s.index,s.index+1),!o){const t=Error(`0 width match regex (${e})`) 222 | ;throw t.languageName=e,t.badRule=m.rule,t}return 1} 223 | if(m=s,"begin"===s.type)return(e=>{ 224 | const t=e[0],n=e.rule,r=new i(n),s=[n.__beforeBegin,n["on:begin"]] 225 | ;for(const n of s)if(n&&(n(e,r),r.isMatchIgnored))return p(t) 226 | ;return n.skip?S+=t:(n.excludeBegin&&(S+=t), 227 | d(),n.returnBegin||n.excludeBegin||(S=t)),h(n,e),n.returnBegin?0:t.length})(s) 228 | ;if("illegal"===s.type&&!r){ 229 | const e=Error('Illegal lexeme "'+a+'" for mode "'+(k.scope||"")+'"') 230 | ;throw e.mode=k,e}if("end"===s.type){const e=b(s);if(e!==ee)return e} 231 | if("illegal"===s.type&&""===a)return 1 232 | ;if(A>1e5&&A>3*s.index)throw Error("potential infinite loop, way more iterations than matches") 233 | ;return S+=a,a.length}const y=v(e) 234 | ;if(!y)throw K(a.replace("{}",e)),Error('Unknown language: "'+e+'"') 235 | ;const _=V(y);let O="",k=s||_;const N={},M=new g.__emitter(g);(()=>{const e=[] 236 | ;for(let t=k;t!==y;t=t.parent)t.scope&&e.unshift(t.scope) 237 | ;e.forEach((e=>M.openNode(e)))})();let S="",R=0,j=0,A=0,I=!1;try{ 238 | for(k.matcher.considerAll();;){ 239 | A++,I?I=!1:k.matcher.considerAll(),k.matcher.lastIndex=j 240 | ;const e=k.matcher.exec(n);if(!e)break;const t=w(n.substring(j,e.index),e) 241 | ;j=e.index+t}return w(n.substr(j)),M.closeAllNodes(),M.finalize(),O=M.toHTML(),{ 242 | language:e,value:O,relevance:R,illegal:!1,_emitter:M,_top:k}}catch(t){ 243 | if(t.message&&t.message.includes("Illegal"))return{language:e,value:Y(n), 244 | illegal:!0,relevance:0,_illegalBy:{message:t.message,index:j, 245 | context:n.slice(j-100,j+100),mode:t.mode,resultSoFar:O},_emitter:M};if(o)return{ 246 | language:e,value:Y(n),illegal:!1,relevance:0,errorRaised:t,_emitter:M,_top:k} 247 | ;throw t}}function x(e,n){n=n||g.languages||Object.keys(t);const i=(e=>{ 248 | const t={value:Y(e),illegal:!1,relevance:0,_top:c,_emitter:new g.__emitter(g)} 249 | ;return t._emitter.addText(e),t})(e),r=n.filter(v).filter(k).map((t=>E(t,e,!1))) 250 | ;r.unshift(i);const s=r.sort(((e,t)=>{ 251 | if(e.relevance!==t.relevance)return t.relevance-e.relevance 252 | ;if(e.language&&t.language){if(v(e.language).supersetOf===t.language)return 1 253 | ;if(v(t.language).supersetOf===e.language)return-1}return 0})),[o,a]=s,l=o 254 | ;return l.secondBest=a,l}function w(e){let t=null;const n=(e=>{ 255 | let t=e.className+" ";t+=e.parentNode?e.parentNode.className:"" 256 | ;const n=g.languageDetectRe.exec(t);if(n){const t=v(n[1]) 257 | ;return t||(W(a.replace("{}",n[1])), 258 | W("Falling back to no-highlight mode for this block.",e)),t?n[1]:"no-highlight"} 259 | return t.split(/\s+/).find((e=>b(e)||v(e)))})(e);if(b(n))return 260 | ;if(N("before:highlightElement",{el:e,language:n 261 | }),e.children.length>0&&(g.ignoreUnescapedHTML||(console.warn("One of your code blocks includes unescaped HTML. This is a potentially serious security risk."), 262 | console.warn("https://github.com/highlightjs/highlight.js/issues/2886"), 263 | console.warn(e)), 264 | g.throwUnescapedHTML))throw new J("One of your code blocks includes unescaped HTML.",e.innerHTML) 265 | ;t=e;const i=t.textContent,s=n?m(i,{language:n,ignoreIllegals:!0}):x(i) 266 | ;e.innerHTML=s.value,((e,t,n)=>{const i=t&&r[t]||n 267 | ;e.classList.add("hljs"),e.classList.add("language-"+i) 268 | })(e,n,s.language),e.result={language:s.language,re:s.relevance, 269 | relevance:s.relevance},s.secondBest&&(e.secondBest={ 270 | language:s.secondBest.language,relevance:s.secondBest.relevance 271 | }),N("after:highlightElement",{el:e,result:s,text:i})}let y=!1;function _(){ 272 | "loading"!==document.readyState?document.querySelectorAll(g.cssSelector).forEach(w):y=!0 273 | }function v(e){return e=(e||"").toLowerCase(),t[e]||t[r[e]]} 274 | function O(e,{languageName:t}){"string"==typeof e&&(e=[e]),e.forEach((e=>{ 275 | r[e.toLowerCase()]=t}))}function k(e){const t=v(e) 276 | ;return t&&!t.disableAutodetect}function N(e,t){const n=e;s.forEach((e=>{ 277 | e[n]&&e[n](t)}))} 278 | "undefined"!=typeof window&&window.addEventListener&&window.addEventListener("DOMContentLoaded",(()=>{ 279 | y&&_()}),!1),Object.assign(e,{highlight:m,highlightAuto:x,highlightAll:_, 280 | highlightElement:w, 281 | highlightBlock:e=>(X("10.7.0","highlightBlock will be removed entirely in v12.0"), 282 | X("10.7.0","Please use highlightElement now."),w(e)),configure:e=>{g=Q(g,e)}, 283 | initHighlighting:()=>{ 284 | _(),X("10.6.0","initHighlighting() deprecated. Use highlightAll() now.")}, 285 | initHighlightingOnLoad:()=>{ 286 | _(),X("10.6.0","initHighlightingOnLoad() deprecated. Use highlightAll() now.") 287 | },registerLanguage:(n,i)=>{let r=null;try{r=i(e)}catch(e){ 288 | if(K("Language definition for '{}' could not be registered.".replace("{}",n)), 289 | !o)throw e;K(e),r=c} 290 | r.name||(r.name=n),t[n]=r,r.rawDefinition=i.bind(null,e),r.aliases&&O(r.aliases,{ 291 | languageName:n})},unregisterLanguage:e=>{delete t[e] 292 | ;for(const t of Object.keys(r))r[t]===e&&delete r[t]}, 293 | listLanguages:()=>Object.keys(t),getLanguage:v,registerAliases:O, 294 | autoDetection:k,inherit:Q,addPlugin:e=>{(e=>{ 295 | e["before:highlightBlock"]&&!e["before:highlightElement"]&&(e["before:highlightElement"]=t=>{ 296 | e["before:highlightBlock"](Object.assign({block:t.el},t)) 297 | }),e["after:highlightBlock"]&&!e["after:highlightElement"]&&(e["after:highlightElement"]=t=>{ 298 | e["after:highlightBlock"](Object.assign({block:t.el},t))})})(e),s.push(e)} 299 | }),e.debugMode=()=>{o=!1},e.safeMode=()=>{o=!0 300 | },e.versionString="11.3.1",e.regex={concat:f,lookahead:d,either:p,optional:h, 301 | anyNumberOfTimes:u};for(const e in A)"object"==typeof A[e]&&n(A[e]) 302 | ;return Object.assign(e,A),e})({});return te}() 303 | ;"object"==typeof exports&&"undefined"!=typeof module&&(module.exports=hljs);/*! `kotlin` grammar compiled for Highlight.js 11.3.1 */ 304 | (()=>{var e=(()=>{"use strict" 305 | ;var e="\\.([0-9](_*[0-9])*)",n="[0-9a-fA-F](_*[0-9a-fA-F])*",a={ 306 | className:"number",variants:[{ 307 | begin:`(\\b([0-9](_*[0-9])*)((${e})|\\.)?|(${e}))[eE][+-]?([0-9](_*[0-9])*)[fFdD]?\\b` 308 | },{begin:`\\b([0-9](_*[0-9])*)((${e})[fFdD]?\\b|\\.([fFdD]\\b)?)`},{ 309 | begin:`(${e})[fFdD]?\\b`},{begin:"\\b([0-9](_*[0-9])*)[fFdD]\\b"},{ 310 | begin:`\\b0[xX]((${n})\\.?|(${n})?\\.(${n}))[pP][+-]?([0-9](_*[0-9])*)[fFdD]?\\b` 311 | },{begin:"\\b(0|[1-9](_*[0-9])*)[lL]?\\b"},{begin:`\\b0[xX](${n})[lL]?\\b`},{ 312 | begin:"\\b0(_*[0-7])*[lL]?\\b"},{begin:"\\b0[bB][01](_*[01])*[lL]?\\b"}], 313 | relevance:0};return e=>{const n={ 314 | keyword:"abstract as val var vararg get set class object open private protected public noinline crossinline dynamic final enum if else do while for when throw try catch finally import package is in fun override companion reified inline lateinit init interface annotation data sealed internal infix operator out by constructor super tailrec where const inner suspend typealias external expect actual", 315 | built_in:"Byte Short Char Int Long Boolean Float Double Void Unit Nothing", 316 | literal:"true false null"},i={className:"symbol",begin:e.UNDERSCORE_IDENT_RE+"@" 317 | },s={className:"subst",begin:/\$\{/,end:/\}/,contains:[e.C_NUMBER_MODE]},t={ 318 | className:"variable",begin:"\\$"+e.UNDERSCORE_IDENT_RE},r={className:"string", 319 | variants:[{begin:'"""',end:'"""(?=[^"])',contains:[t,s]},{begin:"'",end:"'", 320 | illegal:/\n/,contains:[e.BACKSLASH_ESCAPE]},{begin:'"',end:'"',illegal:/\n/, 321 | contains:[e.BACKSLASH_ESCAPE,t,s]}]};s.contains.push(r);const l={ 322 | className:"meta", 323 | begin:"@(?:file|property|field|get|set|receiver|param|setparam|delegate)\\s*:(?:\\s*"+e.UNDERSCORE_IDENT_RE+")?" 324 | },c={className:"meta",begin:"@"+e.UNDERSCORE_IDENT_RE,contains:[{begin:/\(/, 325 | end:/\)/,contains:[e.inherit(r,{className:"string"})]}] 326 | },o=a,b=e.COMMENT("/\\*","\\*/",{contains:[e.C_BLOCK_COMMENT_MODE]}),E={ 327 | variants:[{className:"type",begin:e.UNDERSCORE_IDENT_RE},{begin:/\(/,end:/\)/, 328 | contains:[]}]},d=E;return d.variants[1].contains=[E],E.variants[1].contains=[d], 329 | {name:"Kotlin",aliases:["kt","kts"],keywords:n, 330 | contains:[e.COMMENT("/\\*\\*","\\*/",{relevance:0,contains:[{className:"doctag", 331 | begin:"@[A-Za-z]+"}]}),e.C_LINE_COMMENT_MODE,b,{className:"keyword", 332 | begin:/\b(break|continue|return|this)\b/,starts:{contains:[{className:"symbol", 333 | begin:/@\w+/}]}},i,l,c,{className:"function",beginKeywords:"fun",end:"[(]|$", 334 | returnBegin:!0,excludeEnd:!0,keywords:n,relevance:5,contains:[{ 335 | begin:e.UNDERSCORE_IDENT_RE+"\\s*\\(",returnBegin:!0,relevance:0, 336 | contains:[e.UNDERSCORE_TITLE_MODE]},{className:"type",begin://, 337 | keywords:"reified",relevance:0},{className:"params",begin:/\(/,end:/\)/, 338 | endsParent:!0,keywords:n,relevance:0,contains:[{begin:/:/,end:/[=,\/]/, 339 | endsWithParent:!0,contains:[E,e.C_LINE_COMMENT_MODE,b],relevance:0 340 | },e.C_LINE_COMMENT_MODE,b,l,c,r,e.C_NUMBER_MODE]},b]},{className:"class", 341 | beginKeywords:"class interface trait",end:/[:\{(]|$/,excludeEnd:!0, 342 | illegal:"extends implements",contains:[{ 343 | beginKeywords:"public protected internal private constructor" 344 | },e.UNDERSCORE_TITLE_MODE,{className:"type",begin://,excludeBegin:!0, 345 | excludeEnd:!0,relevance:0},{className:"type",begin:/[,:]\s*/,end:/[<\(,]|$/, 346 | excludeBegin:!0,returnEnd:!0},l,c]},r,{className:"meta",begin:"^#!/usr/bin/env", 347 | end:"$",illegal:"\n"},o]}}})();hljs.registerLanguage("kotlin",e)})(); -------------------------------------------------------------------------------- /site/.kobweb/site/dom-to-image.js: -------------------------------------------------------------------------------- 1 | (function (global) { 2 | 'use strict'; 3 | 4 | var util = newUtil(); 5 | var inliner = newInliner(); 6 | var fontFaces = newFontFaces(); 7 | var images = newImages(); 8 | 9 | // Default impl options 10 | var defaultOptions = { 11 | // Default is to fail on error, no placeholder 12 | imagePlaceholder: undefined, 13 | // Default cache bust is false, it will use the cache 14 | cacheBust: false 15 | }; 16 | 17 | var domtoimage = { 18 | toSvg: toSvg, 19 | toPng: toPng, 20 | toJpeg: toJpeg, 21 | toBlob: toBlob, 22 | toPixelData: toPixelData, 23 | impl: { 24 | fontFaces: fontFaces, 25 | images: images, 26 | util: util, 27 | inliner: inliner, 28 | options: {} 29 | } 30 | }; 31 | 32 | if (typeof module !== 'undefined') 33 | module.exports = domtoimage; 34 | else 35 | global.domtoimage = domtoimage; 36 | 37 | 38 | /** 39 | * @param {Node} node - The DOM Node object to render 40 | * @param {Object} options - Rendering options 41 | * @param {Function} options.filter - Should return true if passed node should be included in the output 42 | * (excluding node means excluding it's children as well). Not called on the root node. 43 | * @param {String} options.bgcolor - color for the background, any valid CSS color value. 44 | * @param {Number} options.width - width to be applied to node before rendering. 45 | * @param {Number} options.height - height to be applied to node before rendering. 46 | * @param {Object} options.style - an object whose properties to be copied to node's style before rendering. 47 | * @param {Number} options.quality - a Number between 0 and 1 indicating image quality (applicable to JPEG only), 48 | defaults to 1.0. 49 | * @param {String} options.imagePlaceholder - dataURL to use as a placeholder for failed images, default behaviour is to fail fast on images we can't fetch 50 | * @param {Boolean} options.cacheBust - set to true to cache bust by appending the time to the request url 51 | * @return {Promise} - A promise that is fulfilled with a SVG image data URL 52 | * */ 53 | function toSvg(node, options) { 54 | options = options || {}; 55 | copyOptions(options); 56 | return Promise.resolve(node) 57 | .then(function (node) { 58 | return cloneNode(node, options.filter, true); 59 | }) 60 | .then(embedFonts) 61 | .then(inlineImages) 62 | .then(applyOptions) 63 | .then(function (clone) { 64 | return makeSvgDataUri(clone, 65 | options.width || util.width(node), 66 | options.height || util.height(node) 67 | ); 68 | }); 69 | 70 | function applyOptions(clone) { 71 | if (options.bgcolor) clone.style.backgroundColor = options.bgcolor; 72 | 73 | if (options.width) clone.style.width = options.width + 'px'; 74 | if (options.height) clone.style.height = options.height + 'px'; 75 | 76 | if (options.style) 77 | Object.keys(options.style).forEach(function (property) { 78 | clone.style[property] = options.style[property]; 79 | }); 80 | 81 | return clone; 82 | } 83 | } 84 | 85 | /** 86 | * @param {Node} node - The DOM Node object to render 87 | * @param {Object} options - Rendering options, @see {@link toSvg} 88 | * @return {Promise} - A promise that is fulfilled with a Uint8Array containing RGBA pixel data. 89 | * */ 90 | function toPixelData(node, options) { 91 | return draw(node, options || {}) 92 | .then(function (canvas) { 93 | return canvas.getContext('2d').getImageData( 94 | 0, 95 | 0, 96 | util.width(node), 97 | util.height(node) 98 | ).data; 99 | }); 100 | } 101 | 102 | /** 103 | * @param {Node} node - The DOM Node object to render 104 | * @param {Object} options - Rendering options, @see {@link toSvg} 105 | * @return {Promise} - A promise that is fulfilled with a PNG image data URL 106 | * */ 107 | function toPng(node, options) { 108 | return draw(node, options || {}) 109 | .then(function (canvas) { 110 | return canvas.toDataURL(); 111 | }); 112 | } 113 | 114 | /** 115 | * @param {Node} node - The DOM Node object to render 116 | * @param {Object} options - Rendering options, @see {@link toSvg} 117 | * @return {Promise} - A promise that is fulfilled with a JPEG image data URL 118 | * */ 119 | function toJpeg(node, options) { 120 | options = options || {}; 121 | return draw(node, options) 122 | .then(function (canvas) { 123 | return canvas.toDataURL('image/jpeg', options.quality || 1.0); 124 | }); 125 | } 126 | 127 | /** 128 | * @param {Node} node - The DOM Node object to render 129 | * @param {Object} options - Rendering options, @see {@link toSvg} 130 | * @return {Promise} - A promise that is fulfilled with a PNG image blob 131 | * */ 132 | function toBlob(node, options) { 133 | return draw(node, options || {}) 134 | .then(util.canvasToBlob); 135 | } 136 | 137 | function copyOptions(options) { 138 | // Copy options to impl options for use in impl 139 | if(typeof(options.imagePlaceholder) === 'undefined') { 140 | domtoimage.impl.options.imagePlaceholder = defaultOptions.imagePlaceholder; 141 | } else { 142 | domtoimage.impl.options.imagePlaceholder = options.imagePlaceholder; 143 | } 144 | 145 | if(typeof(options.cacheBust) === 'undefined') { 146 | domtoimage.impl.options.cacheBust = defaultOptions.cacheBust; 147 | } else { 148 | domtoimage.impl.options.cacheBust = options.cacheBust; 149 | } 150 | } 151 | 152 | function draw(domNode, options) { 153 | return toSvg(domNode, options) 154 | .then(util.makeImage) 155 | .then(util.delay(100)) 156 | .then(function (image) { 157 | var canvas = newCanvas(domNode); 158 | canvas.getContext('2d').drawImage(image, 0, 0); 159 | return canvas; 160 | }); 161 | 162 | function newCanvas(domNode) { 163 | var canvas = document.createElement('canvas'); 164 | canvas.width = options.width || util.width(domNode); 165 | canvas.height = options.height || util.height(domNode); 166 | 167 | if (options.bgcolor) { 168 | var ctx = canvas.getContext('2d'); 169 | ctx.fillStyle = options.bgcolor; 170 | ctx.fillRect(0, 0, canvas.width, canvas.height); 171 | } 172 | 173 | return canvas; 174 | } 175 | } 176 | 177 | function cloneNode(node, filter, root) { 178 | if (!root && filter && !filter(node)) return Promise.resolve(); 179 | 180 | return Promise.resolve(node) 181 | .then(makeNodeCopy) 182 | .then(function (clone) { 183 | return cloneChildren(node, clone, filter); 184 | }) 185 | .then(function (clone) { 186 | return processClone(node, clone); 187 | }); 188 | 189 | function makeNodeCopy(node) { 190 | if (node instanceof HTMLCanvasElement) return util.makeImage(node.toDataURL()); 191 | return node.cloneNode(false); 192 | } 193 | 194 | function cloneChildren(original, clone, filter) { 195 | var children = original.childNodes; 196 | if (children.length === 0) return Promise.resolve(clone); 197 | 198 | return cloneChildrenInOrder(clone, util.asArray(children), filter) 199 | .then(function () { 200 | return clone; 201 | }); 202 | 203 | function cloneChildrenInOrder(parent, children, filter) { 204 | var done = Promise.resolve(); 205 | children.forEach(function (child) { 206 | done = done 207 | .then(function () { 208 | return cloneNode(child, filter); 209 | }) 210 | .then(function (childClone) { 211 | if (childClone) parent.appendChild(childClone); 212 | }); 213 | }); 214 | return done; 215 | } 216 | } 217 | 218 | function processClone(original, clone) { 219 | if (!(clone instanceof Element)) return clone; 220 | 221 | return Promise.resolve() 222 | .then(cloneStyle) 223 | .then(clonePseudoElements) 224 | .then(copyUserInput) 225 | .then(fixSvg) 226 | .then(function () { 227 | return clone; 228 | }); 229 | 230 | function cloneStyle() { 231 | copyStyle(window.getComputedStyle(original), clone.style); 232 | 233 | function copyStyle(source, target) { 234 | if (source.cssText) target.cssText = source.cssText; 235 | else copyProperties(source, target); 236 | 237 | function copyProperties(source, target) { 238 | util.asArray(source).forEach(function (name) { 239 | target.setProperty( 240 | name, 241 | source.getPropertyValue(name), 242 | source.getPropertyPriority(name) 243 | ); 244 | }); 245 | } 246 | } 247 | } 248 | 249 | function clonePseudoElements() { 250 | [':before', ':after'].forEach(function (element) { 251 | clonePseudoElement(element); 252 | }); 253 | 254 | function clonePseudoElement(element) { 255 | var style = window.getComputedStyle(original, element); 256 | var content = style.getPropertyValue('content'); 257 | 258 | if (content === '' || content === 'none') return; 259 | 260 | var className = util.uid(); 261 | clone.className = clone.className + ' ' + className; 262 | var styleElement = document.createElement('style'); 263 | styleElement.appendChild(formatPseudoElementStyle(className, element, style)); 264 | clone.appendChild(styleElement); 265 | 266 | function formatPseudoElementStyle(className, element, style) { 267 | var selector = '.' + className + ':' + element; 268 | var cssText = style.cssText ? formatCssText(style) : formatCssProperties(style); 269 | return document.createTextNode(selector + '{' + cssText + '}'); 270 | 271 | function formatCssText(style) { 272 | var content = style.getPropertyValue('content'); 273 | return style.cssText + ' content: ' + content + ';'; 274 | } 275 | 276 | function formatCssProperties(style) { 277 | 278 | return util.asArray(style) 279 | .map(formatProperty) 280 | .join('; ') + ';'; 281 | 282 | function formatProperty(name) { 283 | return name + ': ' + 284 | style.getPropertyValue(name) + 285 | (style.getPropertyPriority(name) ? ' !important' : ''); 286 | } 287 | } 288 | } 289 | } 290 | } 291 | 292 | function copyUserInput() { 293 | if (original instanceof HTMLTextAreaElement) clone.innerHTML = original.value; 294 | if (original instanceof HTMLInputElement) clone.setAttribute("value", original.value); 295 | } 296 | 297 | function fixSvg() { 298 | if (!(clone instanceof SVGElement)) return; 299 | clone.setAttribute('xmlns', 'http://www.w3.org/2000/svg'); 300 | 301 | if (!(clone instanceof SVGRectElement)) return; 302 | ['width', 'height'].forEach(function (attribute) { 303 | var value = clone.getAttribute(attribute); 304 | if (!value) return; 305 | 306 | clone.style.setProperty(attribute, value); 307 | }); 308 | } 309 | } 310 | } 311 | 312 | function embedFonts(node) { 313 | return fontFaces.resolveAll() 314 | .then(function (cssText) { 315 | var styleNode = document.createElement('style'); 316 | node.appendChild(styleNode); 317 | styleNode.appendChild(document.createTextNode(cssText)); 318 | return node; 319 | }); 320 | } 321 | 322 | function inlineImages(node) { 323 | return images.inlineAll(node) 324 | .then(function () { 325 | return node; 326 | }); 327 | } 328 | 329 | function makeSvgDataUri(node, width, height) { 330 | return Promise.resolve(node) 331 | .then(function (node) { 332 | node.setAttribute('xmlns', 'http://www.w3.org/1999/xhtml'); 333 | return new XMLSerializer().serializeToString(node); 334 | }) 335 | .then(util.escapeXhtml) 336 | .then(function (xhtml) { 337 | return '' + xhtml + ''; 338 | }) 339 | .then(function (foreignObject) { 340 | return '' + 341 | foreignObject + ''; 342 | }) 343 | .then(function (svg) { 344 | return 'data:image/svg+xml;charset=utf-8,' + svg; 345 | }); 346 | } 347 | 348 | function newUtil() { 349 | return { 350 | escape: escape, 351 | parseExtension: parseExtension, 352 | mimeType: mimeType, 353 | dataAsUrl: dataAsUrl, 354 | isDataUrl: isDataUrl, 355 | canvasToBlob: canvasToBlob, 356 | resolveUrl: resolveUrl, 357 | getAndEncode: getAndEncode, 358 | uid: uid(), 359 | delay: delay, 360 | asArray: asArray, 361 | escapeXhtml: escapeXhtml, 362 | makeImage: makeImage, 363 | width: width, 364 | height: height 365 | }; 366 | 367 | function mimes() { 368 | /* 369 | * Only WOFF and EOT mime types for fonts are 'real' 370 | * see http://www.iana.org/assignments/media-types/media-types.xhtml 371 | */ 372 | var WOFF = 'application/font-woff'; 373 | var JPEG = 'image/jpeg'; 374 | 375 | return { 376 | 'woff': WOFF, 377 | 'woff2': WOFF, 378 | 'ttf': 'application/font-truetype', 379 | 'eot': 'application/vnd.ms-fontobject', 380 | 'png': 'image/png', 381 | 'jpg': JPEG, 382 | 'jpeg': JPEG, 383 | 'gif': 'image/gif', 384 | 'tiff': 'image/tiff', 385 | 'svg': 'image/svg+xml' 386 | }; 387 | } 388 | 389 | function parseExtension(url) { 390 | var match = /\.([^\.\/]*?)$/g.exec(url); 391 | if (match) return match[1]; 392 | else return ''; 393 | } 394 | 395 | function mimeType(url) { 396 | var extension = parseExtension(url).toLowerCase(); 397 | return mimes()[extension] || ''; 398 | } 399 | 400 | function isDataUrl(url) { 401 | return url.search(/^(data:)/) !== -1; 402 | } 403 | 404 | function toBlob(canvas) { 405 | return new Promise(function (resolve) { 406 | var binaryString = window.atob(canvas.toDataURL().split(',')[1]); 407 | var length = binaryString.length; 408 | var binaryArray = new Uint8Array(length); 409 | 410 | for (var i = 0; i < length; i++) 411 | binaryArray[i] = binaryString.charCodeAt(i); 412 | 413 | resolve(new Blob([binaryArray], { 414 | type: 'image/png' 415 | })); 416 | }); 417 | } 418 | 419 | function canvasToBlob(canvas) { 420 | if (canvas.toBlob) 421 | return new Promise(function (resolve) { 422 | canvas.toBlob(resolve); 423 | }); 424 | 425 | return toBlob(canvas); 426 | } 427 | 428 | function resolveUrl(url, baseUrl) { 429 | var doc = document.implementation.createHTMLDocument(); 430 | var base = doc.createElement('base'); 431 | doc.head.appendChild(base); 432 | var a = doc.createElement('a'); 433 | doc.body.appendChild(a); 434 | base.href = baseUrl; 435 | a.href = url; 436 | return a.href; 437 | } 438 | 439 | function uid() { 440 | var index = 0; 441 | 442 | return function () { 443 | return 'u' + fourRandomChars() + index++; 444 | 445 | function fourRandomChars() { 446 | /* see http://stackoverflow.com/a/6248722/2519373 */ 447 | return ('0000' + (Math.random() * Math.pow(36, 4) << 0).toString(36)).slice(-4); 448 | } 449 | }; 450 | } 451 | 452 | function makeImage(uri) { 453 | return new Promise(function (resolve, reject) { 454 | var image = new Image(); 455 | image.onload = function () { 456 | resolve(image); 457 | }; 458 | image.onerror = reject; 459 | image.src = uri; 460 | }); 461 | } 462 | 463 | function getAndEncode(url) { 464 | var TIMEOUT = 30000; 465 | if(domtoimage.impl.options.cacheBust) { 466 | // Cache bypass so we dont have CORS issues with cached images 467 | // Source: https://developer.mozilla.org/en/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequest#Bypassing_the_cache 468 | url += ((/\?/).test(url) ? "&" : "?") + (new Date()).getTime(); 469 | } 470 | 471 | return new Promise(function (resolve) { 472 | var request = new XMLHttpRequest(); 473 | 474 | request.onreadystatechange = done; 475 | request.ontimeout = timeout; 476 | request.responseType = 'blob'; 477 | request.timeout = TIMEOUT; 478 | request.open('GET', url, true); 479 | request.send(); 480 | 481 | var placeholder; 482 | if(domtoimage.impl.options.imagePlaceholder) { 483 | var split = domtoimage.impl.options.imagePlaceholder.split(/,/); 484 | if(split && split[1]) { 485 | placeholder = split[1]; 486 | } 487 | } 488 | 489 | function done() { 490 | if (request.readyState !== 4) return; 491 | 492 | if (request.status !== 200) { 493 | if(placeholder) { 494 | resolve(placeholder); 495 | } else { 496 | fail('cannot fetch resource: ' + url + ', status: ' + request.status); 497 | } 498 | 499 | return; 500 | } 501 | 502 | var encoder = new FileReader(); 503 | encoder.onloadend = function () { 504 | var content = encoder.result.split(/,/)[1]; 505 | resolve(content); 506 | }; 507 | encoder.readAsDataURL(request.response); 508 | } 509 | 510 | function timeout() { 511 | if(placeholder) { 512 | resolve(placeholder); 513 | } else { 514 | fail('timeout of ' + TIMEOUT + 'ms occured while fetching resource: ' + url); 515 | } 516 | } 517 | 518 | function fail(message) { 519 | console.error(message); 520 | resolve(''); 521 | } 522 | }); 523 | } 524 | 525 | function dataAsUrl(content, type) { 526 | return 'data:' + type + ';base64,' + content; 527 | } 528 | 529 | function escape(string) { 530 | return string.replace(/([.*+?^${}()|\[\]\/\\])/g, '\\$1'); 531 | } 532 | 533 | function delay(ms) { 534 | return function (arg) { 535 | return new Promise(function (resolve) { 536 | setTimeout(function () { 537 | resolve(arg); 538 | }, ms); 539 | }); 540 | }; 541 | } 542 | 543 | function asArray(arrayLike) { 544 | var array = []; 545 | var length = arrayLike.length; 546 | for (var i = 0; i < length; i++) array.push(arrayLike[i]); 547 | return array; 548 | } 549 | 550 | function escapeXhtml(string) { 551 | return string.replace(/#/g, '%23').replace(/\n/g, '%0A'); 552 | } 553 | 554 | function width(node) { 555 | var leftBorder = px(node, 'border-left-width'); 556 | var rightBorder = px(node, 'border-right-width'); 557 | return node.scrollWidth + leftBorder + rightBorder; 558 | } 559 | 560 | function height(node) { 561 | var topBorder = px(node, 'border-top-width'); 562 | var bottomBorder = px(node, 'border-bottom-width'); 563 | return node.scrollHeight + topBorder + bottomBorder; 564 | } 565 | 566 | function px(node, styleProperty) { 567 | var value = window.getComputedStyle(node).getPropertyValue(styleProperty); 568 | return parseFloat(value.replace('px', '')); 569 | } 570 | } 571 | 572 | function newInliner() { 573 | var URL_REGEX = /url\(['"]?([^'"]+?)['"]?\)/g; 574 | 575 | return { 576 | inlineAll: inlineAll, 577 | shouldProcess: shouldProcess, 578 | impl: { 579 | readUrls: readUrls, 580 | inline: inline 581 | } 582 | }; 583 | 584 | function shouldProcess(string) { 585 | return string.search(URL_REGEX) !== -1; 586 | } 587 | 588 | function readUrls(string) { 589 | var result = []; 590 | var match; 591 | while ((match = URL_REGEX.exec(string)) !== null) { 592 | result.push(match[1]); 593 | } 594 | return result.filter(function (url) { 595 | return !util.isDataUrl(url); 596 | }); 597 | } 598 | 599 | function inline(string, url, baseUrl, get) { 600 | return Promise.resolve(url) 601 | .then(function (url) { 602 | return baseUrl ? util.resolveUrl(url, baseUrl) : url; 603 | }) 604 | .then(get || util.getAndEncode) 605 | .then(function (data) { 606 | return util.dataAsUrl(data, util.mimeType(url)); 607 | }) 608 | .then(function (dataUrl) { 609 | return string.replace(urlAsRegex(url), '$1' + dataUrl + '$3'); 610 | }); 611 | 612 | function urlAsRegex(url) { 613 | return new RegExp('(url\\([\'"]?)(' + util.escape(url) + ')([\'"]?\\))', 'g'); 614 | } 615 | } 616 | 617 | function inlineAll(string, baseUrl, get) { 618 | if (nothingToInline()) return Promise.resolve(string); 619 | 620 | return Promise.resolve(string) 621 | .then(readUrls) 622 | .then(function (urls) { 623 | var done = Promise.resolve(string); 624 | urls.forEach(function (url) { 625 | done = done.then(function (string) { 626 | return inline(string, url, baseUrl, get); 627 | }); 628 | }); 629 | return done; 630 | }); 631 | 632 | function nothingToInline() { 633 | return !shouldProcess(string); 634 | } 635 | } 636 | } 637 | 638 | function newFontFaces() { 639 | return { 640 | resolveAll: resolveAll, 641 | impl: { 642 | readAll: readAll 643 | } 644 | }; 645 | 646 | function resolveAll() { 647 | return readAll(document) 648 | .then(function (webFonts) { 649 | return Promise.all( 650 | webFonts.map(function (webFont) { 651 | return webFont.resolve(); 652 | }) 653 | ); 654 | }) 655 | .then(function (cssStrings) { 656 | return cssStrings.join('\n'); 657 | }); 658 | } 659 | 660 | function readAll() { 661 | return Promise.resolve(util.asArray(document.styleSheets)) 662 | .then(getCssRules) 663 | .then(selectWebFontRules) 664 | .then(function (rules) { 665 | return rules.map(newWebFont); 666 | }); 667 | 668 | function selectWebFontRules(cssRules) { 669 | return cssRules 670 | .filter(function (rule) { 671 | return rule.type === CSSRule.FONT_FACE_RULE; 672 | }) 673 | .filter(function (rule) { 674 | return inliner.shouldProcess(rule.style.getPropertyValue('src')); 675 | }); 676 | } 677 | 678 | function getCssRules(styleSheets) { 679 | var cssRules = []; 680 | styleSheets.forEach(function (sheet) { 681 | try { 682 | util.asArray(sheet.cssRules || []).forEach(cssRules.push.bind(cssRules)); 683 | } catch (e) { 684 | console.log('Error while reading CSS rules from ' + sheet.href, e.toString()); 685 | } 686 | }); 687 | return cssRules; 688 | } 689 | 690 | function newWebFont(webFontRule) { 691 | return { 692 | resolve: function resolve() { 693 | var baseUrl = (webFontRule.parentStyleSheet || {}).href; 694 | return inliner.inlineAll(webFontRule.cssText, baseUrl); 695 | }, 696 | src: function () { 697 | return webFontRule.style.getPropertyValue('src'); 698 | } 699 | }; 700 | } 701 | } 702 | } 703 | 704 | function newImages() { 705 | return { 706 | inlineAll: inlineAll, 707 | impl: { 708 | newImage: newImage 709 | } 710 | }; 711 | 712 | function newImage(element) { 713 | return { 714 | inline: inline 715 | }; 716 | 717 | function inline(get) { 718 | if (util.isDataUrl(element.src)) return Promise.resolve(); 719 | 720 | return Promise.resolve(element.src) 721 | .then(get || util.getAndEncode) 722 | .then(function (data) { 723 | return util.dataAsUrl(data, util.mimeType(element.src)); 724 | }) 725 | .then(function (dataUrl) { 726 | return new Promise(function (resolve, reject) { 727 | element.onload = resolve; 728 | element.onerror = reject; 729 | element.src = dataUrl; 730 | }); 731 | }); 732 | } 733 | } 734 | 735 | function inlineAll(node) { 736 | if (!(node instanceof Element)) return Promise.resolve(node); 737 | 738 | return inlineBackground(node) 739 | .then(function () { 740 | if (node instanceof HTMLImageElement) 741 | return newImage(node).inline(); 742 | else 743 | return Promise.all( 744 | util.asArray(node.childNodes).map(function (child) { 745 | return inlineAll(child); 746 | }) 747 | ); 748 | }); 749 | 750 | function inlineBackground(node) { 751 | var background = node.style.getPropertyValue('background'); 752 | 753 | if (!background) return Promise.resolve(node); 754 | 755 | return inliner.inlineAll(background) 756 | .then(function (inlined) { 757 | node.style.setProperty( 758 | 'background', 759 | inlined, 760 | node.style.getPropertyPriority('background') 761 | ); 762 | }) 763 | .then(function () { 764 | return node; 765 | }); 766 | } 767 | } 768 | } 769 | })(this); 770 | -------------------------------------------------------------------------------- /site/src/jsMain/resources/public/dom-to-image.js: -------------------------------------------------------------------------------- 1 | (function (global) { 2 | 'use strict'; 3 | 4 | var util = newUtil(); 5 | var inliner = newInliner(); 6 | var fontFaces = newFontFaces(); 7 | var images = newImages(); 8 | 9 | // Default impl options 10 | var defaultOptions = { 11 | // Default is to fail on error, no placeholder 12 | imagePlaceholder: undefined, 13 | // Default cache bust is false, it will use the cache 14 | cacheBust: false 15 | }; 16 | 17 | var domtoimage = { 18 | toSvg: toSvg, 19 | toPng: toPng, 20 | toJpeg: toJpeg, 21 | toBlob: toBlob, 22 | toPixelData: toPixelData, 23 | impl: { 24 | fontFaces: fontFaces, 25 | images: images, 26 | util: util, 27 | inliner: inliner, 28 | options: {} 29 | } 30 | }; 31 | 32 | if (typeof module !== 'undefined') 33 | module.exports = domtoimage; 34 | else 35 | global.domtoimage = domtoimage; 36 | 37 | 38 | /** 39 | * @param {Node} node - The DOM Node object to render 40 | * @param {Object} options - Rendering options 41 | * @param {Function} options.filter - Should return true if passed node should be included in the output 42 | * (excluding node means excluding it's children as well). Not called on the root node. 43 | * @param {String} options.bgcolor - color for the background, any valid CSS color value. 44 | * @param {Number} options.width - width to be applied to node before rendering. 45 | * @param {Number} options.height - height to be applied to node before rendering. 46 | * @param {Object} options.style - an object whose properties to be copied to node's style before rendering. 47 | * @param {Number} options.quality - a Number between 0 and 1 indicating image quality (applicable to JPEG only), 48 | defaults to 1.0. 49 | * @param {String} options.imagePlaceholder - dataURL to use as a placeholder for failed images, default behaviour is to fail fast on images we can't fetch 50 | * @param {Boolean} options.cacheBust - set to true to cache bust by appending the time to the request url 51 | * @return {Promise} - A promise that is fulfilled with a SVG image data URL 52 | * */ 53 | function toSvg(node, options) { 54 | options = options || {}; 55 | copyOptions(options); 56 | return Promise.resolve(node) 57 | .then(function (node) { 58 | return cloneNode(node, options.filter, true); 59 | }) 60 | .then(embedFonts) 61 | .then(inlineImages) 62 | .then(applyOptions) 63 | .then(function (clone) { 64 | return makeSvgDataUri(clone, 65 | options.width || util.width(node), 66 | options.height || util.height(node) 67 | ); 68 | }); 69 | 70 | function applyOptions(clone) { 71 | if (options.bgcolor) clone.style.backgroundColor = options.bgcolor; 72 | 73 | if (options.width) clone.style.width = options.width + 'px'; 74 | if (options.height) clone.style.height = options.height + 'px'; 75 | 76 | if (options.style) 77 | Object.keys(options.style).forEach(function (property) { 78 | clone.style[property] = options.style[property]; 79 | }); 80 | 81 | return clone; 82 | } 83 | } 84 | 85 | /** 86 | * @param {Node} node - The DOM Node object to render 87 | * @param {Object} options - Rendering options, @see {@link toSvg} 88 | * @return {Promise} - A promise that is fulfilled with a Uint8Array containing RGBA pixel data. 89 | * */ 90 | function toPixelData(node, options) { 91 | return draw(node, options || {}) 92 | .then(function (canvas) { 93 | return canvas.getContext('2d').getImageData( 94 | 0, 95 | 0, 96 | util.width(node), 97 | util.height(node) 98 | ).data; 99 | }); 100 | } 101 | 102 | /** 103 | * @param {Node} node - The DOM Node object to render 104 | * @param {Object} options - Rendering options, @see {@link toSvg} 105 | * @return {Promise} - A promise that is fulfilled with a PNG image data URL 106 | * */ 107 | function toPng(node, options) { 108 | return draw(node, options || {}) 109 | .then(function (canvas) { 110 | return canvas.toDataURL(); 111 | }); 112 | } 113 | 114 | /** 115 | * @param {Node} node - The DOM Node object to render 116 | * @param {Object} options - Rendering options, @see {@link toSvg} 117 | * @return {Promise} - A promise that is fulfilled with a JPEG image data URL 118 | * */ 119 | function toJpeg(node, options) { 120 | options = options || {}; 121 | return draw(node, options) 122 | .then(function (canvas) { 123 | return canvas.toDataURL('image/jpeg', options.quality || 1.0); 124 | }); 125 | } 126 | 127 | /** 128 | * @param {Node} node - The DOM Node object to render 129 | * @param {Object} options - Rendering options, @see {@link toSvg} 130 | * @return {Promise} - A promise that is fulfilled with a PNG image blob 131 | * */ 132 | function toBlob(node, options) { 133 | return draw(node, options || {}) 134 | .then(util.canvasToBlob); 135 | } 136 | 137 | function copyOptions(options) { 138 | // Copy options to impl options for use in impl 139 | if(typeof(options.imagePlaceholder) === 'undefined') { 140 | domtoimage.impl.options.imagePlaceholder = defaultOptions.imagePlaceholder; 141 | } else { 142 | domtoimage.impl.options.imagePlaceholder = options.imagePlaceholder; 143 | } 144 | 145 | if(typeof(options.cacheBust) === 'undefined') { 146 | domtoimage.impl.options.cacheBust = defaultOptions.cacheBust; 147 | } else { 148 | domtoimage.impl.options.cacheBust = options.cacheBust; 149 | } 150 | } 151 | 152 | function draw(domNode, options) { 153 | return toSvg(domNode, options) 154 | .then(util.makeImage) 155 | .then(util.delay(100)) 156 | .then(function (image) { 157 | var canvas = newCanvas(domNode); 158 | canvas.getContext('2d').drawImage(image, 0, 0); 159 | return canvas; 160 | }); 161 | 162 | function newCanvas(domNode) { 163 | var canvas = document.createElement('canvas'); 164 | canvas.width = options.width || util.width(domNode); 165 | canvas.height = options.height || util.height(domNode); 166 | 167 | if (options.bgcolor) { 168 | var ctx = canvas.getContext('2d'); 169 | ctx.fillStyle = options.bgcolor; 170 | ctx.fillRect(0, 0, canvas.width, canvas.height); 171 | } 172 | 173 | return canvas; 174 | } 175 | } 176 | 177 | function cloneNode(node, filter, root) { 178 | if (!root && filter && !filter(node)) return Promise.resolve(); 179 | 180 | return Promise.resolve(node) 181 | .then(makeNodeCopy) 182 | .then(function (clone) { 183 | return cloneChildren(node, clone, filter); 184 | }) 185 | .then(function (clone) { 186 | return processClone(node, clone); 187 | }); 188 | 189 | function makeNodeCopy(node) { 190 | if (node instanceof HTMLCanvasElement) return util.makeImage(node.toDataURL()); 191 | return node.cloneNode(false); 192 | } 193 | 194 | function cloneChildren(original, clone, filter) { 195 | var children = original.childNodes; 196 | if (children.length === 0) return Promise.resolve(clone); 197 | 198 | return cloneChildrenInOrder(clone, util.asArray(children), filter) 199 | .then(function () { 200 | return clone; 201 | }); 202 | 203 | function cloneChildrenInOrder(parent, children, filter) { 204 | var done = Promise.resolve(); 205 | children.forEach(function (child) { 206 | done = done 207 | .then(function () { 208 | return cloneNode(child, filter); 209 | }) 210 | .then(function (childClone) { 211 | if (childClone) parent.appendChild(childClone); 212 | }); 213 | }); 214 | return done; 215 | } 216 | } 217 | 218 | function processClone(original, clone) { 219 | if (!(clone instanceof Element)) return clone; 220 | 221 | return Promise.resolve() 222 | .then(cloneStyle) 223 | .then(clonePseudoElements) 224 | .then(copyUserInput) 225 | .then(fixSvg) 226 | .then(function () { 227 | return clone; 228 | }); 229 | 230 | function cloneStyle() { 231 | copyStyle(window.getComputedStyle(original), clone.style); 232 | 233 | function copyStyle(source, target) { 234 | if (source.cssText) target.cssText = source.cssText; 235 | else copyProperties(source, target); 236 | 237 | function copyProperties(source, target) { 238 | util.asArray(source).forEach(function (name) { 239 | target.setProperty( 240 | name, 241 | source.getPropertyValue(name), 242 | source.getPropertyPriority(name) 243 | ); 244 | }); 245 | } 246 | } 247 | } 248 | 249 | function clonePseudoElements() { 250 | [':before', ':after'].forEach(function (element) { 251 | clonePseudoElement(element); 252 | }); 253 | 254 | function clonePseudoElement(element) { 255 | var style = window.getComputedStyle(original, element); 256 | var content = style.getPropertyValue('content'); 257 | 258 | if (content === '' || content === 'none') return; 259 | 260 | var className = util.uid(); 261 | clone.className = clone.className + ' ' + className; 262 | var styleElement = document.createElement('style'); 263 | styleElement.appendChild(formatPseudoElementStyle(className, element, style)); 264 | clone.appendChild(styleElement); 265 | 266 | function formatPseudoElementStyle(className, element, style) { 267 | var selector = '.' + className + ':' + element; 268 | var cssText = style.cssText ? formatCssText(style) : formatCssProperties(style); 269 | return document.createTextNode(selector + '{' + cssText + '}'); 270 | 271 | function formatCssText(style) { 272 | var content = style.getPropertyValue('content'); 273 | return style.cssText + ' content: ' + content + ';'; 274 | } 275 | 276 | function formatCssProperties(style) { 277 | 278 | return util.asArray(style) 279 | .map(formatProperty) 280 | .join('; ') + ';'; 281 | 282 | function formatProperty(name) { 283 | return name + ': ' + 284 | style.getPropertyValue(name) + 285 | (style.getPropertyPriority(name) ? ' !important' : ''); 286 | } 287 | } 288 | } 289 | } 290 | } 291 | 292 | function copyUserInput() { 293 | if (original instanceof HTMLTextAreaElement) clone.innerHTML = original.value; 294 | if (original instanceof HTMLInputElement) clone.setAttribute("value", original.value); 295 | } 296 | 297 | function fixSvg() { 298 | if (!(clone instanceof SVGElement)) return; 299 | clone.setAttribute('xmlns', 'http://www.w3.org/2000/svg'); 300 | 301 | if (!(clone instanceof SVGRectElement)) return; 302 | ['width', 'height'].forEach(function (attribute) { 303 | var value = clone.getAttribute(attribute); 304 | if (!value) return; 305 | 306 | clone.style.setProperty(attribute, value); 307 | }); 308 | } 309 | } 310 | } 311 | 312 | function embedFonts(node) { 313 | return fontFaces.resolveAll() 314 | .then(function (cssText) { 315 | var styleNode = document.createElement('style'); 316 | node.appendChild(styleNode); 317 | styleNode.appendChild(document.createTextNode(cssText)); 318 | return node; 319 | }); 320 | } 321 | 322 | function inlineImages(node) { 323 | return images.inlineAll(node) 324 | .then(function () { 325 | return node; 326 | }); 327 | } 328 | 329 | function makeSvgDataUri(node, width, height) { 330 | return Promise.resolve(node) 331 | .then(function (node) { 332 | node.setAttribute('xmlns', 'http://www.w3.org/1999/xhtml'); 333 | return new XMLSerializer().serializeToString(node); 334 | }) 335 | .then(util.escapeXhtml) 336 | .then(function (xhtml) { 337 | return '' + xhtml + ''; 338 | }) 339 | .then(function (foreignObject) { 340 | return '' + 341 | foreignObject + ''; 342 | }) 343 | .then(function (svg) { 344 | return 'data:image/svg+xml;charset=utf-8,' + svg; 345 | }); 346 | } 347 | 348 | function newUtil() { 349 | return { 350 | escape: escape, 351 | parseExtension: parseExtension, 352 | mimeType: mimeType, 353 | dataAsUrl: dataAsUrl, 354 | isDataUrl: isDataUrl, 355 | canvasToBlob: canvasToBlob, 356 | resolveUrl: resolveUrl, 357 | getAndEncode: getAndEncode, 358 | uid: uid(), 359 | delay: delay, 360 | asArray: asArray, 361 | escapeXhtml: escapeXhtml, 362 | makeImage: makeImage, 363 | width: width, 364 | height: height 365 | }; 366 | 367 | function mimes() { 368 | /* 369 | * Only WOFF and EOT mime types for fonts are 'real' 370 | * see http://www.iana.org/assignments/media-types/media-types.xhtml 371 | */ 372 | var WOFF = 'application/font-woff'; 373 | var JPEG = 'image/jpeg'; 374 | 375 | return { 376 | 'woff': WOFF, 377 | 'woff2': WOFF, 378 | 'ttf': 'application/font-truetype', 379 | 'eot': 'application/vnd.ms-fontobject', 380 | 'png': 'image/png', 381 | 'jpg': JPEG, 382 | 'jpeg': JPEG, 383 | 'gif': 'image/gif', 384 | 'tiff': 'image/tiff', 385 | 'svg': 'image/svg+xml' 386 | }; 387 | } 388 | 389 | function parseExtension(url) { 390 | var match = /\.([^\.\/]*?)$/g.exec(url); 391 | if (match) return match[1]; 392 | else return ''; 393 | } 394 | 395 | function mimeType(url) { 396 | var extension = parseExtension(url).toLowerCase(); 397 | return mimes()[extension] || ''; 398 | } 399 | 400 | function isDataUrl(url) { 401 | return url.search(/^(data:)/) !== -1; 402 | } 403 | 404 | function toBlob(canvas) { 405 | return new Promise(function (resolve) { 406 | var binaryString = window.atob(canvas.toDataURL().split(',')[1]); 407 | var length = binaryString.length; 408 | var binaryArray = new Uint8Array(length); 409 | 410 | for (var i = 0; i < length; i++) 411 | binaryArray[i] = binaryString.charCodeAt(i); 412 | 413 | resolve(new Blob([binaryArray], { 414 | type: 'image/png' 415 | })); 416 | }); 417 | } 418 | 419 | function canvasToBlob(canvas) { 420 | if (canvas.toBlob) 421 | return new Promise(function (resolve) { 422 | canvas.toBlob(resolve); 423 | }); 424 | 425 | return toBlob(canvas); 426 | } 427 | 428 | function resolveUrl(url, baseUrl) { 429 | var doc = document.implementation.createHTMLDocument(); 430 | var base = doc.createElement('base'); 431 | doc.head.appendChild(base); 432 | var a = doc.createElement('a'); 433 | doc.body.appendChild(a); 434 | base.href = baseUrl; 435 | a.href = url; 436 | return a.href; 437 | } 438 | 439 | function uid() { 440 | var index = 0; 441 | 442 | return function () { 443 | return 'u' + fourRandomChars() + index++; 444 | 445 | function fourRandomChars() { 446 | /* see http://stackoverflow.com/a/6248722/2519373 */ 447 | return ('0000' + (Math.random() * Math.pow(36, 4) << 0).toString(36)).slice(-4); 448 | } 449 | }; 450 | } 451 | 452 | function makeImage(uri) { 453 | return new Promise(function (resolve, reject) { 454 | var image = new Image(); 455 | image.onload = function () { 456 | resolve(image); 457 | }; 458 | image.onerror = reject; 459 | image.src = uri; 460 | }); 461 | } 462 | 463 | function getAndEncode(url) { 464 | var TIMEOUT = 30000; 465 | if(domtoimage.impl.options.cacheBust) { 466 | // Cache bypass so we dont have CORS issues with cached images 467 | // Source: https://developer.mozilla.org/en/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequest#Bypassing_the_cache 468 | url += ((/\?/).test(url) ? "&" : "?") + (new Date()).getTime(); 469 | } 470 | 471 | return new Promise(function (resolve) { 472 | var request = new XMLHttpRequest(); 473 | 474 | request.onreadystatechange = done; 475 | request.ontimeout = timeout; 476 | request.responseType = 'blob'; 477 | request.timeout = TIMEOUT; 478 | request.open('GET', url, true); 479 | request.send(); 480 | 481 | var placeholder; 482 | if(domtoimage.impl.options.imagePlaceholder) { 483 | var split = domtoimage.impl.options.imagePlaceholder.split(/,/); 484 | if(split && split[1]) { 485 | placeholder = split[1]; 486 | } 487 | } 488 | 489 | function done() { 490 | if (request.readyState !== 4) return; 491 | 492 | if (request.status !== 200) { 493 | if(placeholder) { 494 | resolve(placeholder); 495 | } else { 496 | fail('cannot fetch resource: ' + url + ', status: ' + request.status); 497 | } 498 | 499 | return; 500 | } 501 | 502 | var encoder = new FileReader(); 503 | encoder.onloadend = function () { 504 | var content = encoder.result.split(/,/)[1]; 505 | resolve(content); 506 | }; 507 | encoder.readAsDataURL(request.response); 508 | } 509 | 510 | function timeout() { 511 | if(placeholder) { 512 | resolve(placeholder); 513 | } else { 514 | fail('timeout of ' + TIMEOUT + 'ms occured while fetching resource: ' + url); 515 | } 516 | } 517 | 518 | function fail(message) { 519 | console.error(message); 520 | resolve(''); 521 | } 522 | }); 523 | } 524 | 525 | function dataAsUrl(content, type) { 526 | return 'data:' + type + ';base64,' + content; 527 | } 528 | 529 | function escape(string) { 530 | return string.replace(/([.*+?^${}()|\[\]\/\\])/g, '\\$1'); 531 | } 532 | 533 | function delay(ms) { 534 | return function (arg) { 535 | return new Promise(function (resolve) { 536 | setTimeout(function () { 537 | resolve(arg); 538 | }, ms); 539 | }); 540 | }; 541 | } 542 | 543 | function asArray(arrayLike) { 544 | var array = []; 545 | var length = arrayLike.length; 546 | for (var i = 0; i < length; i++) array.push(arrayLike[i]); 547 | return array; 548 | } 549 | 550 | function escapeXhtml(string) { 551 | return string.replace(/#/g, '%23').replace(/\n/g, '%0A'); 552 | } 553 | 554 | function width(node) { 555 | var leftBorder = px(node, 'border-left-width'); 556 | var rightBorder = px(node, 'border-right-width'); 557 | return node.scrollWidth + leftBorder + rightBorder; 558 | } 559 | 560 | function height(node) { 561 | var topBorder = px(node, 'border-top-width'); 562 | var bottomBorder = px(node, 'border-bottom-width'); 563 | return node.scrollHeight + topBorder + bottomBorder; 564 | } 565 | 566 | function px(node, styleProperty) { 567 | var value = window.getComputedStyle(node).getPropertyValue(styleProperty); 568 | return parseFloat(value.replace('px', '')); 569 | } 570 | } 571 | 572 | function newInliner() { 573 | var URL_REGEX = /url\(['"]?([^'"]+?)['"]?\)/g; 574 | 575 | return { 576 | inlineAll: inlineAll, 577 | shouldProcess: shouldProcess, 578 | impl: { 579 | readUrls: readUrls, 580 | inline: inline 581 | } 582 | }; 583 | 584 | function shouldProcess(string) { 585 | return string.search(URL_REGEX) !== -1; 586 | } 587 | 588 | function readUrls(string) { 589 | var result = []; 590 | var match; 591 | while ((match = URL_REGEX.exec(string)) !== null) { 592 | result.push(match[1]); 593 | } 594 | return result.filter(function (url) { 595 | return !util.isDataUrl(url); 596 | }); 597 | } 598 | 599 | function inline(string, url, baseUrl, get) { 600 | return Promise.resolve(url) 601 | .then(function (url) { 602 | return baseUrl ? util.resolveUrl(url, baseUrl) : url; 603 | }) 604 | .then(get || util.getAndEncode) 605 | .then(function (data) { 606 | return util.dataAsUrl(data, util.mimeType(url)); 607 | }) 608 | .then(function (dataUrl) { 609 | return string.replace(urlAsRegex(url), '$1' + dataUrl + '$3'); 610 | }); 611 | 612 | function urlAsRegex(url) { 613 | return new RegExp('(url\\([\'"]?)(' + util.escape(url) + ')([\'"]?\\))', 'g'); 614 | } 615 | } 616 | 617 | function inlineAll(string, baseUrl, get) { 618 | if (nothingToInline()) return Promise.resolve(string); 619 | 620 | return Promise.resolve(string) 621 | .then(readUrls) 622 | .then(function (urls) { 623 | var done = Promise.resolve(string); 624 | urls.forEach(function (url) { 625 | done = done.then(function (string) { 626 | return inline(string, url, baseUrl, get); 627 | }); 628 | }); 629 | return done; 630 | }); 631 | 632 | function nothingToInline() { 633 | return !shouldProcess(string); 634 | } 635 | } 636 | } 637 | 638 | function newFontFaces() { 639 | return { 640 | resolveAll: resolveAll, 641 | impl: { 642 | readAll: readAll 643 | } 644 | }; 645 | 646 | function resolveAll() { 647 | return readAll(document) 648 | .then(function (webFonts) { 649 | return Promise.all( 650 | webFonts.map(function (webFont) { 651 | return webFont.resolve(); 652 | }) 653 | ); 654 | }) 655 | .then(function (cssStrings) { 656 | return cssStrings.join('\n'); 657 | }); 658 | } 659 | 660 | function readAll() { 661 | return Promise.resolve(util.asArray(document.styleSheets)) 662 | .then(getCssRules) 663 | .then(selectWebFontRules) 664 | .then(function (rules) { 665 | return rules.map(newWebFont); 666 | }); 667 | 668 | function selectWebFontRules(cssRules) { 669 | return cssRules 670 | .filter(function (rule) { 671 | return rule.type === CSSRule.FONT_FACE_RULE; 672 | }) 673 | .filter(function (rule) { 674 | return inliner.shouldProcess(rule.style.getPropertyValue('src')); 675 | }); 676 | } 677 | 678 | function getCssRules(styleSheets) { 679 | var cssRules = []; 680 | styleSheets.forEach(function (sheet) { 681 | try { 682 | util.asArray(sheet.cssRules || []).forEach(cssRules.push.bind(cssRules)); 683 | } catch (e) { 684 | console.log('Error while reading CSS rules from ' + sheet.href, e.toString()); 685 | } 686 | }); 687 | return cssRules; 688 | } 689 | 690 | function newWebFont(webFontRule) { 691 | return { 692 | resolve: function resolve() { 693 | var baseUrl = (webFontRule.parentStyleSheet || {}).href; 694 | return inliner.inlineAll(webFontRule.cssText, baseUrl); 695 | }, 696 | src: function () { 697 | return webFontRule.style.getPropertyValue('src'); 698 | } 699 | }; 700 | } 701 | } 702 | } 703 | 704 | function newImages() { 705 | return { 706 | inlineAll: inlineAll, 707 | impl: { 708 | newImage: newImage 709 | } 710 | }; 711 | 712 | function newImage(element) { 713 | return { 714 | inline: inline 715 | }; 716 | 717 | function inline(get) { 718 | if (util.isDataUrl(element.src)) return Promise.resolve(); 719 | 720 | return Promise.resolve(element.src) 721 | .then(get || util.getAndEncode) 722 | .then(function (data) { 723 | return util.dataAsUrl(data, util.mimeType(element.src)); 724 | }) 725 | .then(function (dataUrl) { 726 | return new Promise(function (resolve, reject) { 727 | element.onload = resolve; 728 | element.onerror = reject; 729 | element.src = dataUrl; 730 | }); 731 | }); 732 | } 733 | } 734 | 735 | function inlineAll(node) { 736 | if (!(node instanceof Element)) return Promise.resolve(node); 737 | 738 | return inlineBackground(node) 739 | .then(function () { 740 | if (node instanceof HTMLImageElement) 741 | return newImage(node).inline(); 742 | else 743 | return Promise.all( 744 | util.asArray(node.childNodes).map(function (child) { 745 | return inlineAll(child); 746 | }) 747 | ); 748 | }); 749 | 750 | function inlineBackground(node) { 751 | var background = node.style.getPropertyValue('background'); 752 | 753 | if (!background) return Promise.resolve(node); 754 | 755 | return inliner.inlineAll(background) 756 | .then(function (inlined) { 757 | node.style.setProperty( 758 | 'background', 759 | inlined, 760 | node.style.getPropertyPriority('background') 761 | ); 762 | }) 763 | .then(function () { 764 | return node; 765 | }); 766 | } 767 | } 768 | } 769 | })(this); 770 | --------------------------------------------------------------------------------