├── .github ├── FUNDING.yml ├── dependabot.yml └── workflows │ └── actions.yml ├── .gitignore ├── .mill-version ├── LICENSE ├── build.mill ├── build.sbt ├── example ├── resources │ ├── index-dev.html │ └── index-opt.html └── src │ └── example │ └── ScalaJSExample.scala ├── mill ├── package-lock.json ├── package.json ├── project ├── Constants.scala ├── build.properties └── build.sbt ├── readme.md ├── readme ├── Readme.scalatex └── resources │ ├── Autocomplete.png │ ├── ErrorHighlighting.png │ ├── InlineDocs.png │ └── favicon.png └── scalatags ├── src-2 └── scalatags │ └── stylesheet │ └── SourceClasses.scala ├── src-3 └── scalatags │ └── stylesheet │ └── SourceClasses.scala ├── src-js └── scalatags │ ├── JsDom.scala │ └── jsdom │ ├── Frag.scala │ ├── SvgTags.scala │ ├── TagFactory.scala │ ├── Tags.scala │ └── Tags2.scala ├── src └── scalatags │ ├── DataTypes.scala │ ├── Escaping.scala │ ├── Text.scala │ ├── VirtualDom.scala │ ├── generic │ ├── Attrs.scala │ ├── Bundle.scala │ ├── Core.scala │ ├── Styles.scala │ ├── SvgAttrs.scala │ ├── SvgTags.scala │ ├── Tags.scala │ ├── Tags2.scala │ └── Util.scala │ ├── package.scala │ ├── stylesheet │ ├── Core.scala │ └── StyleSheet.scala │ ├── text │ ├── Builder.scala │ ├── SvgTags.scala │ ├── TagFactory.scala │ ├── Tags.scala │ └── Tags2.scala │ └── vdom │ ├── Builder.scala │ ├── SvgTags.scala │ ├── TagFactory.scala │ ├── Tags.scala │ └── Tags2.scala └── test ├── resource ├── page.jade ├── page.mustache ├── para.jade └── para.mustache ├── src-js └── scalatags │ └── jsdom │ ├── BasicTests.scala │ ├── DomTests.scala │ ├── ExampleTests.scala │ ├── PerfTests.scala │ └── StyleSheetTests.scala ├── src-jvm └── scalatags │ ├── generic │ └── ExampleTestsJvmOnly.scala │ └── text │ └── PerfTests.scala ├── src └── scalatags │ ├── Main.scala │ ├── TestUtil.scala │ ├── generic │ ├── BasicTests.scala │ ├── ExampleTests.scala │ ├── PerfTests.scala │ └── StyleSheetTests.scala │ └── text │ ├── BasicTests.scala │ ├── BundlingTests.scala │ ├── ExampleTests.scala │ ├── PerfTests.scala │ ├── StyleSheetTests.scala │ └── TextTests.scala └── twirl ├── page.scala.html └── para.scala.html /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: lihaoyi 4 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | 4 | - package-ecosystem: "github-actions" 5 | directory: "/" 6 | schedule: 7 | interval: "monthly" 8 | 9 | - package-ecosystem: "npm" 10 | directory: "/" 11 | schedule: 12 | interval: "monthly" 13 | -------------------------------------------------------------------------------- /.github/workflows/actions.yml: -------------------------------------------------------------------------------- 1 | name: ci 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | tags: 8 | - '*' 9 | pull_request: 10 | branches: 11 | - master 12 | 13 | jobs: 14 | test: 15 | runs-on: ubuntu-latest 16 | strategy: 17 | matrix: 18 | java: ['8', '17'] 19 | steps: 20 | - uses: actions/checkout@v3 21 | - uses: actions/setup-java@v3 22 | with: 23 | distribution: 'temurin' 24 | java-version: ${{ matrix.java }} 25 | 26 | - name: Install node and jsdom 27 | run: npm install 28 | 29 | - name: Run tests 30 | run: | 31 | ./mill -i --disable-ticker __.resolvedIvyDeps 32 | ./mill -i --disable-ticker -j 0 __.publishArtifacts __.test 33 | 34 | check-binary-compatibility: 35 | runs-on: ubuntu-latest 36 | steps: 37 | - uses: actions/checkout@v3 38 | with: 39 | fetch-depth: 0 40 | - uses: actions/setup-java@v3 41 | with: 42 | distribution: 'temurin' 43 | java-version: 17 44 | - name: Check Binary Compatibility 45 | run: ./mill -i --disable-ticker __.mimaReportBinaryIssues 46 | 47 | publish-sonatype: 48 | if: github.repository == 'com-lihaoyi/scalatags' && contains(github.ref, 'refs/tags/') 49 | needs: test 50 | runs-on: ubuntu-latest 51 | env: 52 | MILL_SONATYPE_USERNAME: ${{ secrets.SONATYPE_USERNAME }} 53 | MILL_SONATYPE_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }} 54 | MILL_PGP_SECRET_BASE64: ${{ secrets.SONATYPE_PGP_PRIVATE_KEY }} 55 | MILL_PGP_PASSPHRASE: ${{ secrets.SONATYPE_PGP_PRIVATE_KEY_PASSWORD }} 56 | LANG: "en_US.UTF-8" 57 | LC_MESSAGES: "en_US.UTF-8" 58 | LC_ALL: "en_US.UTF-8" 59 | 60 | steps: 61 | - uses: actions/checkout@v3 62 | - uses: actions/setup-java@v3 63 | with: 64 | distribution: 'temurin' 65 | java-version: 11 66 | - name: Publish to Maven Central 67 | run: ./mill -i mill.scalalib.PublishModule/ 68 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target/* 2 | example/target/* 3 | scalatags/target/* 4 | scalatags/js/target/* 5 | scalatags/jvm/target/* 6 | project/target/* 7 | project/project/target/* 8 | readme/target/* 9 | .idea/* 10 | .idea_modules/* 11 | *.iml 12 | out 13 | /node_modules 14 | /.bsp 15 | -------------------------------------------------------------------------------- /.mill-version: -------------------------------------------------------------------------------- 1 | 0.12.0 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2018 Li Haoyi (haoyi.sg@gmail.com) 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /build.mill: -------------------------------------------------------------------------------- 1 | package build 2 | import mill._, scalalib._, scalajslib._, scalanativelib._, publish._ 3 | import $ivy.`de.tototec::de.tobiasroeser.mill.vcs.version::0.4.0` 4 | import $ivy.`com.github.lolgab::mill-mima::0.0.23` 5 | import $ivy.`com.lihaoyi::mill-contrib-buildinfo:` 6 | import mill.contrib.buildinfo._ 7 | import de.tobiasroeser.mill.vcs.version.VcsVersion 8 | 9 | import com.github.lolgab.mill.mima._ 10 | import mill.scalalib.api.ZincWorkerUtil.isScala3 11 | 12 | val dottyVersions = sys.props.get("dottyVersion").toList 13 | 14 | val scalaVersions = List("2.12.17", "2.13.10", "3.3.1") ++ dottyVersions 15 | 16 | trait ScalatagsPublishModule extends CrossScalaModule with PublishModule with PlatformScalaModule with Mima{ 17 | def ivyDeps = Agg( 18 | ivy"com.lihaoyi::sourcecode::0.4.0", 19 | ivy"com.lihaoyi::geny::1.1.0", 20 | ) 21 | 22 | def compileIvyDeps = T { 23 | super.compileIvyDeps() ++ 24 | ( 25 | if (isScala3(crossScalaVersion)) Agg() 26 | else Agg( 27 | ivy"org.scala-lang:scala-reflect:${scalaVersion()}" 28 | ) 29 | ) 30 | } 31 | 32 | def publishVersion = VcsVersion.vcsState().format() 33 | def pomSettings = PomSettings( 34 | description = artifactName(), 35 | organization = "com.lihaoyi", 36 | url = "https://github.com/com-lihaoyi/scalatags", 37 | licenses = Seq(License.MIT), 38 | versionControl = VersionControl( 39 | browsableRepository = Some("git://github.com/com-lihaoyi/scalatags.git"), 40 | connection = Some("scm:git://github.com/com-lihaoyi/scalatags.git") 41 | ), 42 | developers = Seq( 43 | Developer("lihaoyi", "Li Haoyi", "https://github.com/lihaoyi") 44 | ) 45 | ) 46 | 47 | def mimaReportBinaryIssues() = 48 | if (crossScalaVersion.startsWith("3.") || this.isInstanceOf[ScalaNativeModule] || this.isInstanceOf[ScalaJSModule]) T.command() 49 | else super.mimaReportBinaryIssues() 50 | 51 | def mimaPreviousVersions = Seq("0.11.0", "0.11.1", "0.12.0") 52 | } 53 | 54 | trait CommonTestModule extends ScalaModule with TestModule.Utest with BuildInfo { 55 | def ivyDeps = Agg( 56 | ivy"com.lihaoyi::utest::0.8.3", 57 | ivy"org.scala-lang.modules::scala-xml:${if(scalaVersion().startsWith("2.11.")) "1.3.0" else "2.1.0"}" 58 | ) 59 | 60 | override def buildInfoPackageName = "scalatags" 61 | override def buildInfoMembers = Seq( 62 | BuildInfo.Value("scalaMajorVersion", scalaVersion().split('.').head) 63 | ) 64 | } 65 | 66 | 67 | object scalatags extends Module { 68 | object jvm extends Cross[JvmScalatagsModule](scalaVersions) 69 | trait JvmScalatagsModule 70 | extends ScalatagsPublishModule { 71 | 72 | object test extends ScalaTests with CommonTestModule 73 | } 74 | 75 | object js extends Cross[JSScalatagsModule](scalaVersions) 76 | trait JSScalatagsModule 77 | extends ScalaJSModule with ScalatagsPublishModule { 78 | def scalaJSVersion = "1.12.0" 79 | def ivyDeps = super.ivyDeps() ++ Agg(ivy"org.scala-js::scalajs-dom::2.3.0") 80 | private def sourceMapOptions = T.task { 81 | val vcsState = VcsVersion.vcsState() 82 | vcsState.lastTag.collect { 83 | case tag if vcsState.commitsSinceLastTag == 0 => 84 | val baseUrl = 85 | pomSettings().url.replace("github.com", "raw.githubusercontent.com") 86 | val sourcesOptionName = 87 | if (isScala3(crossScalaVersion)) 88 | "-scalajs-mapSourceURI" 89 | else "-P:scalajs:mapSourceURI" 90 | s"$sourcesOptionName:${T.workspace.toIO.toURI}->$baseUrl/$tag/" 91 | } 92 | } 93 | override def scalacOptions = super.scalacOptions() ++ sourceMapOptions() 94 | 95 | object test extends ScalaJSTests with CommonTestModule{ 96 | def ivyDeps = super.ivyDeps() ++ Agg(ivy"org.scala-js::scalajs-env-jsdom-nodejs:1.1.0").map(_.withDottyCompat(crossScalaVersion)) 97 | def jsEnvConfig = mill.scalajslib.api.JsEnvConfig.JsDom() 98 | } 99 | } 100 | 101 | object native extends Cross[NativeScalatagsModule](scalaVersions) 102 | trait NativeScalatagsModule extends ScalaNativeModule with ScalatagsPublishModule { 103 | def scalaNativeVersion = "0.5.0" 104 | // No released Scala Native Scala 3 version yet 105 | def mimaPreviousArtifacts = if(isScala3(crossScalaVersion)) Agg.empty[Dep] else super.mimaPreviousArtifacts() 106 | object test extends ScalaNativeTests with CommonTestModule 107 | } 108 | } 109 | 110 | object example extends ScalaJSModule{ 111 | def scalaVersion = scalaVersions.head 112 | def scalaJSVersion = "1.12.0" 113 | def moduleDeps = Seq(scalatags.js(scalaVersions.head)) 114 | } 115 | -------------------------------------------------------------------------------- /build.sbt: -------------------------------------------------------------------------------- 1 | 2 | lazy val readme = scalatex.ScalatexReadme( 3 | projectId = "readme", 4 | wd = file(""), 5 | url = "https://github.com/lihaoyi/scalatags/tree/master", 6 | source = "Readme", 7 | autoResources = Seq("Autocomplete.png", "ErrorHighlighting.png", "InlineDocs.png", "example-opt.js") 8 | ).settings( 9 | scalaVersion := "2.12.8", 10 | (unmanagedSources in Compile) += baseDirectory.value/".."/"project"/"Constants.scala", 11 | (resources in Compile) += (fullOptJS in (example, Compile)).value.data, 12 | (resources in Compile) += (doc in (scalatagsJS, Compile)).value, 13 | (run in Compile) := (run in Compile).dependsOn(Def.task{ 14 | sbt.IO.copyDirectory( 15 | (doc in (scalatagsJS, Compile)).value, 16 | (target in Compile).value/"scalatex"/"api", 17 | overwrite = true 18 | ) 19 | }).evaluated 20 | ) 21 | -------------------------------------------------------------------------------- /example/resources/index-dev.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Example Scala.js application 5 | 6 | 7 | 8 | 9 | 10 | 11 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /example/resources/index-opt.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Example Scala.js application 5 | 6 | 7 | 8 | 9 | 10 | 11 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /example/src/example/ScalaJSExample.scala: -------------------------------------------------------------------------------- 1 | package example 2 | import scala.scalajs.js.annotation.JSExportTopLevel 3 | import org.scalajs.dom 4 | class Example[Builder, Output <: FragT, FragT] 5 | (val bundle: scalatags.generic.Bundle[Builder, Output, FragT]){ 6 | val htmlFrag = { 7 | import bundle.all._ 8 | div( 9 | h1("Header 1"), 10 | h2("Header 2"), 11 | h3("Header 3"), 12 | h4("Header 4"), 13 | h5("Header 5"), 14 | h6("Header 6"), 15 | hr, 16 | p( 17 | "This is a paragraph with a whole bunch of ", 18 | "text inside. You can have lots and lots of ", 19 | "text" 20 | ), 21 | b("bold"), " ", 22 | i("italic"), " ", 23 | s("strikethrough"), " ", 24 | u("underlined"), " ", 25 | em("emphasis"), " ", 26 | strong("strong"), " ", 27 | sub("sub"), " ", 28 | sup("sup"), " ", 29 | code("code"), " ", 30 | a("a-link"), " ", 31 | small("small"), " ", 32 | cite("cite"), " ", 33 | ins("ins"), " ", 34 | del("del"), " ", 35 | hr, 36 | ol( 37 | li("Ordered List Item 1"), 38 | li("Ordered List Item 2"), 39 | li("Ordered List Item 3") 40 | ), 41 | ul( 42 | li("Unordered List Item 1"), 43 | li("Unordered List Item 2"), 44 | li("Unordered List Item 3") 45 | ), 46 | dl( 47 | dt("Definition List Term 1"), 48 | dd("Definition List Item 1"), 49 | dt("Definition List Term 2"), 50 | dd("Definition List Item 2") 51 | ), 52 | hr, 53 | pre( 54 | """ 55 | |Preserved formatting area with all the whitespace 56 | |kept around and probably some kind of monospace font 57 | """.stripMargin 58 | ), 59 | blockquote( 60 | """ 61 | |Block quote with a bunch of text inside 62 | |which really rox 63 | """.stripMargin 64 | ), 65 | hr, 66 | table( 67 | caption("This is a table caption"), 68 | thead( 69 | tr( 70 | th("Header Content 1"), 71 | th("Header Content 2") 72 | ) 73 | ), 74 | tbody( 75 | tr( 76 | td("Body Content 1"), 77 | td("Body Content 2") 78 | ), 79 | tr( 80 | td("Body Content A"), 81 | td("Body Content B") 82 | ) 83 | ), 84 | tfoot( 85 | tr( 86 | td("Foot Content 1"), 87 | td("Foot Content 2") 88 | ) 89 | ) 90 | ), 91 | hr, 92 | label("input"), input, 93 | br, 94 | label("select"), 95 | select( 96 | option("lol"), 97 | option("wtf") 98 | ), 99 | br, 100 | label("textarea"), 101 | textarea 102 | ) 103 | } 104 | val svgFrag = { 105 | import bundle.implicits._ 106 | import bundle.svgTags._ 107 | import bundle.svgAttrs._ 108 | svg(height := "800", width := "500")( 109 | polyline( 110 | points := "20,20 40,25 60,40 80,120 120,140 200,180", 111 | fill := "none", 112 | stroke := "black", 113 | strokeWidth := "3" 114 | ), 115 | line( 116 | x1 := 175, y1 := 100, 117 | x2 := 275, y2 := 0, 118 | stroke := "rgb(255,0,0)", 119 | strokeWidth := 10 120 | ), 121 | rect( 122 | x := 300, y := 10, 123 | rx := 20, ry := 20, 124 | width := 100, 125 | height := 100, 126 | fill := "rgb(0,0,255)", 127 | strokeWidth := 3, 128 | stroke := "rgb(0,0,0)", 129 | fillOpacity := "0.1", 130 | strokeOpacity := "0.5" 131 | ), 132 | circle( 133 | cx := 30, cy := 250, 134 | r := 10, 135 | stroke := "black", 136 | strokeWidth := 3, 137 | fill := "red" 138 | ), 139 | ellipse( 140 | cx := 150, cy := 250, 141 | rx := 100, ry := 50, 142 | fill := "yellow", 143 | stroke := "purple", 144 | strokeWidth := 4 145 | ), 146 | polygon( 147 | points := "300,110 350,290 260,310", 148 | fill := "line", 149 | stroke := "purple", 150 | strokeWidth := 10 151 | ), 152 | path( 153 | d := "M100 300 L25 500 L175 500 Z" 154 | ), 155 | text( 156 | x := 350, y := 250, 157 | fill := "red", 158 | transform := "rotate(30 20, 40)", 159 | "I love SVG!" 160 | ), 161 | text(x := 350, y := 350, fill := "green")( 162 | "Several lines", 163 | tspan(x := 350, y := 380, "First line."), 164 | tspan(x := 350, y := 410, "Second line.") 165 | ), 166 | defs( 167 | linearGradient( 168 | id := "grad1", 169 | x1 := "0%", 170 | y1 := "0%", 171 | x2 := "100%", 172 | y2 := "0%", 173 | stop( 174 | offset := "0%", 175 | stopColor := "rgb(255,255,0)" 176 | ), 177 | stop( 178 | offset := "100%", 179 | stopColor := "rgb(255,0,0)" 180 | ) 181 | ) 182 | ), 183 | ellipse( 184 | cx := 100, cy := 590, 185 | rx := 85, ry := 55, 186 | fill := "url(#grad1)" 187 | ) 188 | ) 189 | } 190 | } 191 | 192 | @JSExportTopLevel("ScalaJSExample") 193 | object ScalaJSExample { 194 | def main(t1: String, t2: String, t3: String, t4: String): Unit = { 195 | val textExample = new Example(scalatags.Text) 196 | val jsDomExample = new Example(scalatags.JsDom) 197 | dom.document.getElementById(t1).innerHTML = textExample.htmlFrag.render 198 | dom.document.getElementById(t2).innerHTML = textExample.svgFrag.render 199 | dom.document.getElementById(t3).appendChild(jsDomExample.htmlFrag.render) 200 | dom.document.getElementById(t4).appendChild(jsDomExample.svgFrag.render) 201 | () 202 | } 203 | } 204 | -------------------------------------------------------------------------------- /mill: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # This is a wrapper script, that automatically download mill from GitHub release pages 4 | # You can give the required mill version with MILL_VERSION env variable 5 | # If no version is given, it falls back to the value of DEFAULT_MILL_VERSION 6 | 7 | set -e 8 | 9 | if [ -z "${DEFAULT_MILL_VERSION}" ] ; then 10 | DEFAULT_MILL_VERSION=0.11.0 11 | fi 12 | 13 | if [ -z "$MILL_VERSION" ] ; then 14 | if [ -f ".mill-version" ] ; then 15 | MILL_VERSION="$(head -n 1 .mill-version 2> /dev/null)" 16 | elif [ -f ".config/mill-version" ] ; then 17 | MILL_VERSION="$(head -n 1 .config/mill-version 2> /dev/null)" 18 | elif [ -f "mill" ] && [ "$0" != "mill" ] ; then 19 | MILL_VERSION=$(grep -F "DEFAULT_MILL_VERSION=" "mill" | head -n 1 | cut -d= -f2) 20 | else 21 | MILL_VERSION=$DEFAULT_MILL_VERSION 22 | fi 23 | fi 24 | 25 | if [ "x${XDG_CACHE_HOME}" != "x" ] ; then 26 | MILL_DOWNLOAD_PATH="${XDG_CACHE_HOME}/mill/download" 27 | else 28 | MILL_DOWNLOAD_PATH="${HOME}/.cache/mill/download" 29 | fi 30 | MILL_EXEC_PATH="${MILL_DOWNLOAD_PATH}/${MILL_VERSION}" 31 | 32 | version_remainder="$MILL_VERSION" 33 | MILL_MAJOR_VERSION="${version_remainder%%.*}"; version_remainder="${version_remainder#*.}" 34 | MILL_MINOR_VERSION="${version_remainder%%.*}"; version_remainder="${version_remainder#*.}" 35 | 36 | if [ ! -s "$MILL_EXEC_PATH" ] ; then 37 | mkdir -p "$MILL_DOWNLOAD_PATH" 38 | if [ "$MILL_MAJOR_VERSION" -gt 0 ] || [ "$MILL_MINOR_VERSION" -ge 5 ] ; then 39 | ASSEMBLY="-assembly" 40 | fi 41 | DOWNLOAD_FILE=$MILL_EXEC_PATH-tmp-download 42 | MILL_VERSION_TAG=$(echo $MILL_VERSION | sed -E 's/([^-]+)(-M[0-9]+)?(-.*)?/\1\2/') 43 | MILL_DOWNLOAD_URL="https://repo1.maven.org/maven2/com/lihaoyi/mill-dist/$MILL_VERSION/mill-dist-$MILL_VERSION.jar" 44 | curl --fail -L -o "$DOWNLOAD_FILE" "$MILL_DOWNLOAD_URL" 45 | chmod +x "$DOWNLOAD_FILE" 46 | mv "$DOWNLOAD_FILE" "$MILL_EXEC_PATH" 47 | unset DOWNLOAD_FILE 48 | unset MILL_DOWNLOAD_URL 49 | fi 50 | 51 | if [ -z "$MILL_MAIN_CLI" ] ; then 52 | MILL_MAIN_CLI="${0}" 53 | fi 54 | 55 | MILL_FIRST_ARG="" 56 | if [ "$1" = "--bsp" ] || [ "$1" = "-i" ] || [ "$1" = "--interactive" ] || [ "$1" = "--no-server" ] || [ "$1" = "--repl" ] || [ "$1" = "--help" ] ; then 57 | # Need to preserve the first position of those listed options 58 | MILL_FIRST_ARG=$1 59 | shift 60 | fi 61 | 62 | unset MILL_DOWNLOAD_PATH 63 | unset MILL_VERSION 64 | 65 | exec $MILL_EXEC_PATH $MILL_FIRST_ARG -D "mill.main.cli=${MILL_MAIN_CLI}" "$@" 66 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "scalatags", 3 | "devDependencies": { 4 | "jsdom": "^20.0.3" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /project/Constants.scala: -------------------------------------------------------------------------------- 1 | package scalatags 2 | object Constants{ 3 | val version = "0.13.1" 4 | } 5 | -------------------------------------------------------------------------------- /project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=1.2.8 2 | -------------------------------------------------------------------------------- /project/build.sbt: -------------------------------------------------------------------------------- 1 | addSbtPlugin("com.lihaoyi" % "scalatex-sbt-plugin" % "0.3.11") 2 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Scalatags [![Join the chat at https://gitter.im/lihaoyi/scalatags](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/lihaoyi/scalatags?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Build Status](https://travis-ci.org/lihaoyi/scalatags.svg)](https://travis-ci.org/lihaoyi/scalatags) 2 | 3 | [Documentation](https://com-lihaoyi.github.io/scalatags) 4 | 5 | If you use Scalatags and like it, you will probably enjoy the following book by the Author: 6 | 7 | - [*Hands-on Scala Programming*](https://www.handsonscala.com/) 8 | 9 | *Hands-on Scala* has uses Scalatags extensively throughout the book, using 10 | the library to build static websites in *Chapter 9: Self-Contained Scala 11 | Scripts* and dynamic web servers in *Chapter 14: Simple Web and API Servers*. 12 | *Hands-on Scala* is a great way to level up your skills in Scala in general 13 | and Scalatags in particular. 14 | 15 | -------------------------------------------------------------------------------- /readme/resources/Autocomplete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/com-lihaoyi/scalatags/762ab37d0addc614bfd65bbeabeb5f123caf4395/readme/resources/Autocomplete.png -------------------------------------------------------------------------------- /readme/resources/ErrorHighlighting.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/com-lihaoyi/scalatags/762ab37d0addc614bfd65bbeabeb5f123caf4395/readme/resources/ErrorHighlighting.png -------------------------------------------------------------------------------- /readme/resources/InlineDocs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/com-lihaoyi/scalatags/762ab37d0addc614bfd65bbeabeb5f123caf4395/readme/resources/InlineDocs.png -------------------------------------------------------------------------------- /readme/resources/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/com-lihaoyi/scalatags/762ab37d0addc614bfd65bbeabeb5f123caf4395/readme/resources/favicon.png -------------------------------------------------------------------------------- /scalatags/src-2/scalatags/stylesheet/SourceClasses.scala: -------------------------------------------------------------------------------- 1 | package scalatags.stylesheet 2 | 3 | import scala.language.experimental.macros 4 | import scala.reflect.macros.Context 5 | 6 | class SourceClasses[T](val value: T => Seq[Cls]) 7 | object SourceClasses{ 8 | implicit def apply[T]: SourceClasses[T] = macro manglerImpl[T] 9 | def manglerImpl[T: c.WeakTypeTag](c: Context) = { 10 | import c.universe._ 11 | 12 | val weakType = weakTypeOf[T] 13 | 14 | val stylesheetName = newTermName("stylesheet") 15 | val names = for { 16 | member <- weakType.members.toSeq.reverse 17 | // Not sure if there's a better way to capture by-name types 18 | if member.typeSignature.toString == "=> scalatags.stylesheet.Cls" || 19 | member.typeSignature.toString == "scalatags.stylesheet.Cls" 20 | if member.isPublic 21 | } yield q"$stylesheetName.${member.name.toTermName}" 22 | val res = q""" 23 | new scalatags.stylesheet.SourceClasses[$weakType]( 24 | ($stylesheetName: $weakType) => Seq[scalatags.stylesheet.Cls](..$names) 25 | ) 26 | """ 27 | 28 | c.Expr[SourceClasses[T]](res) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /scalatags/src-3/scalatags/stylesheet/SourceClasses.scala: -------------------------------------------------------------------------------- 1 | package scalatags.stylesheet 2 | 3 | import scala.quoted.* 4 | 5 | class SourceClasses[T](val value: T => Seq[Cls]) 6 | object SourceClasses: 7 | inline implicit def apply[T]: SourceClasses[T] = ${ manglerImpl[T] } 8 | 9 | def manglerImpl[T: Type](using Quotes): Expr[SourceClasses[T]] = 10 | '{ new SourceClasses[T]((t: T) => ${ Expr.ofSeq(terms('t)) }) } 11 | 12 | private def terms[T: Type](t: Expr[T])(using ctx: Quotes): Seq[Expr[Cls]] = 13 | import ctx.reflect._ 14 | val valdefs = TypeRepr.of[T].typeSymbol.declaredFields.map(_.tree.asInstanceOf[ValDef]) 15 | val defdefs = TypeRepr.of[T].typeSymbol.declaredMethods.map(_.tree.asInstanceOf[DefDef]) 16 | 17 | val valclsesOnly: Seq[ValDef] = valdefs.filter(_.tpt.tpe =:= TypeRepr.of[Cls]) 18 | val defclsesOnly: Seq[DefDef] = defdefs.filter(_.returnTpt.tpe =:= TypeRepr.of[Cls]) 19 | 20 | val defClsExpressions = defclsesOnly.map(defdef => t.asTerm.select(defdef.symbol).asExprOf[Cls]) 21 | val valClsExpressions = valclsesOnly.map(valdef => t.asTerm.select(valdef.symbol).asExprOf[Cls]) 22 | 23 | defClsExpressions ++ valClsExpressions 24 | -------------------------------------------------------------------------------- /scalatags/src-js/scalatags/JsDom.scala: -------------------------------------------------------------------------------- 1 | package scalatags 2 | import java.util.Objects 3 | 4 | import org.scalajs 5 | import org.scalajs.dom 6 | 7 | import scala.language.implicitConversions 8 | import scala.scalajs.js 9 | import org.scalajs.dom.{Element, html, raw, svg} 10 | 11 | import scala.annotation.unchecked.uncheckedVariance 12 | import scalatags.generic.{Aliases, Namespace, StylePair} 13 | import scalatags.stylesheet.{StyleSheetFrag, StyleTree} 14 | 15 | 16 | /** 17 | * A Scalatags module that generates `dom.Element`s when the tags are rendered. 18 | * This provides some additional flexibility over the [[Text]] backend, as you 19 | * can bind structured objects to the attributes of your `dom.Element` without 20 | * serializing them first into strings. 21 | */ 22 | object JsDom 23 | extends generic.Bundle[dom.Element, dom.Element, dom.Node] 24 | with Aliases[dom.Element, dom.Element, dom.Node]{ 25 | 26 | object attrs extends JsDom.Cap with Attrs 27 | object tags extends JsDom.Cap with jsdom.Tags 28 | object tags2 extends JsDom.Cap with jsdom.Tags2 29 | object styles extends JsDom.Cap with Styles 30 | object styles2 extends JsDom.Cap with Styles2 31 | object svgTags extends JsDom.Cap with jsdom.SvgTags 32 | object svgAttrs extends JsDom.Cap with SvgAttrs 33 | 34 | object implicits extends Aggregate with DataConverters 35 | 36 | object all 37 | extends Cap 38 | with Attrs 39 | with Styles 40 | with jsdom.Tags 41 | with DataConverters 42 | with Aggregate 43 | with LowPriorityImplicits 44 | 45 | object short 46 | extends Cap 47 | with jsdom.Tags 48 | with DataConverters 49 | with Aggregate 50 | with AbstractShort 51 | with LowPriorityImplicits{ 52 | 53 | object * extends Cap with Attrs with Styles 54 | } 55 | 56 | 57 | trait Aggregate extends generic.Aggregate[dom.Element, dom.Element, dom.Node]{ 58 | implicit class ApplyTags(e: dom.Element){ 59 | def applyTags(mods: Modifier*) = mods.foreach(_.applyTo(e)) 60 | } 61 | implicit def ClsModifier(s: stylesheet.Cls): Modifier = new Modifier{ 62 | def applyTo(t: dom.Element) = t.classList.add(s.name) 63 | } 64 | implicit class StyleFrag(s: generic.StylePair[dom.Element, _]) extends StyleSheetFrag{ 65 | def applyTo(c: StyleTree) = { 66 | val b = dom.document.createElement("div") 67 | s.applyTo(b) 68 | val Array(style, value) = Option(b.getAttribute("style")).map(_.split(":", 2)) 69 | .getOrElse(throw new IllegalArgumentException(s"Cannot apply style $s. Does it contain a syntax error?")) 70 | c.copy(styles = c.styles.updated(style, value)) 71 | } 72 | } 73 | 74 | 75 | def genericAttr[T] = new JsDom.GenericAttr[T] 76 | def genericStyle[T] = new JsDom.GenericStyle[T] 77 | def genericPixelStyle[T](implicit ev: StyleValue[T]): PixelStyleValue[T] = new JsDom.GenericPixelStyle[T](ev) 78 | def genericPixelStylePx[T](implicit ev: StyleValue[String]): PixelStyleValue[T] = new JsDom.GenericPixelStylePx[T](ev) 79 | 80 | implicit def stringFrag(v: String): StringFrag = new JsDom.StringFrag(v) 81 | 82 | 83 | val RawFrag = JsDom.RawFrag 84 | val StringFrag = JsDom.StringFrag 85 | type StringFrag = JsDom.StringFrag 86 | type RawFrag = JsDom.RawFrag 87 | def raw(s: String) = RawFrag(s) 88 | 89 | type HtmlTag = JsDom.TypedTag[html.Element] 90 | val HtmlTag = JsDom.TypedTag 91 | type SvgTag = JsDom.TypedTag[svg.Element] 92 | val SvgTag = JsDom.TypedTag 93 | 94 | val Tag = JsDom.TypedTag 95 | 96 | 97 | } 98 | 99 | trait Cap extends Util with jsdom.TagFactory{ self => 100 | type ConcreteHtmlTag[T <: dom.Element] = TypedTag[T] 101 | 102 | protected[this] implicit def stringAttrX = new GenericAttr[String] 103 | protected[this] implicit def stringStyleX = new GenericStyle[String] 104 | protected[this] implicit def stringPixelStyleX = new GenericPixelStyle[String](stringStyleX) 105 | implicit def UnitFrag(u: Unit): JsDom.StringFrag = new JsDom.StringFrag("") 106 | def makeAbstractTypedTag[T <: dom.Element](tag: String, void: Boolean, namespaceConfig: Namespace): TypedTag[T] = { 107 | TypedTag(tag, Nil, void, namespaceConfig) 108 | } 109 | 110 | implicit class SeqFrag[A](xs: Seq[A])(implicit ev: A => Frag) extends Frag{ 111 | Objects.requireNonNull(xs) 112 | def applyTo(t: dom.Element): Unit = xs.foreach(elem => ev(elem).applyTo(t)) 113 | def render: dom.Node = { 114 | val frag = org.scalajs.dom.document.createDocumentFragment() 115 | xs.map(elem => ev(elem).render).foreach(frag.appendChild) 116 | frag 117 | } 118 | } 119 | implicit class GeneratorFrag[A](xs: geny.Generator[A])(implicit ev: A => Frag) extends Frag{ 120 | Objects.requireNonNull(xs) 121 | def applyTo(t: dom.Element): Unit = xs.foreach(elem => ev(elem).applyTo(t)) 122 | def render: dom.Node = { 123 | val frag = org.scalajs.dom.document.createDocumentFragment() 124 | xs.map(elem => ev(elem).render).foreach(frag.appendChild) 125 | frag 126 | } 127 | } 128 | } 129 | 130 | case class StringFrag(v: String) extends jsdom.Frag { 131 | Objects.requireNonNull(v) 132 | def render: dom.Text = dom.document.createTextNode(v) 133 | } 134 | 135 | case class RawFrag(v: String) extends jsdom.Frag{ 136 | Objects.requireNonNull(v) 137 | def render: dom.Node = { 138 | // https://davidwalsh.name/convert-html-stings-dom-nodes 139 | dom.document.createRange().createContextualFragment(v) 140 | } 141 | } 142 | 143 | class GenericAttr[T] extends AttrValue[T]{ 144 | def apply(t: dom.Element, a: Attr, v: T): Unit = { 145 | a.namespace match { 146 | case None => 147 | if (!a.raw) { 148 | if (a.name == "class") { 149 | v.toString.split(" ").foreach { cls => 150 | if (cls.trim.nonEmpty) t.classList.add(cls.trim) 151 | } 152 | } else t.setAttribute(a.name, v.toString) 153 | } else { 154 | 155 | // Ugly workaround for https://www.w3.org/Bugs/Public/show_bug.cgi?id=27228 156 | val tmpElm = dom.document.createElement("p") 157 | tmpElm.innerHTML = s"""

""" 158 | val newAttr = tmpElm.children(0).attributes(0).cloneNode(true) 159 | t.setAttributeNode(newAttr.asInstanceOf[raw.Attr]) 160 | 161 | } 162 | case Some(namespace) => 163 | t.setAttributeNS(namespace.uri, a.name, v.toString) 164 | } 165 | } 166 | } 167 | 168 | class GenericStyle[T] extends StyleValue[T]{ 169 | def apply(t: dom.Element, s: Style, v: T): Unit = { 170 | t.asInstanceOf[html.Html] 171 | .style 172 | .setProperty(s.cssName, v.toString) 173 | } 174 | } 175 | class GenericPixelStyle[T](ev: StyleValue[T]) extends PixelStyleValue[T]{ 176 | def apply(s: Style, v: T) = StylePair(s, v, ev) 177 | } 178 | class GenericPixelStylePx[T](ev: StyleValue[String]) extends PixelStyleValue[T]{ 179 | def apply(s: Style, v: T) = StylePair(s, s"${v}px", ev) 180 | } 181 | case class TypedTag[+Output <: dom.Element](tag: String = "", 182 | modifiers: List[Seq[Modifier]], 183 | void: Boolean = false, 184 | namespace: Namespace) 185 | extends generic.TypedTag[dom.Element, Output, dom.Node] 186 | with jsdom.Frag{ 187 | // unchecked because Scala 2.10.4 seems to not like this, even though 188 | // 2.11.1 works just fine. I trust that 2.11.1 is more correct than 2.10.4 189 | // and so just force this. 190 | protected[this] type Self = TypedTag[Output @uncheckedVariance] 191 | 192 | def render: Output = { 193 | val elem = dom.document.createElementNS(namespace.uri, tag) 194 | build(elem) 195 | elem.asInstanceOf[Output] 196 | } 197 | /** 198 | * Trivial override, not strictly necessary, but it makes IntelliJ happy... 199 | */ 200 | def apply(xs: Modifier*): TypedTag[Output] = { 201 | this.copy(tag = tag, void = void, modifiers = xs :: modifiers) 202 | } 203 | override def toString = render.outerHTML 204 | } 205 | 206 | } 207 | 208 | trait LowPriorityImplicits{ 209 | implicit object bindJsAny extends generic.AttrValue[dom.Element, js.Any]{ 210 | def apply(t: dom.Element, a: generic.Attr, v: js.Any): Unit = { 211 | t.asInstanceOf[js.Dynamic].updateDynamic(a.name)(v) 212 | } 213 | } 214 | implicit def bindJsAnyLike[T](implicit ev: T => js.Any): generic.AttrValue[dom.Element, T] = new generic.AttrValue[dom.Element, T]{ 215 | def apply(t: dom.Element, a: generic.Attr, v: T): Unit = { 216 | t.asInstanceOf[js.Dynamic].updateDynamic(a.name)(ev(v)) 217 | } 218 | } 219 | implicit class bindNode[T <: dom.Node](e: T) extends generic.Frag[dom.Element, T] { 220 | def applyTo(t: Element) = t.appendChild(e) 221 | def render = e 222 | } 223 | } 224 | -------------------------------------------------------------------------------- /scalatags/src-js/scalatags/jsdom/Frag.scala: -------------------------------------------------------------------------------- 1 | package scalatags 2 | package jsdom 3 | import org.scalajs.dom 4 | 5 | trait Frag extends generic.Frag[dom.Element, dom.Node]{ 6 | def render: dom.Node 7 | def applyTo(b: dom.Element) = b.appendChild(this.render) 8 | } 9 | -------------------------------------------------------------------------------- /scalatags/src-js/scalatags/jsdom/SvgTags.scala: -------------------------------------------------------------------------------- 1 | package scalatags 2 | package jsdom 3 | import org.scalajs.dom 4 | import scalatags.generic.{Namespace, Util} 5 | trait SvgTags extends generic.SvgTags[dom.Element, dom.Element, dom.Node] with TagFactory{ 6 | implicit lazy val svgNamespaceConfig: Namespace = Namespace.svgNamespaceConfig 7 | lazy val altGlyph = typedTag[dom.svg.Element]("altGlyph") 8 | lazy val altGlyphDef = typedTag[dom.svg.Element]("altGlyphDef") 9 | lazy val altGlyphItem = typedTag[dom.svg.Element]("altGlyphItem") 10 | lazy val animate = typedTag[dom.svg.Element]("animate") 11 | lazy val animateMotion = typedTag[dom.svg.Element]("animateMotion") 12 | lazy val animateTransform = typedTag[dom.svg.Element]("animateTransform") 13 | lazy val circle = typedTag[dom.svg.Circle]("circle") 14 | lazy val clipPath = typedTag[dom.svg.ClipPath]("clipPath") 15 | lazy val `color-profile` = typedTag[dom.svg.Element]("color-profile") 16 | lazy val cursor = typedTag[dom.svg.Element]("cursor") 17 | lazy val defs = typedTag[dom.svg.Defs]("defs") 18 | lazy val desc = typedTag[dom.svg.Desc]("desc") 19 | lazy val ellipse = typedTag[dom.svg.Ellipse]("ellipse") 20 | lazy val feBlend = typedTag[dom.svg.FEBlend]("feBlend") 21 | lazy val feColorMatrix = typedTag[dom.svg.FEColorMatrix]("feColorMatrix") 22 | lazy val feComponentTransfer = typedTag[dom.svg.ComponentTransferFunction]("feComponentTransfer") 23 | lazy val feComposite = typedTag[dom.svg.FEComposite]("feComposite") 24 | lazy val feConvolveMatrix = typedTag[dom.svg.FEConvolveMatrix]("feConvolveMatrix") 25 | lazy val feDiffuseLighting = typedTag[dom.svg.FEDiffuseLighting]("feDiffuseLighting") 26 | lazy val feDisplacementMap = typedTag[dom.svg.FEDisplacementMap]("feDisplacementMap") 27 | lazy val feDistantLighting = typedTag[dom.svg.FEDistantLight]("feDistantLighting") 28 | lazy val feFlood = typedTag[dom.svg.FEFlood]("feFlood") 29 | lazy val feFuncA = typedTag[dom.svg.FEFuncA]("feFuncA") 30 | lazy val feFuncB = typedTag[dom.svg.FEFuncB]("feFuncB") 31 | lazy val feFuncG = typedTag[dom.svg.FEFuncG]("feFuncG") 32 | lazy val feFuncR = typedTag[dom.svg.FEFuncR]("feFuncR") 33 | lazy val feGaussianBlur = typedTag[dom.svg.FEGaussianBlur]("feGaussianBlur") 34 | lazy val feImage = typedTag[dom.svg.FEImage]("feImage") 35 | lazy val feMerge = typedTag[dom.svg.FEMerge]("feMerge") 36 | lazy val feMergeNode = typedTag[dom.svg.FEMergeNode]("feMergeNode") 37 | lazy val feMorphology = typedTag[dom.svg.FEMorphology]("feMorphology") 38 | lazy val feOffset = typedTag[dom.svg.FEOffset]("feOffset") 39 | lazy val fePointLight = typedTag[dom.svg.FEPointLight]("fePointLight") 40 | lazy val feSpecularLighting = typedTag[dom.svg.FESpecularLighting]("feSpecularLighting") 41 | lazy val feSpotlight = typedTag[dom.svg.FESpotLight]("feSpotlight") 42 | lazy val feTile = typedTag[dom.svg.FETile]("feTile") 43 | lazy val feTurbulance = typedTag[dom.svg.FETurbulence]("feTurbulance") 44 | lazy val filter = typedTag[dom.svg.Filter]("filter") 45 | lazy val font = typedTag[dom.svg.Element]("font") 46 | lazy val `font-face` = typedTag[dom.svg.Element]("font-face") 47 | lazy val `font-face-format` = typedTag[dom.svg.Element]("font-face-format") 48 | lazy val `font-face-name` = typedTag[dom.svg.Element]("font-face-name") 49 | lazy val `font-face-src` = typedTag[dom.svg.Element]("font-face-src") 50 | lazy val `font-face-uri` = typedTag[dom.svg.Element]("font-face-uri") 51 | lazy val foreignObject = typedTag[dom.svg.Element]("foreignObject") 52 | lazy val g = typedTag[dom.svg.G]("g") 53 | lazy val glyph = typedTag[dom.svg.Element]("glyph") 54 | lazy val glyphRef = typedTag[dom.svg.Element]("glyphRef") 55 | lazy val hkern = typedTag[dom.svg.Element]("hkern") 56 | lazy val image = typedTag[dom.svg.Image]("image") 57 | lazy val line = typedTag[dom.svg.Line]("line") 58 | lazy val linearGradient = typedTag[dom.svg.LinearGradient]("linearGradient") 59 | lazy val marker = typedTag[dom.svg.Marker]("marker") 60 | lazy val mask = typedTag[dom.svg.Mask]("mask") 61 | lazy val metadata = typedTag[dom.svg.Metadata]("metadata") 62 | lazy val `missing-glyph` = typedTag[dom.svg.Element]("missing-glyph") 63 | lazy val mpath = typedTag[dom.svg.Element]("mpath") 64 | lazy val path = typedTag[dom.svg.Path]("path") 65 | lazy val pattern = typedTag[dom.svg.Pattern]("pattern") 66 | lazy val polygon = typedTag[dom.svg.Polygon]("polygon") 67 | lazy val polyline = typedTag[dom.svg.Polyline]("polyline") 68 | lazy val radialGradient = typedTag[dom.svg.RadialGradient]("radialGradient") 69 | lazy val rect = typedTag[dom.svg.RectElement]("rect") 70 | lazy val set = typedTag[dom.svg.Element]("set") 71 | lazy val stop = typedTag[dom.svg.Stop]("stop") 72 | lazy val svg = typedTag[dom.svg.SVG]("svg") 73 | lazy val switch = typedTag[dom.svg.Switch]("switch") 74 | lazy val symbol = typedTag[dom.svg.Symbol]("symbol") 75 | lazy val text = typedTag[dom.svg.Text]("text") 76 | lazy val textPath = typedTag[dom.svg.TextPath]("textPath") 77 | lazy val tref = typedTag[dom.svg.Element]("tref") 78 | lazy val tspan = typedTag[dom.svg.TSpan]("tspan") 79 | lazy val use = typedTag[dom.svg.Use]("use") 80 | lazy val view = typedTag[dom.svg.View]("view") 81 | lazy val vkern = typedTag[dom.svg.Element]("vkern") 82 | } 83 | -------------------------------------------------------------------------------- /scalatags/src-js/scalatags/jsdom/TagFactory.scala: -------------------------------------------------------------------------------- 1 | package scalatags.jsdom 2 | 3 | import org.scalajs.dom 4 | 5 | import scalatags.Escaping 6 | import scalatags.generic.Namespace 7 | 8 | /** 9 | * Created by haoyi on 7/9/16. 10 | */ 11 | trait TagFactory extends scalatags.generic.Util[dom.Element, dom.Element, dom.Node]{ 12 | def tag(s: String, void: Boolean = false): ConcreteHtmlTag[dom.Element] = { 13 | typedTag[dom.Element](s, void) 14 | } 15 | def typedTag[T <: dom.Element](s: String, void: Boolean = false) 16 | (implicit ns: Namespace): ConcreteHtmlTag[T] = { 17 | if (!Escaping.validTag(s)) 18 | throw new IllegalArgumentException( 19 | s"Illegal tag name: $s is not a valid XML tag name" 20 | ) 21 | makeAbstractTypedTag[T](s, void, ns) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /scalatags/src-js/scalatags/jsdom/Tags.scala: -------------------------------------------------------------------------------- 1 | 2 | package scalatags 3 | package jsdom 4 | import org.scalajs.dom 5 | import scalatags.generic.Util 6 | trait Tags extends generic.Tags[dom.Element, dom.Element, dom.Node] with TagFactory{ 7 | // Root Element 8 | lazy val html: ConcreteHtmlTag[dom.html.Html] = typedTag[dom.html.Html]("html") 9 | // Document Metadata 10 | lazy val head: ConcreteHtmlTag[dom.html.Head] = typedTag[dom.html.Head]("head") 11 | lazy val base: ConcreteHtmlTag[dom.html.Base] = typedTag[dom.html.Base]("base", void = true) 12 | lazy val link: ConcreteHtmlTag[dom.html.Link] = typedTag[dom.html.Link]("link", void = true) 13 | lazy val meta: ConcreteHtmlTag[dom.html.Meta] = typedTag[dom.html.Meta]("meta", void = true) 14 | // Scripting 15 | lazy val script: ConcreteHtmlTag[dom.html.Script] = typedTag[dom.html.Script]("script") 16 | // Sections 17 | lazy val body: ConcreteHtmlTag[dom.html.Body] = typedTag[dom.html.Body]("body") 18 | lazy val h1 = typedTag[dom.html.Heading]("h1") 19 | lazy val h2 = typedTag[dom.html.Heading]("h2") 20 | lazy val h3 = typedTag[dom.html.Heading]("h3") 21 | lazy val h4 = typedTag[dom.html.Heading]("h4") 22 | lazy val h5 = typedTag[dom.html.Heading]("h5") 23 | lazy val h6 = typedTag[dom.html.Heading]("h6") 24 | lazy val header: ConcreteHtmlTag[dom.html.Element] = typedTag[dom.html.Element]("header") 25 | lazy val footer: ConcreteHtmlTag[dom.html.Element] = typedTag[dom.html.Element]("footer") 26 | // Grouping content 27 | lazy val p: ConcreteHtmlTag[dom.html.Paragraph] = typedTag[dom.html.Paragraph]("p") 28 | lazy val hr: ConcreteHtmlTag[dom.html.HR] = typedTag[dom.html.HR]("hr", void = true) 29 | lazy val pre: ConcreteHtmlTag[dom.html.Pre] = typedTag[dom.html.Pre]("pre") 30 | lazy val blockquote: ConcreteHtmlTag[dom.html.Quote] = typedTag[dom.html.Quote]("blockquote") 31 | lazy val ol: ConcreteHtmlTag[dom.html.OList] = typedTag[dom.html.OList]("ol") 32 | lazy val ul: ConcreteHtmlTag[dom.html.UList] = typedTag[dom.html.UList]("ul") 33 | lazy val li: ConcreteHtmlTag[dom.html.LI] = typedTag[dom.html.LI]("li") 34 | lazy val dl: ConcreteHtmlTag[dom.html.DList] = typedTag[dom.html.DList]("dl") 35 | lazy val dt: ConcreteHtmlTag[dom.html.Element] = typedTag[dom.html.Element]("dt") 36 | lazy val dd: ConcreteHtmlTag[dom.html.Element] = typedTag[dom.html.Element]("dd") 37 | lazy val figure: ConcreteHtmlTag[dom.html.Element] = typedTag[dom.html.Element]("figure") 38 | lazy val figcaption: ConcreteHtmlTag[dom.html.Element] = typedTag[dom.html.Element]("figcaption") 39 | lazy val div: ConcreteHtmlTag[dom.html.Div] = typedTag[dom.html.Div]("div") 40 | // Text-level semantics 41 | lazy val a: ConcreteHtmlTag[dom.html.Anchor] = typedTag[dom.html.Anchor]("a") 42 | lazy val em: ConcreteHtmlTag[dom.html.Element] = typedTag[dom.html.Element]("em") 43 | lazy val strong: ConcreteHtmlTag[dom.html.Element] = typedTag[dom.html.Element]("strong") 44 | lazy val small: ConcreteHtmlTag[dom.html.Element] = typedTag[dom.html.Element]("small") 45 | lazy val s: ConcreteHtmlTag[dom.html.Element] = typedTag[dom.html.Element]("s") 46 | lazy val cite: ConcreteHtmlTag[dom.html.Element] = typedTag[dom.html.Element]("cite") 47 | lazy val code: ConcreteHtmlTag[dom.html.Element] = typedTag[dom.html.Element]("code") 48 | lazy val sub: ConcreteHtmlTag[dom.html.Element] = typedTag[dom.html.Element]("sub") 49 | lazy val sup: ConcreteHtmlTag[dom.html.Element] = typedTag[dom.html.Element]("sup") 50 | lazy val i: ConcreteHtmlTag[dom.html.Element] = typedTag[dom.html.Element]("i") 51 | lazy val b: ConcreteHtmlTag[dom.html.Element] = typedTag[dom.html.Element]("b") 52 | lazy val u: ConcreteHtmlTag[dom.html.Element] = typedTag[dom.html.Element]("u") 53 | lazy val span: ConcreteHtmlTag[dom.html.Span] = typedTag[dom.html.Span]("span") 54 | lazy val br: ConcreteHtmlTag[dom.html.BR] = typedTag[dom.html.BR]("br", void = true) 55 | lazy val wbr: ConcreteHtmlTag[dom.html.Element] = typedTag[dom.html.Element]("wbr", void = true) 56 | // Edits 57 | lazy val ins: ConcreteHtmlTag[dom.html.Mod] = typedTag[dom.html.Mod]("ins") 58 | lazy val del: ConcreteHtmlTag[dom.html.Mod] = typedTag[dom.html.Mod]("del") 59 | // Embedded content 60 | lazy val img: ConcreteHtmlTag[dom.html.Image] = typedTag[dom.html.Image]("img", void = true) 61 | lazy val iframe: ConcreteHtmlTag[dom.html.IFrame] = typedTag[dom.html.IFrame]("iframe") 62 | lazy val embed: ConcreteHtmlTag[dom.html.Embed] = typedTag[dom.html.Embed]("embed", void = true) 63 | lazy val `object` = typedTag[dom.html.Object]("object") 64 | lazy val param: ConcreteHtmlTag[dom.html.Param] = typedTag[dom.html.Param]("param", void = true) 65 | lazy val video: ConcreteHtmlTag[dom.html.Video] = typedTag[dom.html.Video]("video") 66 | lazy val audio: ConcreteHtmlTag[dom.html.Audio] = typedTag[dom.html.Audio]("audio") 67 | lazy val source: ConcreteHtmlTag[dom.html.Source] = typedTag[dom.html.Source]("source", void = true) 68 | lazy val track: ConcreteHtmlTag[dom.html.Track] = typedTag[dom.html.Track]("track", void = true) 69 | lazy val canvas: ConcreteHtmlTag[dom.html.Canvas] = typedTag[dom.html.Canvas]("canvas") 70 | lazy val map: ConcreteHtmlTag[dom.html.Map] = typedTag[dom.html.Map]("map") 71 | lazy val area: ConcreteHtmlTag[dom.html.Area] = typedTag[dom.html.Area]("area", void = true) 72 | // Tabular data 73 | lazy val table: ConcreteHtmlTag[dom.html.Table] = typedTag[dom.html.Table]("table") 74 | lazy val caption: ConcreteHtmlTag[dom.html.TableCaption] = typedTag[dom.html.TableCaption]("caption") 75 | lazy val colgroup: ConcreteHtmlTag[dom.html.TableCol] = typedTag[dom.html.TableCol]("colgroup") 76 | lazy val col: ConcreteHtmlTag[dom.html.TableCol] = typedTag[dom.html.TableCol]("col", void = true) 77 | lazy val tbody: ConcreteHtmlTag[dom.html.TableSection] = typedTag[dom.html.TableSection]("tbody") 78 | lazy val thead: ConcreteHtmlTag[dom.html.TableSection] = typedTag[dom.html.TableSection]("thead") 79 | lazy val tfoot: ConcreteHtmlTag[dom.html.TableSection] = typedTag[dom.html.TableSection]("tfoot") 80 | lazy val tr: ConcreteHtmlTag[dom.html.TableRow] = typedTag[dom.html.TableRow]("tr") 81 | lazy val td: ConcreteHtmlTag[dom.html.TableCell] = typedTag[dom.html.TableCell]("td") 82 | lazy val th: ConcreteHtmlTag[dom.html.TableCell] = typedTag[dom.html.TableCell]("th") 83 | // Forms 84 | lazy val form: ConcreteHtmlTag[dom.html.Form] = typedTag[dom.html.Form]("form") 85 | lazy val fieldset: ConcreteHtmlTag[dom.html.FieldSet] = typedTag[dom.html.FieldSet]("fieldset") 86 | lazy val legend: ConcreteHtmlTag[dom.html.Legend] = typedTag[dom.html.Legend]("legend") 87 | lazy val label: ConcreteHtmlTag[dom.html.Label] = typedTag[dom.html.Label]("label") 88 | lazy val input: ConcreteHtmlTag[dom.html.Input] = typedTag[dom.html.Input]("input", void = true) 89 | lazy val button: ConcreteHtmlTag[dom.html.Button] = typedTag[dom.html.Button]("button") 90 | lazy val select: ConcreteHtmlTag[dom.html.Select] = typedTag[dom.html.Select]("select") 91 | lazy val datalist: ConcreteHtmlTag[dom.html.DataList] = typedTag[dom.html.DataList]("datalist") 92 | lazy val optgroup: ConcreteHtmlTag[dom.html.OptGroup] = typedTag[dom.html.OptGroup]("optgroup") 93 | lazy val option: ConcreteHtmlTag[dom.html.Option] = typedTag[dom.html.Option]("option") 94 | lazy val textarea: ConcreteHtmlTag[dom.html.TextArea] = typedTag[dom.html.TextArea]("textarea") 95 | } 96 | -------------------------------------------------------------------------------- /scalatags/src-js/scalatags/jsdom/Tags2.scala: -------------------------------------------------------------------------------- 1 | package scalatags 2 | package jsdom 3 | import org.scalajs.dom 4 | import org.scalajs.dom.html 5 | import scalatags.generic.Util 6 | trait Tags2 extends generic.Tags2[dom.Element, dom.Element, dom.Node] with TagFactory{ 7 | // Document Metadata 8 | lazy val title = typedTag[html.Title]("title") 9 | lazy val style = typedTag[html.Style]("style") 10 | // Scripting 11 | lazy val noscript = typedTag[html.Element]("noscript") 12 | // Sections 13 | lazy val section = typedTag[html.Element]("section") 14 | lazy val nav = typedTag[html.Element]("nav") 15 | lazy val article = typedTag[html.Element]("article") 16 | lazy val aside = typedTag[html.Element]("aside") 17 | lazy val address = typedTag[html.Element]("address") 18 | lazy val main = typedTag[html.Element]("main") 19 | // Text level semantics 20 | lazy val q = typedTag[html.Quote]("q") 21 | lazy val dfn = typedTag[html.Element]("dfn") 22 | lazy val abbr = typedTag[html.Element]("abbr") 23 | lazy val data = typedTag[html.Element]("data") 24 | lazy val time = typedTag[html.Element]("time") 25 | lazy val `var` = typedTag[html.Element]("var") 26 | lazy val samp = typedTag[html.Element]("samp") 27 | lazy val kbd = typedTag[html.Element]("kbd") 28 | lazy val math = typedTag[html.Element]("math") 29 | lazy val mark = typedTag[html.Element]("mark") 30 | lazy val ruby = typedTag[html.Element]("ruby") 31 | lazy val rt = typedTag[html.Element]("rt") 32 | lazy val rp = typedTag[html.Element]("rp") 33 | lazy val bdi = typedTag[html.Element]("bdi") 34 | lazy val bdo = typedTag[html.Element]("bdo") 35 | // Forms 36 | lazy val keygen = typedTag[html.Element]("keygen", void = true) 37 | lazy val output = typedTag[html.Element]("output") 38 | lazy val progress = typedTag[html.Progress]("progress") 39 | lazy val meter = typedTag[html.Element]("meter") 40 | // Interactive elements 41 | lazy val details = typedTag[html.Element]("details") 42 | lazy val summary = typedTag[html.Element]("summary") 43 | lazy val command = typedTag[html.Element]("command", void = true) 44 | lazy val menu = typedTag[html.Menu]("menu") 45 | } 46 | -------------------------------------------------------------------------------- /scalatags/src/scalatags/DataTypes.scala: -------------------------------------------------------------------------------- 1 | package scalatags 2 | 3 | import scala.language.implicitConversions 4 | 5 | /** 6 | * Module containing convenient ways of constructing CSS data types 7 | */ 8 | object DataConverters extends DataConverters 9 | 10 | 11 | /** 12 | * Trait containing the contents of the [[DataConverters]] module, so it can be 13 | * mixed in to other objects as needed. 14 | */ 15 | trait DataConverters{ 16 | implicit def Int2CssNumber(x: Int): CssNumber[Int] = new CssNumber(x) 17 | implicit def Double2CssNumber(x: Double): CssNumber[Double] = new CssNumber(x) 18 | implicit def Float2CssNumber(x: Float): CssNumber[Float] = new CssNumber(x) 19 | implicit def Long2CssNumber(x: Long): CssNumber[Long] = new CssNumber(x) 20 | implicit def Short2CssNumber(x: Short): CssNumber[Short] = new CssNumber(x) 21 | implicit def Byte2CssNumber(x: Byte): CssNumber[Byte] = new CssNumber(x) 22 | /** 23 | * Extends numbers to provide a bunch of useful methods, allowing you to write 24 | * CSS-lengths in a nice syntax without resorting to strings. 25 | */ 26 | class CssNumber[T: Numeric](x: T){ 27 | 28 | /** 29 | * Relative to the viewing device. For screen display, typically one device 30 | * pixel (dot) of the display. 31 | * 32 | * For printers and very high resolution screens one CSS pixel implies 33 | * multiple device pixels, so that the number of pixel per inch stays around 34 | * 96. 35 | * 36 | * MDN 37 | */ 38 | def px = s"${x}px" 39 | 40 | /** 41 | * One point which is 1/72 of an inch. 42 | * 43 | * MDN 44 | */ 45 | def pt = s"${x}pt" 46 | 47 | /** 48 | * One millimeter. 49 | * 50 | * MDN 51 | */ 52 | def mm = s"${x}mm" 53 | 54 | /** 55 | * One centimeter 10 millimeters. 56 | * 57 | * MDN 58 | */ 59 | def cm = s"${x}cm" 60 | 61 | /** 62 | * One inch 2.54 centimeters. 63 | * 64 | * MDN 65 | */ 66 | def in = s"${x}in" 67 | 68 | /** 69 | * One pica which is 12 points. 70 | * 71 | * MDN 72 | */ 73 | def pc = s"${x}pc" 74 | /** 75 | * This unit represents the calculated font-size of the element. If used on 76 | * the font-size property itself, it represents the inherited font-size 77 | * of the element. 78 | * 79 | * MDN 80 | */ 81 | def em = s"${x}em" 82 | 83 | /** 84 | * This unit represents the width, or more precisely the advance measure, of 85 | * the glyph '0' zero, the Unicode character U+0030 in the element's font. 86 | * 87 | * MDN 88 | */ 89 | def ch = s"${x}ch" 90 | 91 | /** 92 | * This unit represents the x-height of the element's font. On fonts with the 93 | * 'x' letter, this is generally the height of lowercase letters in the font; 94 | * 1ex ≈ 0.5em in many fonts. 95 | * 96 | * MDN 97 | */ 98 | def ex = s"${x}ex" 99 | 100 | /** 101 | * This unit represents the font-size of the root element e.g. the font-size 102 | * of the html element. When used on the font-size on this root element, 103 | * it represents its initial value. 104 | * 105 | * MDN 106 | */ 107 | def rem = s"${x}rem" 108 | 109 | /** 110 | * An angle in degrees. One full circle is 360deg. E.g. 0deg, 90deg, 360deg. 111 | */ 112 | def deg = s"${x}deg" 113 | 114 | /** 115 | * An angle in gradians. One full circle is 400grad. E.g. 0grad, 100grad, 116 | * 400grad. 117 | * 118 | * MDN 119 | */ 120 | def grad = s"${x}grad" 121 | 122 | /** 123 | * An angle in radians. One full circle is 2π radians which approximates 124 | * to 6.2832rad. 1rad is 180/π degrees. E.g. 0rad, 1.0708rad, 6.2832rad. 125 | * 126 | * MDN 127 | */ 128 | def rad = s"${x}rad" 129 | 130 | /** 131 | * The number of turns the angle is. One full circle is 1turn. E.g. 0turn, 132 | * 0.25turn, 1turn. 133 | * 134 | * MDN 135 | */ 136 | def turn = s"${x}turn" 137 | 138 | /** 139 | * A percentage value 140 | */ 141 | def pct = s"${x}%" 142 | } 143 | } 144 | 145 | 146 | 147 | -------------------------------------------------------------------------------- /scalatags/src/scalatags/Escaping.scala: -------------------------------------------------------------------------------- 1 | package scalatags 2 | /** 3 | * Utility methods related to validating and escaping XML; used internally but 4 | * potentially useful outside of Scalatags. 5 | */ 6 | object Escaping { 7 | 8 | private[this] val tagRegex = "^[a-z][:\\w0-9-]*$".r 9 | 10 | /** 11 | * Uses a regex to check if something is a valid tag name. 12 | */ 13 | def validTag(s: String) = tagRegex.unapplySeq(s).isDefined 14 | 15 | /** 16 | * Check if 's' is a valid attribute name. 17 | */ 18 | def validAttrName(s: String): Boolean = { 19 | // this is equivalent of the regex but without a huge amount of object creation. 20 | // original regex - ^[a-zA-Z_:][-a-zA-Z0-9_:.]*$ 21 | // n.b. I know its ugly, but its fast 22 | val len = s.length 23 | if(len == 0) 24 | return false 25 | 26 | val sc = s.charAt(0) 27 | val startCharValid = (sc >= 'a' && sc <='z') || (sc >= 'A' && sc <='Z') || sc ==':' 28 | if(!startCharValid) 29 | return false 30 | 31 | var pos = 1 32 | while (pos < len) { 33 | val c = s.charAt(pos) 34 | val valid = (c >= 'a' && c <='z') || (c >= 'A' && c <='Z') || (c >= '0' && c <='9') || 35 | c == '-' || c ==':' || c =='.' || c == '_' 36 | if(!valid) 37 | return false 38 | pos += 1 39 | } 40 | 41 | true 42 | } 43 | 44 | /** 45 | * Code to escape text HTML nodes. Based on code from scala.xml 46 | */ 47 | def escape(text: String, s: java.io.Writer) = { 48 | // Implemented per XML spec: 49 | // http://www.w3.org/International/questions/qa-controls 50 | // Highly imperative code, ~2-3x faster than the previous implementation (2020-06-11) 51 | val charsArray = text.toCharArray 52 | val len = charsArray.size 53 | var pos = 0 54 | var i = 0 55 | while (i < len) { 56 | val c = charsArray(i) 57 | c match { 58 | case '<' => 59 | s.write(charsArray, pos, i - pos) 60 | s.write("<") 61 | pos = i + 1 62 | case '>' => 63 | s.write(charsArray, pos, i - pos) 64 | s.write(">") 65 | pos = i + 1 66 | case '&' => 67 | s.write(charsArray, pos, i - pos) 68 | s.write("&") 69 | pos = i + 1 70 | case '"' => 71 | s.write(charsArray, pos, i - pos) 72 | s.write(""") 73 | pos = i + 1 74 | case '\n' => 75 | case '\r' => 76 | case '\t' => 77 | case c if c < ' ' => 78 | s.write(charsArray, pos, i - pos) 79 | pos = i + 1 80 | case _ => 81 | } 82 | i += 1 83 | } 84 | // Apparently this isn't technically necessary if (len - pos) == 0 as 85 | // it doesn't cause any exception to occur in the JVM. 86 | // The problem is that it isn't documented anywhere so I left this if here 87 | // to make the error clear. 88 | if (pos < len) { 89 | s.write(charsArray, pos, len - pos) 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /scalatags/src/scalatags/Text.scala: -------------------------------------------------------------------------------- 1 | package scalatags 2 | import java.util.Objects 3 | 4 | import scalatags.generic._ 5 | import scala.annotation.unchecked.uncheckedVariance 6 | import scalatags.stylesheet.{StyleSheetFrag, StyleTree} 7 | import scalatags.text.Builder 8 | 9 | import scala.language.implicitConversions 10 | 11 | /** 12 | * A Scalatags module that works with a text back-end, i.e. it creates HTML 13 | * `String`s. 14 | */ 15 | 16 | 17 | object Text 18 | extends generic.Bundle[text.Builder, String, String] 19 | with Aliases[text.Builder, String, String]{ 20 | object attrs extends Text.Cap with Attrs 21 | object tags extends Text.Cap with text.Tags 22 | object tags2 extends Text.Cap with text.Tags2 23 | object styles extends Text.Cap with Styles 24 | object styles2 extends Text.Cap with Styles2 25 | 26 | object svgTags extends Text.Cap with text.SvgTags 27 | object svgAttrs extends Text.Cap with SvgAttrs 28 | 29 | object implicits extends Aggregate with DataConverters 30 | 31 | object all 32 | extends Cap 33 | with Attrs 34 | with Styles 35 | with text.Tags 36 | with DataConverters 37 | with Aggregate 38 | 39 | object short 40 | extends Cap 41 | with text.Tags 42 | with DataConverters 43 | with Aggregate 44 | with AbstractShort{ 45 | 46 | object * extends Cap with Attrs with Styles 47 | } 48 | 49 | trait Cap extends Util with text.TagFactory{ self => 50 | type ConcreteHtmlTag[T <: String] = TypedTag[T] 51 | type BaseTagType = TypedTag[String] 52 | protected[this] implicit def stringAttrX = new GenericAttr[String] 53 | protected[this] implicit def stringStyleX = new GenericStyle[String] 54 | protected[this] implicit def stringPixelStyleX = new GenericPixelStyle[String](stringStyleX) 55 | implicit def UnitFrag(u: Unit) = new Text.StringFrag("") 56 | def makeAbstractTypedTag[T <: String](tag: String, void: Boolean, namespaceConfig: Namespace): TypedTag[T] = { 57 | TypedTag(tag, Nil, void) 58 | } 59 | implicit class SeqFrag[A](xs: Seq[A])(implicit ev: A => Frag) extends Frag{ 60 | Objects.requireNonNull(xs) 61 | def applyTo(t: text.Builder) = xs.foreach(elem => ev(elem).applyTo(t)) 62 | def render = xs.map(elem => ev(elem).render).mkString 63 | } 64 | implicit class GeneratorFrag[A](xs: geny.Generator[A])(implicit ev: A => Frag) extends Frag{ 65 | Objects.requireNonNull(xs) 66 | def applyTo(t: text.Builder) = xs.foreach(elem => ev(elem).applyTo(t)) 67 | def render = xs.map(elem => ev(elem).render).mkString 68 | } 69 | 70 | case class doctype(s: String)(content: text.Frag) extends geny.Writable{ 71 | override def httpContentType = Some("text/html") 72 | def writeTo(strb: java.io.Writer): Unit = { 73 | strb.write(s"") 74 | content.writeTo(strb) 75 | } 76 | def writeBytesTo(out: java.io.OutputStream): Unit = { 77 | val w = new java.io.OutputStreamWriter(out, java.nio.charset.StandardCharsets.UTF_8) 78 | writeTo(w) 79 | w.flush() 80 | } 81 | def render = { 82 | val strb = new java.io.StringWriter() 83 | writeTo(strb) 84 | strb.toString() 85 | } 86 | } 87 | } 88 | 89 | trait Aggregate extends generic.Aggregate[text.Builder, String, String]{ 90 | implicit def ClsModifier(s: stylesheet.Cls): Modifier = new Modifier with text.Builder.ValueSource{ 91 | def applyTo(t: text.Builder) = t.appendAttr("class",this) 92 | 93 | override def appendAttrValue(sb: java.io.Writer): Unit = { 94 | Escaping.escape(s.name, sb) 95 | } 96 | } 97 | implicit class StyleFrag(s: generic.StylePair[text.Builder, _]) extends StyleSheetFrag{ 98 | def applyTo(c: StyleTree) = { 99 | val b = new Builder() 100 | s.applyTo(b) 101 | val Array(style, value) = b.attrsString(b.attrs(b.attrIndex("style"))._2).split(":", 2) 102 | c.copy(styles = c.styles.updated(style, value)) 103 | } 104 | } 105 | 106 | def genericAttr[T] = new Text.GenericAttr[T] 107 | def genericStyle[T] = new Text.GenericStyle[T] 108 | def genericPixelStyle[T](implicit ev: StyleValue[T]): PixelStyleValue[T] = new Text.GenericPixelStyle[T](ev) 109 | def genericPixelStylePx[T](implicit ev: StyleValue[String]): PixelStyleValue[T] = new Text.GenericPixelStylePx[T](ev) 110 | 111 | implicit def stringFrag(v: String) = new Text.StringFrag(v) 112 | 113 | val RawFrag = Text.RawFrag 114 | val StringFrag = Text.StringFrag 115 | type StringFrag = Text.StringFrag 116 | type RawFrag = Text.RawFrag 117 | def raw(s: String) = RawFrag(s) 118 | 119 | 120 | val Tag = Text.TypedTag 121 | } 122 | 123 | 124 | 125 | case class StringFrag(v: String) extends text.Frag{ 126 | Objects.requireNonNull(v) 127 | def render = { 128 | val strb = new java.io.StringWriter() 129 | writeTo(strb) 130 | strb.toString() 131 | } 132 | def writeTo(strb: java.io.Writer) = Escaping.escape(v, strb) 133 | } 134 | 135 | case class RawFrag(v: String) extends text.Frag{ 136 | Objects.requireNonNull(v) 137 | def render = v 138 | def writeTo(strb: java.io.Writer) = strb.append(v) 139 | } 140 | 141 | class GenericAttr[T] extends AttrValue[T] { 142 | def apply(t: text.Builder, a: Attr, v: T): Unit = { 143 | t.setAttr(a.name, Builder.GenericAttrValueSource(v.toString)) 144 | } 145 | } 146 | 147 | class GenericStyle[T] extends StyleValue[T] { 148 | def apply(t: text.Builder, s: Style, v: T): Unit = { 149 | t.appendAttr("style", Builder.StyleValueSource(s, v.toString)) 150 | } 151 | } 152 | class GenericPixelStyle[T](ev: StyleValue[T]) extends PixelStyleValue[T]{ 153 | def apply(s: Style, v: T) = StylePair(s, v, ev) 154 | } 155 | class GenericPixelStylePx[T](ev: StyleValue[String]) extends PixelStyleValue[T]{ 156 | def apply(s: Style, v: T) = { 157 | StylePair(s, s"${v}px", ev) 158 | } 159 | } 160 | 161 | 162 | 163 | case class TypedTag[+Output <: String](tag: String = "", 164 | modifiers: List[Seq[Modifier]], 165 | void: Boolean = false) 166 | extends generic.TypedTag[text.Builder, Output, String] 167 | with text.Frag with geny.Writable{ 168 | override def httpContentType = Some("text/html") 169 | // unchecked because Scala 2.10.4 seems to not like this, even though 170 | // 2.11.1 works just fine. I trust that 2.11.1 is more correct than 2.10.4 171 | // and so just force this. 172 | protected[this] type Self = TypedTag[Output @uncheckedVariance] 173 | 174 | /** 175 | * Serialize this [[TypedTag]] and all its children out to the given StringBuilder. 176 | * 177 | * Although the external interface is pretty simple, the internals are a huge mess, 178 | * because I've inlined a whole lot of things to improve the performance of this code 179 | * ~4x from what it originally was, which is a pretty nice speedup 180 | */ 181 | def writeTo(strb: java.io.Writer): Unit = { 182 | val builder = new text.Builder() 183 | build(builder) 184 | 185 | // tag 186 | strb.append('<').append(tag) 187 | 188 | // attributes 189 | var i = 0 190 | while (i < builder.attrIndex){ 191 | val pair = builder.attrs(i) 192 | strb.append(' ').append(pair._1).append("=\"") 193 | builder.appendAttrStrings(pair._2,strb) 194 | strb.append('\"') 195 | i += 1 196 | } 197 | 198 | if (builder.childIndex == 0 && void) { 199 | // No children - close tag 200 | strb.append(" />") 201 | } else { 202 | strb.append('>') 203 | // Childrens 204 | var i = 0 205 | while(i < builder.childIndex){ 206 | builder.children(i).writeTo(strb) 207 | i += 1 208 | } 209 | 210 | // Closing tag 211 | strb.append("') 212 | } 213 | } 214 | 215 | def apply(xs: Modifier*): TypedTag[Output] = { 216 | this.copy(tag=tag, void = void, modifiers = xs :: modifiers) 217 | } 218 | 219 | /** 220 | * Converts an ScalaTag fragment into an html string 221 | */ 222 | override def toString = { 223 | val strb = new java.io.StringWriter 224 | writeTo(strb) 225 | strb.toString() 226 | } 227 | def render: Output = this.toString.asInstanceOf[Output] 228 | } 229 | 230 | 231 | } 232 | -------------------------------------------------------------------------------- /scalatags/src/scalatags/VirtualDom.scala: -------------------------------------------------------------------------------- 1 | package scalatags 2 | 3 | import java.util.Objects 4 | 5 | import scalatags.generic.{Aliases, Namespace, StylePair} 6 | import scalatags.stylesheet.{StyleSheetFrag, StyleTree} 7 | 8 | import scala.annotation.unchecked.uncheckedVariance 9 | import scala.language.implicitConversions 10 | 11 | /** 12 | * A Scalatags module that can be configured to construct arbitrary virtual DOM 13 | * fragments of uniform type 14 | * 15 | * Simply instantiate it with `stringToFrag` and `rawToFrag` factory to construct 16 | * `Frag`, and a `makeBuilder` factory to construct `Tag`s, and you can then use 17 | * it to instantiate any sort of virtual DOM of uniform type: scala.xml trees, 18 | * Preact/React VDOM nodes in the browser, etc. 19 | */ 20 | trait VirtualDom[Output <: FragT, FragT] 21 | extends generic.Bundle[vdom.Builder[Output, FragT], Output, FragT] 22 | with Aliases[vdom.Builder[Output, FragT], Output, FragT]{ 23 | 24 | def stringToFrag(s: String): FragT 25 | def rawToFrag(s: String): FragT 26 | def makeBuilder(tag: String): vdom.Builder[Output, FragT] 27 | object attrs extends Cap with Attrs 28 | object tags extends Cap with vdom.Tags[Output, FragT] 29 | object tags2 extends Cap with vdom.Tags2[Output, FragT] 30 | object styles extends Cap with Styles 31 | object styles2 extends Cap with Styles2 32 | object svgTags extends Cap with vdom.SvgTags[Output, FragT] 33 | object svgAttrs extends Cap with SvgAttrs 34 | 35 | object implicits extends Aggregate with DataConverters 36 | 37 | object all 38 | extends Cap 39 | with Attrs 40 | with Styles 41 | with vdom.Tags[Output, FragT] 42 | with DataConverters 43 | with Aggregate 44 | 45 | object short 46 | extends Cap 47 | with vdom.Tags[Output, FragT] 48 | with DataConverters 49 | with Aggregate 50 | with AbstractShort{ 51 | 52 | object * extends Cap with Attrs with Styles 53 | } 54 | 55 | 56 | trait Aggregate extends generic.Aggregate[vdom.Builder[Output, FragT], Output, FragT]{ 57 | implicit class ApplyTags(e: vdom.Builder[Output, FragT]){ 58 | def applyTags(mods: Modifier*) = mods.foreach(_.applyTo(e)) 59 | } 60 | implicit def ClsModifier(s: stylesheet.Cls): Modifier = new Modifier{ 61 | def applyTo(t: vdom.Builder[Output, FragT]) = { 62 | t.appendClass(s.name) 63 | 64 | } 65 | } 66 | implicit class StyleFrag(s: generic.StylePair[vdom.Builder[Output, FragT], _]) extends StyleSheetFrag{ 67 | def applyTo(c: StyleTree) = { 68 | ??? 69 | // val b = makeBuilder 70 | // s.applyTo(b) 71 | // val Array(style, value) = b.attrs.get("style").map(_.split(":", 2)) 72 | // .getOrElse(throw new IllegalArgumentException(s"Cannot apply style $s. Does it contain a syntax error?")) 73 | // c.copy(styles = c.styles.updated(style, value)) 74 | } 75 | } 76 | 77 | 78 | def genericAttr[T] = new GenericAttr[T] 79 | def genericStyle[T] = new GenericStyle[T] 80 | def genericPixelStyle[T](implicit ev: StyleValue[T]): PixelStyleValue[T] = new GenericPixelStyle[T](ev) 81 | def genericPixelStylePx[T](implicit ev: StyleValue[String]): PixelStyleValue[T] = new GenericPixelStylePx[T](ev) 82 | 83 | implicit def stringFrag(v: String): StringFrag = new VirtualDom.this.StringFrag(v) 84 | 85 | 86 | val RawFrag = VirtualDom.this.RawFrag 87 | val StringFrag = VirtualDom.this.StringFrag 88 | type StringFrag = VirtualDom.this.StringFrag 89 | type RawFrag = VirtualDom.this.RawFrag 90 | def raw(s: String) = RawFrag(s) 91 | 92 | val Tag = TypedTag 93 | } 94 | 95 | trait Cap extends Util with vdom.TagFactory[Output, FragT]{ self => 96 | type ConcreteHtmlTag[T <: Output] = TypedTag[T] 97 | 98 | protected[this] implicit def stringAttrX = new GenericAttr[String] 99 | protected[this] implicit def stringStyleX = new GenericStyle[String] 100 | protected[this] implicit def stringPixelStyleX = new GenericPixelStyle[String](stringStyleX) 101 | implicit def UnitFrag(u: Unit): VirtualDom.this.StringFrag = new VirtualDom.this.StringFrag("") 102 | def makeAbstractTypedTag[T <: Output](tag: String, void: Boolean, namespaceConfig: Namespace): TypedTag[T] = { 103 | TypedTag(tag, Nil, void, namespaceConfig) 104 | } 105 | 106 | implicit class SeqFrag[A](xs: Seq[A])(implicit ev: A => Frag) extends Frag{ 107 | Objects.requireNonNull(xs) 108 | def applyTo(t: vdom.Builder[Output, FragT]): Unit = xs.foreach(elem => ev(elem).applyTo(t)) 109 | def render: FragT = { 110 | throw new Exception("Rendering of bare arrays of nodes is not supported in virtual dom backend") 111 | } 112 | } 113 | implicit class GeneratorFrag[A](xs: geny.Generator[A])(implicit ev: A => Frag) extends Frag{ 114 | Objects.requireNonNull(xs) 115 | def applyTo(t: vdom.Builder[Output, FragT]): Unit = xs.foreach(elem => ev(elem).applyTo(t)) 116 | def render: FragT = { 117 | throw new Exception("Rendering of bare arrays of nodes is not supported in virtual dom backend") 118 | } 119 | } 120 | } 121 | 122 | case class StringFrag(v: String) extends vdom.Frag[Output, FragT]{ 123 | Objects.requireNonNull(v) 124 | def render: FragT = stringToFrag(v) 125 | } 126 | 127 | case class RawFrag(v: String) extends vdom.Frag[Output, FragT]{ 128 | Objects.requireNonNull(v) 129 | def render = rawToFrag(v) 130 | } 131 | 132 | class GenericAttr[T] extends AttrValue[T]{ 133 | def apply(t: vdom.Builder[Output, FragT], a: Attr, v: T): Unit = { 134 | t.setAttr(a.name, v.toString) 135 | } 136 | } 137 | 138 | class GenericStyle[T] extends StyleValue[T]{ 139 | def apply(t: vdom.Builder[Output, FragT], s: Style, v: T): Unit = { 140 | t.appendStyle(s.cssName, v.toString) 141 | } 142 | } 143 | class GenericPixelStyle[T](ev: StyleValue[T]) extends PixelStyleValue[T]{ 144 | def apply(s: Style, v: T) = StylePair(s, v, ev) 145 | } 146 | class GenericPixelStylePx[T](ev: StyleValue[String]) extends PixelStyleValue[T]{ 147 | def apply(s: Style, v: T) = StylePair(s, s"${v}px", ev) 148 | } 149 | case class TypedTag[+O <: Output](tag: String = "", 150 | modifiers: List[Seq[Modifier]], 151 | void: Boolean = false, 152 | namespace: Namespace) 153 | extends generic.TypedTag[vdom.Builder[Output, FragT], O, FragT] 154 | with vdom.Frag[Output, FragT]{ 155 | // unchecked because Scala 2.10.4 seems to not like this, even though 156 | // 2.11.1 works just fine. I trust that 2.11.1 is more correct than 2.10.4 157 | // and so just force this. 158 | protected[this] type Self = TypedTag[O @uncheckedVariance] 159 | 160 | def render: O = { 161 | val builder = makeBuilder(tag) 162 | build(builder) 163 | builder.render().asInstanceOf[O] 164 | } 165 | /** 166 | * Trivial override, not strictly necessary, but it makes IntelliJ happy... 167 | */ 168 | def apply(xs: Modifier*): TypedTag[O] = { 169 | this.copy(tag = tag, void = void, modifiers = xs :: modifiers) 170 | } 171 | } 172 | 173 | } 174 | 175 | -------------------------------------------------------------------------------- /scalatags/src/scalatags/generic/Bundle.scala: -------------------------------------------------------------------------------- 1 | package scalatags 2 | package generic 3 | import scalatags.stylesheet.StyleSheetFrag 4 | import scalatags.text 5 | 6 | import scala.language.implicitConversions 7 | 8 | /** 9 | * An abstract representation of the Scalatags package. This allows you to 10 | * customize Scalatags to work with different backends, by defining your own 11 | * implementation of `Tag`, and specifying how the various [[Attr]]s 12 | * and [[Style]]s contribute to construct the [[Builder]]. Apart from satisfying the 13 | * default String/Boolean/Numeric implementations of [[Attr]] and [[Style]], 14 | * you can also define your own, e.g. ScalaJS ships with an implicit conversion 15 | * from `js.Any` to `Attr`, so that you can attach objects to the resultant 16 | * `dom.Element` without serializing them. 17 | * 18 | * By default, Scalatags ships with [[scalatags.Text]]: `Bundle[StringBuilder]` 19 | * on all platforms, and [[scalatags.JsDom]]: `Bundle[dom.Element]` on ScalaJS. 20 | * 21 | * It is possible to write entirely backend-agnostic Scalatags code by making 22 | * your code parametric on a [[Bundle]] (or some subclass of it), and importing 23 | * from that rather than importing directly from [[scalatags.JsDom]] or 24 | * [[scalatags.Text]]. You will naturally only be able to use functionality 25 | * (e.g. implicit conversions to [[Attr]]s and [[Style]]s which are present 26 | * in the common interface. 27 | * 28 | * @tparam Builder The type to which [[Attr]]s and [[Style]]s are applied to when the 29 | * `Tag` is being rendered to give a final result. 30 | */ 31 | trait Bundle[Builder, Output <: FragT, FragT] extends Aliases[Builder, Output, FragT] { 32 | /** 33 | * Convenience object for importing all of Scalatags' functionality at once 34 | */ 35 | val all: Attrs with Styles with Tags with DataConverters with Util with Aggregate[Builder, Output, FragT] 36 | /** 37 | * Convenience object for importing only Scalatags' tags (e.g. `div`, `p`) 38 | * into the local namespace, while leaving Styles and Attributes accessible 39 | * via the `*` object 40 | */ 41 | val short: AbstractShort with Tags with DataConverters with Aggregate[Builder, Output, FragT] 42 | /** 43 | * Convenience object for only importing Scalatag's implicits, without importing 44 | * any of the tags, styles or attributes themselves. This includes conversions to 45 | * [[Modifier]], typeclass instances for treating strings and numbers as attributes 46 | * or style values, and other things. 47 | */ 48 | val implicits: Aggregate[Builder, Output, FragT] with DataConverters 49 | 50 | type AbstractShort = generic.AbstractShort[Builder, Output, FragT] 51 | 52 | 53 | /** 54 | * Common attributes. 55 | */ 56 | val attrs: Attrs 57 | /** 58 | * Common tags 59 | */ 60 | val tags: Tags 61 | /** 62 | * Less common tags 63 | */ 64 | val tags2: Tags2 65 | /** 66 | * Common styles 67 | */ 68 | val styles: Styles 69 | /** 70 | * Less common styles 71 | */ 72 | val styles2: Styles2 73 | /** 74 | * SVG only tags 75 | */ 76 | val svgTags: SvgTags 77 | 78 | /** 79 | * SVG only attributes 80 | */ 81 | val svgAttrs: SvgAttrs 82 | 83 | 84 | } 85 | 86 | trait Aliases[Builder, Output <: FragT, FragT]{ 87 | type Attrs = generic.Attrs[Builder, Output, FragT] 88 | type Tags = generic.Tags[Builder, Output, FragT] 89 | type Tags2 = generic.Tags2[Builder, Output, FragT] 90 | type Styles = generic.Styles[Builder, Output, FragT] 91 | type Styles2 = generic.Styles2[Builder, Output, FragT] 92 | type SvgTags = generic.SvgTags[Builder, Output, FragT] 93 | type SvgAttrs = generic.SvgAttrs[Builder, Output, FragT] 94 | type Util = generic.Util[Builder, Output, FragT] 95 | type AttrPair = generic.AttrPair[Builder, _] 96 | 97 | type Attr = generic.Attr 98 | type Style = generic.Style 99 | type Modifier = generic.Modifier[Builder] 100 | 101 | type AttrValue[V] = generic.AttrValue[Builder, V] 102 | type StyleValue[V] = generic.StyleValue[Builder, V] 103 | type PixelStyleValue[V] = generic.PixelStyleValue[Builder, V] 104 | 105 | type Tag = generic.TypedTag[Builder, Output, FragT] 106 | 107 | /** 108 | * A [[Modifier]] which contains a String which will not be escaped. 109 | */ 110 | protected[this] type RawFrag <: Modifier 111 | 112 | /** 113 | * A [[Modifier]] which contains a String. 114 | */ 115 | protected[this] type StringFrag <: Modifier 116 | 117 | type Frag = generic.Frag[Builder, FragT] 118 | } 119 | trait Aggregate[Builder, Output <: FragT, FragT] extends Aliases[Builder, Output, FragT]{ 120 | implicit def StyleFrag(s: StylePair[Builder, _]): StyleSheetFrag 121 | implicit def ClsModifier(s: stylesheet.Cls): Modifier 122 | def genericAttr[T]: AttrValue[T] 123 | implicit val stringAttr: AttrValue[String] = genericAttr[String] 124 | implicit val booleanAttr: AttrValue[Boolean] = genericAttr[Boolean] 125 | implicit val byteAttr: AttrValue[Byte] = genericAttr[Byte] 126 | implicit val shortAttr: AttrValue[Short] = genericAttr[Short] 127 | implicit val intAttr: AttrValue[Int] = genericAttr[Int] 128 | implicit val longAttr: AttrValue[Long] = genericAttr[Long] 129 | implicit val floatAttr: AttrValue[Float] = genericAttr[Float] 130 | implicit val doubleAttr: AttrValue[Double] = genericAttr[Double] 131 | 132 | def genericStyle[T]: StyleValue[T] 133 | implicit val stringStyle: StyleValue[String] = genericStyle[String] 134 | implicit val booleanStyle: StyleValue[Boolean] = genericStyle[Boolean] 135 | implicit val byteStyle: StyleValue[Byte] = genericStyle[Byte] 136 | implicit val shortStyle: StyleValue[Short] = genericStyle[Short] 137 | implicit val intStyle: StyleValue[Int] = genericStyle[Int] 138 | implicit val longStyle: StyleValue[Long] = genericStyle[Long] 139 | implicit val floatStyle: StyleValue[Float] = genericStyle[Float] 140 | implicit val doubleStyle: StyleValue[Double] = genericStyle[Double] 141 | 142 | def genericPixelStyle[T](implicit ev: StyleValue[T]): PixelStyleValue[T] 143 | def genericPixelStylePx[T](implicit ev: StyleValue[String]): PixelStyleValue[T] 144 | implicit val stringPixelStyle: PixelStyleValue[String] = genericPixelStyle[String] 145 | implicit val booleanPixelStyle: PixelStyleValue[Boolean] = genericPixelStyle[Boolean] 146 | implicit val bytePixelStyle: PixelStyleValue[Byte] = genericPixelStylePx[Byte] 147 | implicit val shortPixelStyle: PixelStyleValue[Short] = genericPixelStylePx[Short] 148 | implicit val intPixelStyle: PixelStyleValue[Int] = genericPixelStylePx[Int] 149 | implicit val longPixelStyle: PixelStyleValue[Long] = genericPixelStylePx[Long] 150 | implicit val floatPixelStyle: PixelStyleValue[Float] = genericPixelStylePx[Float] 151 | implicit val doublePixelStyle: PixelStyleValue[Double] = genericPixelStylePx[Double] 152 | 153 | implicit def byteFrag(v: Byte): Frag = stringFrag(v.toString) 154 | implicit def shortFrag(v: Short): Frag = stringFrag(v.toString) 155 | implicit def intFrag(v: Int): Frag = stringFrag(v.toString) 156 | implicit def longFrag(v: Long): Frag = stringFrag(v.toString) 157 | implicit def floatFrag(v: Float): Frag = stringFrag(v.toString) 158 | implicit def doubleFrag(v: Double): Frag = stringFrag(v.toString) 159 | implicit def stringFrag(v: String): Frag 160 | 161 | /** 162 | * Delimits a string that should be included in the result as raw, 163 | * un-escaped HTML 164 | */ 165 | def raw(s: String): RawFrag 166 | } 167 | trait AbstractShort[Builder, Output <: FragT, FragT]{ 168 | val `*`: generic.Attrs[Builder, Output, FragT] with generic.Styles[Builder, Output, FragT] 169 | } 170 | -------------------------------------------------------------------------------- /scalatags/src/scalatags/generic/Core.scala: -------------------------------------------------------------------------------- 1 | package scalatags 2 | package generic 3 | 4 | import java.util.Objects.requireNonNull 5 | 6 | import scala.annotation.implicitNotFound 7 | 8 | 9 | /** 10 | * Represents a value that can be nested within a [[scalatags.generic.TypedTag]]. This can be 11 | * another [[scalatags.generic.Modifier]], but can also be a CSS style or HTML attribute binding, 12 | * which will add itself to the node's attributes but not appear in the final 13 | * `children` list. 14 | */ 15 | trait Modifier[Builder] { 16 | /** 17 | * Applies this modifier to the specified [[Builder]], such that when 18 | * rendering is complete the effect of adding this modifier can be seen. 19 | */ 20 | def applyTo(t: Builder): Unit 21 | } 22 | 23 | /** 24 | * Marker sub-type of [[scalatags.generic.Modifier]] which signifies that that type can be 25 | * rendered as a standalone fragment of [[FragT]]. This excludes things 26 | * like [[scalatags.generic.AttrPair]]s or [[scalatags.generic.StylePair]]s which only make sense as part of 27 | * a parent fragment 28 | */ 29 | trait Frag[Builder, +FragT] extends Modifier[Builder]{ 30 | def render: FragT 31 | } 32 | 33 | /** 34 | * A generic representation of a Scalatags tag. 35 | * 36 | * @tparam Output The base type that this tag represents. On Scala-JVM, this is all 37 | * `Nothing`, while on ScalaJS this could be the `dom.XXXElement` 38 | * associated with that tag name. 39 | */ 40 | trait TypedTag[Builder, +Output <: FragT, +FragT] extends Frag[Builder, Output]{ 41 | protected[this] type Self <: TypedTag[Builder, Output, FragT] 42 | def tag: String 43 | 44 | /** 45 | * The modifiers that are applied to a TypedTag are kept in this linked-Seq 46 | * (which are actually WrappedArrays) data-structure in order for maximum 47 | * performance. 48 | */ 49 | def modifiers: List[Seq[Modifier[Builder]]] 50 | 51 | /** 52 | * Walks the [[modifiers]] to apply them to a particular [[Builder]]. 53 | * Super sketchy/procedural for max performance. 54 | */ 55 | def build(b: Builder): Unit = { 56 | var current = modifiers 57 | val arr = new Array[Seq[Modifier[Builder]]](modifiers.length) 58 | 59 | var i = 0 60 | while(current != Nil){ 61 | arr(i) = current.head 62 | current = current.tail 63 | i += 1 64 | } 65 | 66 | var j = arr.length 67 | while (j > 0) { 68 | j -= 1 69 | val frag = arr(j) 70 | var i = 0 71 | while(i < frag.length){ 72 | frag(i).applyTo(b) 73 | i += 1 74 | } 75 | } 76 | } 77 | /** 78 | * Add the given modifications (e.g. additional children, or new attributes) 79 | * to the [[TypedTag]]. 80 | */ 81 | def apply(xs: Modifier[Builder]*): Self 82 | 83 | /** 84 | * Collapses this scalatags tag tree and returns an [[Output]] 85 | */ 86 | def render: Output 87 | } 88 | 89 | /** 90 | * Wraps up a HTML attribute in a value which isn't a string. 91 | * 92 | * @param name the name of this particular attribute 93 | * @param namespace an XML [[Namespace]] that this attribute lives in 94 | * @param raw all [[Attr]]s are checked to fail fast if their names are 95 | * invalid XML attrs; flagging them as [[raw]] disables the checks 96 | * in the few cases you actually want invalid XML attrs 97 | * (e.g. AngularJS) 98 | */ 99 | case class Attr(name: String, namespace: Option[Namespace] = None, raw: Boolean = false) { 100 | 101 | if (!raw && !Escaping.validAttrName(name)) { 102 | throw new IllegalArgumentException( 103 | s"Illegal attribute name: $name is not a valid XML attribute name" 104 | ) 105 | } 106 | /** 107 | * Creates an [[AttrPair]] from an [[Attr]] and a value of type [[T]], if 108 | * there is an [[AttrValue]] of the correct type. 109 | */ 110 | def :=[Builder, T](v: T)(implicit ev: AttrValue[Builder, T]) = { 111 | requireNonNull(v) 112 | AttrPair(this, v, ev) 113 | } 114 | 115 | def empty[Builder](implicit ev: AttrValue[Builder, String]) = this := name 116 | } 117 | 118 | /** 119 | * Wraps up a CSS style in a value. 120 | */ 121 | case class Style(jsName: String, cssName: String) { 122 | /** 123 | * Creates an [[StylePair]] from an [[Style]] and a value of type [[T]], if 124 | * there is an [[StyleValue]] of the correct type. 125 | */ 126 | def :=[Builder, T](v: T)(implicit ev: StyleValue[Builder, T]) = { 127 | requireNonNull(v) 128 | StylePair(this, v, ev) 129 | } 130 | } 131 | /** 132 | * Wraps up a CSS style in a value. 133 | */ 134 | case class PixelStyle(jsName: String, cssName: String) { 135 | val realStyle = Style(jsName, cssName) 136 | /** 137 | * Creates an [[StylePair]] from an [[Style]] and a value of type [[T]], if 138 | * there is an [[StyleValue]] of the correct type. 139 | */ 140 | def :=[Builder, T](v: T)(implicit ev: PixelStyleValue[Builder, T]) = { 141 | requireNonNull(v) 142 | ev(realStyle, v) 143 | } 144 | 145 | } 146 | trait StyleProcessor{ 147 | def apply[T](t: T): String 148 | } 149 | /** 150 | * An [[Attr]], it's associated value, and an [[AttrValue]] of the correct type 151 | */ 152 | case class AttrPair[Builder, T](a: Attr, v: T, ev: AttrValue[Builder, T]) extends Modifier[Builder] { 153 | override def applyTo(t: Builder): Unit = { 154 | ev.apply(t, a, v) 155 | } 156 | def :=[Builder, T](v: T)(implicit ev: AttrValue[Builder, T]) = { 157 | requireNonNull(v) 158 | AttrPair(a, v, ev) 159 | } 160 | } 161 | /** 162 | * Used to specify how to handle a particular type [[T]] when it is used as 163 | * the value of a [[Attr]]. Only types with a specified [[AttrValue]] may 164 | * be used. 165 | */ 166 | @implicitNotFound( 167 | "No AttrValue defined for type ${T}; scalatags does not know how to use ${T} as an attribute" 168 | ) 169 | trait AttrValue[Builder, T]{ 170 | def apply(t: Builder, a: Attr, v: T): Unit 171 | } 172 | 173 | /** 174 | * A [[Style]], it's associated value, and a [[StyleValue]] of the correct type 175 | */ 176 | case class StylePair[Builder, T](s: Style, v: T, ev: StyleValue[Builder, T]) extends Modifier[Builder]{ 177 | override def applyTo(t: Builder): Unit = { 178 | ev.apply(t, s, v) 179 | } 180 | } 181 | 182 | /** 183 | * Used to specify how to handle a particular type [[T]] when it is used as 184 | * the value of a [[Style]]. Only types with a specified [[StyleValue]] may 185 | * be used. 186 | */ 187 | @implicitNotFound( 188 | "No StyleValue defined for type ${T}; scalatags does not know how to use ${T} as an style" 189 | ) 190 | trait StyleValue[Builder, T]{ 191 | def apply(t: Builder, s: Style, v: T): Unit 192 | } 193 | 194 | @implicitNotFound( 195 | "No PixelStyleValue defined for type ${T}; scalatags does not know how to use ${T} as an style" 196 | ) 197 | trait PixelStyleValue[Builder, T]{ 198 | def apply(s: Style, v: T): StylePair[Builder, _] 199 | } 200 | 201 | /** 202 | * Represents a single XML namespace. This is currently ignored in `scalatags.Text`, 203 | * but used to create elements with the correct namespace in `scalatags.JsDom`. A 204 | * [[Namespace]] can be provided implicitly (or explicitly) when creating tags via 205 | * `"".tag`, with a default of "http://www.w3.org/1999/xhtml" if none is found. 206 | */ 207 | trait Namespace { 208 | def uri: String 209 | } 210 | object Namespace{ 211 | implicit val htmlNamespaceConfig: Namespace = new Namespace { 212 | def uri = "http://www.w3.org/1999/xhtml" 213 | } 214 | val svgNamespaceConfig = new Namespace { 215 | def uri = "http://www.w3.org/2000/svg" 216 | } 217 | val svgXlinkNamespaceConfig = new Namespace { 218 | def uri = "http://www.w3.org/1999/xlink" 219 | } 220 | } 221 | -------------------------------------------------------------------------------- /scalatags/src/scalatags/generic/Tags.scala: -------------------------------------------------------------------------------- 1 | package scalatags.generic 2 | 3 | 4 | /** 5 | * Trait that contains the contents of the `Tags` object, so they can be mixed 6 | * in to other objects if needed. 7 | */ 8 | trait Tags[Builder, Output <: FragT, FragT] extends Util[Builder, Output, FragT]{ 9 | 10 | 11 | 12 | // Root Element 13 | /** 14 | * Represents the root of an HTML or XHTML document. All other elements must 15 | * be descendants of this element. 16 | * 17 | * MDN 18 | */ 19 | def html: TypedTag[Builder, Output, FragT] 20 | 21 | // Document Metadata 22 | /** 23 | * Represents a collection of metadata about the document, including links to, 24 | * or definitions of, scripts and style sheets. 25 | * 26 | * MDN 27 | */ 28 | def head: TypedTag[Builder, Output, FragT] 29 | 30 | /** 31 | * Defines the base URL for relative URLs in the page. 32 | * 33 | * MDN 34 | */ 35 | def base: TypedTag[Builder, Output, FragT] 36 | /** 37 | * Used to link JavaScript and external CSS with the current HTML document. 38 | * 39 | * MDN 40 | */ 41 | def link: TypedTag[Builder, Output, FragT] 42 | /** 43 | * Defines metadata that can't be defined using another HTML element. 44 | * 45 | * MDN 46 | */ 47 | def meta: TypedTag[Builder, Output, FragT] 48 | 49 | 50 | // Scripting 51 | /** 52 | * Defines either an internal script or a link to an external script. The 53 | * script language is JavaScript. 54 | * 55 | * MDN 56 | */ 57 | def script: TypedTag[Builder, Output, FragT] 58 | 59 | 60 | // Sections 61 | /** 62 | * Represents the content of an HTML document. There is only one body 63 | * element in a document. 64 | * 65 | * MDN 66 | */ 67 | def body: TypedTag[Builder, Output, FragT] 68 | 69 | /** 70 | * Heading level 1 71 | * 72 | * MDN 73 | */ 74 | def h1: TypedTag[Builder, Output, FragT] 75 | /** 76 | * Heading level 2 77 | * 78 | * MDN 79 | */ 80 | def h2: TypedTag[Builder, Output, FragT] 81 | /** 82 | * Heading level 3 83 | * 84 | * MDN 85 | */ 86 | def h3: TypedTag[Builder, Output, FragT] 87 | /** 88 | * Heading level 4 89 | * 90 | * MDN 91 | */ 92 | def h4: TypedTag[Builder, Output, FragT] 93 | /** 94 | * Heading level 5 95 | * 96 | * MDN 97 | */ 98 | def h5: TypedTag[Builder, Output, FragT] 99 | /** 100 | * Heading level 6 101 | * 102 | * MDN 103 | */ 104 | def h6: TypedTag[Builder, Output, FragT] 105 | /** 106 | * Defines the header of a page or section. It often contains a logo, the 107 | * title of the Web site, and a navigational table of content. 108 | * 109 | * MDN 110 | */ 111 | def header: TypedTag[Builder, Output, FragT] 112 | /** 113 | * Defines the footer for a page or section. It often contains a copyright 114 | * notice, some links to legal information, or addresses to give feedback. 115 | * 116 | * MDN 117 | */ 118 | def footer: TypedTag[Builder, Output, FragT] 119 | 120 | 121 | // Grouping content 122 | /** 123 | * Defines a portion that should be displayed as a paragraph. 124 | * 125 | * MDN 126 | */ 127 | def p: TypedTag[Builder, Output, FragT] 128 | /** 129 | * Represents a thematic break between paragraphs of a section or article or 130 | * any longer content. 131 | * 132 | * MDN 133 | */ 134 | def hr: TypedTag[Builder, Output, FragT] 135 | /** 136 | * Indicates that its content is preformatted and that this format must be 137 | * preserved. 138 | * 139 | * MDN 140 | */ 141 | def pre: TypedTag[Builder, Output, FragT] 142 | /** 143 | * Represents a content that is quoted from another source. 144 | * 145 | * MDN 146 | */ 147 | def blockquote: TypedTag[Builder, Output, FragT] 148 | /** 149 | * Defines an ordered list of items. 150 | * 151 | * MDN 152 | */ 153 | def ol: TypedTag[Builder, Output, FragT] 154 | /** 155 | * Defines an unordered list of items. 156 | * 157 | * MDN 158 | */ 159 | def ul: TypedTag[Builder, Output, FragT] 160 | /** 161 | * Defines an item of an list. 162 | * 163 | * MDN 164 | */ 165 | def li: TypedTag[Builder, Output, FragT] 166 | /** 167 | * Defines a definition list; a list of terms and their associated definitions. 168 | * 169 | * MDN 170 | */ 171 | def dl: TypedTag[Builder, Output, FragT] 172 | /** 173 | * Represents a term defined by the next dd 174 | * 175 | * MDN 176 | */ 177 | def dt: TypedTag[Builder, Output, FragT] 178 | /** 179 | * Represents the definition of the terms immediately listed before it. 180 | * 181 | * MDN 182 | */ 183 | def dd: TypedTag[Builder, Output, FragT] 184 | /** 185 | * Represents a figure illustrated as part of the document. 186 | * 187 | * MDN 188 | */ 189 | def figure: TypedTag[Builder, Output, FragT] 190 | /** 191 | * Represents the legend of a figure. 192 | * 193 | * MDN 194 | */ 195 | def figcaption: TypedTag[Builder, Output, FragT] 196 | /** 197 | * Represents a generic container with no special meaning. 198 | * 199 | * MDN 200 | */ 201 | def div: TypedTag[Builder, Output, FragT] 202 | 203 | // Text-level semantics 204 | /** 205 | * Represents a hyperlink, linking to another resource. 206 | * 207 | * MDN 208 | */ 209 | def a: TypedTag[Builder, Output, FragT] 210 | /** 211 | * Represents emphasized text. 212 | * 213 | * MDN 214 | */ 215 | def em: TypedTag[Builder, Output, FragT] 216 | /** 217 | * Represents especially important text. 218 | * 219 | * MDN 220 | */ 221 | def strong: TypedTag[Builder, Output, FragT] 222 | /** 223 | * Represents a side comment; text like a disclaimer or copyright, which is not 224 | * essential to the comprehension of the document. 225 | * 226 | * MDN 227 | */ 228 | def small: TypedTag[Builder, Output, FragT] 229 | /** 230 | * Strikethrough element, used for that is no longer accurate or relevant. 231 | * 232 | * MDN 233 | */ 234 | def s: TypedTag[Builder, Output, FragT] 235 | /** 236 | * Represents the title of a work being cited. 237 | * 238 | * MDN 239 | */ 240 | def cite: TypedTag[Builder, Output, FragT] 241 | 242 | /** 243 | * Represents computer code. 244 | * 245 | * MDN 246 | */ 247 | def code: TypedTag[Builder, Output, FragT] 248 | 249 | /** 250 | * Subscript tag 251 | * 252 | * MDN 253 | */ 254 | def sub: TypedTag[Builder, Output, FragT] 255 | /** 256 | * Superscript tag. 257 | * 258 | * MDN 259 | */ 260 | def sup: TypedTag[Builder, Output, FragT] 261 | /** 262 | * Italicized text. 263 | * 264 | * MDN 265 | */ 266 | def i: TypedTag[Builder, Output, FragT] 267 | /** 268 | * Bold text. 269 | * 270 | * MDN 271 | */ 272 | def b: TypedTag[Builder, Output, FragT] 273 | /** 274 | * Underlined text. 275 | * 276 | * MDN 277 | */ 278 | def u: TypedTag[Builder, Output, FragT] 279 | 280 | /** 281 | * Represents text with no specific meaning. This has to be used when no other 282 | * text-semantic element conveys an adequate meaning, which, in this case, is 283 | * often brought by global attributes like class, lang, or dir. 284 | * 285 | * MDN 286 | */ 287 | def span: TypedTag[Builder, Output, FragT] 288 | /** 289 | * Represents a line break. 290 | * 291 | * MDN 292 | */ 293 | def br: TypedTag[Builder, Output, FragT] 294 | /** 295 | * Represents a line break opportunity, that is a suggested point for wrapping 296 | * text in order to improve readability of text split on several lines. 297 | * 298 | * MDN 299 | */ 300 | def wbr: TypedTag[Builder, Output, FragT] 301 | 302 | // Edits 303 | /** 304 | * Defines an addition to the document. 305 | * 306 | * MDN 307 | */ 308 | def ins: TypedTag[Builder, Output, FragT] 309 | /** 310 | * Defines a removal from the document. 311 | * 312 | * MDN 313 | */ 314 | def del: TypedTag[Builder, Output, FragT] 315 | 316 | // Embedded content 317 | /** 318 | * Represents an image. 319 | * 320 | * MDN 321 | */ 322 | def img: TypedTag[Builder, Output, FragT] 323 | /** 324 | * Represents a nested browsing context, that is an embedded HTML document. 325 | * 326 | * MDN 327 | */ 328 | def iframe: TypedTag[Builder, Output, FragT] 329 | /** 330 | * Represents a integration point for an external, often non-HTML, application 331 | * or interactive content. 332 | * 333 | * MDN 334 | */ 335 | def embed: TypedTag[Builder, Output, FragT] 336 | /** 337 | * Represents an external resource, which is treated as an image, an HTML 338 | * sub-document, or an external resource to be processed by a plug-in. 339 | * 340 | * MDN 341 | */ 342 | def `object`: TypedTag[Builder, Output, FragT] 343 | /** 344 | * Defines parameters for use by plug-ins invoked by object elements. 345 | * 346 | * MDN 347 | */ 348 | def param: TypedTag[Builder, Output, FragT] 349 | /** 350 | * Represents a video, and its associated audio files and captions, with the 351 | * necessary interface to play it. 352 | * 353 | * MDN 354 | */ 355 | def video: TypedTag[Builder, Output, FragT] 356 | /** 357 | * Represents a sound or an audio stream. 358 | * 359 | * MDN 360 | */ 361 | def audio: TypedTag[Builder, Output, FragT] 362 | /** 363 | * Allows the authors to specify alternate media resources for media elements 364 | * like video or audio 365 | * 366 | * MDN 367 | */ 368 | def source: TypedTag[Builder, Output, FragT] 369 | /** 370 | * Allows authors to specify timed text track for media elements like video or 371 | * audio 372 | * 373 | * MDN 374 | */ 375 | def track: TypedTag[Builder, Output, FragT] 376 | /** 377 | * Represents a bitmap area that scripts can use to render graphics like graphs, 378 | * games or any visual images on the fly. 379 | * 380 | * MDN 381 | */ 382 | def canvas: TypedTag[Builder, Output, FragT] 383 | /** 384 | * In conjunction with area, defines an image map. 385 | * 386 | * MDN 387 | */ 388 | def map: TypedTag[Builder, Output, FragT] 389 | /** 390 | * In conjunction with map, defines an image map 391 | * 392 | * MDN 393 | */ 394 | def area: TypedTag[Builder, Output, FragT] 395 | 396 | 397 | // Tabular data 398 | /** 399 | * Represents data with more than one dimension. 400 | * 401 | * MDN 402 | */ 403 | def table: TypedTag[Builder, Output, FragT] 404 | /** 405 | * The title of a table. 406 | * 407 | * MDN 408 | */ 409 | def caption: TypedTag[Builder, Output, FragT] 410 | /** 411 | * A set of columns. 412 | * 413 | * MDN 414 | */ 415 | def colgroup: TypedTag[Builder, Output, FragT] 416 | /** 417 | * A single column. 418 | * 419 | * MDN 420 | */ 421 | def col: TypedTag[Builder, Output, FragT] 422 | /** 423 | * The table body. 424 | * 425 | * MDN 426 | */ 427 | def tbody: TypedTag[Builder, Output, FragT] 428 | /** 429 | * The table headers. 430 | * 431 | * MDN 432 | */ 433 | def thead: TypedTag[Builder, Output, FragT] 434 | /** 435 | * The table footer. 436 | * 437 | * MDN 438 | */ 439 | def tfoot: TypedTag[Builder, Output, FragT] 440 | /** 441 | * A single row in a table. 442 | * 443 | * MDN 444 | */ 445 | def tr: TypedTag[Builder, Output, FragT] 446 | /** 447 | * A single cell in a table. 448 | * 449 | * MDN 450 | */ 451 | def td: TypedTag[Builder, Output, FragT] 452 | /** 453 | * A header cell in a table. 454 | * 455 | * MDN 456 | */ 457 | def th: TypedTag[Builder, Output, FragT] 458 | 459 | // Forms 460 | /** 461 | * Represents a form, consisting of controls, that can be submitted to a 462 | * server for processing. 463 | * 464 | * MDN 465 | */ 466 | def form: TypedTag[Builder, Output, FragT] 467 | /** 468 | * A set of fields. 469 | * 470 | * MDN 471 | */ 472 | def fieldset: TypedTag[Builder, Output, FragT] 473 | /** 474 | * The caption for a fieldset. 475 | * 476 | * MDN 477 | */ 478 | def legend: TypedTag[Builder, Output, FragT] 479 | /** 480 | * The caption of a single field 481 | * 482 | * MDN 483 | */ 484 | def label: TypedTag[Builder, Output, FragT] 485 | /** 486 | * A typed data field allowing the user to input data. 487 | * 488 | * MDN 489 | */ 490 | def input: TypedTag[Builder, Output, FragT] 491 | /** 492 | * A button 493 | * 494 | * MDN 495 | */ 496 | def button: TypedTag[Builder, Output, FragT] 497 | /** 498 | * A control that allows the user to select one of a set of options. 499 | * 500 | * MDN 501 | */ 502 | def select: TypedTag[Builder, Output, FragT] 503 | /** 504 | * A set of predefined options for other controls. 505 | * 506 | * MDN 507 | */ 508 | def datalist: TypedTag[Builder, Output, FragT] 509 | /** 510 | * A set of options, logically grouped. 511 | * 512 | * MDN 513 | */ 514 | def optgroup: TypedTag[Builder, Output, FragT] 515 | /** 516 | * An option in a select element. 517 | * 518 | * MDN 519 | */ 520 | def option: TypedTag[Builder, Output, FragT] 521 | /** 522 | * A multiline text edit control. 523 | * 524 | * MDN 525 | */ 526 | def textarea: TypedTag[Builder, Output, FragT] 527 | } 528 | -------------------------------------------------------------------------------- /scalatags/src/scalatags/generic/Tags2.scala: -------------------------------------------------------------------------------- 1 | package scalatags.generic 2 | 3 | 4 | /** 5 | * Contains HTML tags which are used less frequently. These are generally 6 | * imported individually as needed. 7 | */ 8 | trait Tags2[Builder, Output <: FragT, FragT] extends Util[Builder, Output, FragT]{ 9 | 10 | // Document Metadata 11 | /** 12 | * Defines the title of the document, shown in a browser's title bar or on the 13 | * page's tab. It can only contain text and any contained tags are not 14 | * interpreted. 15 | * 16 | * MDN 17 | */ 18 | def title: TypedTag[Builder, Output, FragT] 19 | 20 | /** 21 | * Used to write inline CSS. 22 | * 23 | * MDN 24 | */ 25 | def style: TypedTag[Builder, Output, FragT] 26 | // Scripting 27 | /** 28 | * Defines alternative content to display when the browser doesn't support 29 | * scripting. 30 | * 31 | * MDN 32 | */ 33 | def noscript: TypedTag[Builder, Output, FragT] 34 | 35 | // Sections 36 | /** 37 | * Represents a generic section of a document, i.e., a thematic grouping of 38 | * content, typically with a heading. 39 | * 40 | * MDN 41 | */ 42 | def section: TypedTag[Builder, Output, FragT] 43 | /** 44 | * Represents a section of a page that links to other pages or to parts within 45 | * the page: a section with navigation links. 46 | * 47 | * MDN 48 | */ 49 | def nav: TypedTag[Builder, Output, FragT] 50 | /** 51 | * Defines self-contained content that could exist independently of the rest 52 | * of the content. 53 | * 54 | * MDN 55 | */ 56 | def article: TypedTag[Builder, Output, FragT] 57 | /** 58 | * Defines some content loosely related to the page content. If it is removed, 59 | * the remaining content still makes sense. 60 | * 61 | * MDN 62 | */ 63 | def aside: TypedTag[Builder, Output, FragT] 64 | /** 65 | * Defines a section containing contact information. 66 | * 67 | * MDN 68 | */ 69 | def address: TypedTag[Builder, Output, FragT] 70 | 71 | /** 72 | * Defines the main or important content in the document. There is only one 73 | * main element in the document. 74 | * 75 | * MDN 76 | */ 77 | def main: TypedTag[Builder, Output, FragT] 78 | 79 | // Text level semantics 80 | 81 | /** 82 | * An inline quotation. 83 | * 84 | * MDN 85 | */ 86 | def q: TypedTag[Builder, Output, FragT] 87 | /** 88 | * Represents a term whose definition is contained in its nearest ancestor 89 | * content. 90 | * 91 | * MDN 92 | */ 93 | def dfn: TypedTag[Builder, Output, FragT] 94 | /** 95 | * An abbreviation or acronym; the expansion of the abbreviation can be 96 | * represented in the title attribute. 97 | * 98 | * MDN 99 | */ 100 | def abbr: TypedTag[Builder, Output, FragT] 101 | /** 102 | * Associates to its content a machine-readable equivalent. 103 | * 104 | * MDN 105 | */ 106 | def data: TypedTag[Builder, Output, FragT] 107 | /** 108 | * Represents a date and time value; the machine-readable equivalent can be 109 | * represented in the datetime attribetu 110 | * 111 | * MDN 112 | */ 113 | def time: TypedTag[Builder, Output, FragT] 114 | /** 115 | * Represents a variable. 116 | * 117 | * MDN 118 | */ 119 | def `var`: TypedTag[Builder, Output, FragT] 120 | /** 121 | * Represents the output of a program or a computer. 122 | * 123 | * MDN 124 | */ 125 | def samp: TypedTag[Builder, Output, FragT] 126 | /** 127 | * Represents user input, often from a keyboard, but not necessarily. 128 | * 129 | * MDN 130 | */ 131 | def kbd: TypedTag[Builder, Output, FragT] 132 | 133 | /** 134 | * Defines a mathematical formula. 135 | * 136 | * MDN 137 | */ 138 | def math: TypedTag[Builder, Output, FragT] 139 | /** 140 | * Represents text highlighted for reference purposes, that is for its 141 | * relevance in another context. 142 | * 143 | * MDN 144 | */ 145 | def mark: TypedTag[Builder, Output, FragT] 146 | /** 147 | * Represents content to be marked with ruby annotations, short runs of text 148 | * presented alongside the text. This is often used in conjunction with East 149 | * Asian language where the annotations act as a guide for pronunciation, like 150 | * the Japanese furigana . 151 | * 152 | * MDN 153 | */ 154 | def ruby: TypedTag[Builder, Output, FragT] 155 | /** 156 | * Represents the text of a ruby annotation. 157 | * 158 | * MDN 159 | */ 160 | def rt: TypedTag[Builder, Output, FragT] 161 | /** 162 | * Represents parenthesis around a ruby annotation, used to display the 163 | * annotation in an alternate way by browsers not supporting the standard 164 | * display for annotations. 165 | * 166 | * MDN 167 | */ 168 | def rp: TypedTag[Builder, Output, FragT] 169 | /** 170 | * Represents text that must be isolated from its surrounding for bidirectional 171 | * text formatting. It allows embedding a span of text with a different, or 172 | * unknown, directionality. 173 | * 174 | * MDN 175 | */ 176 | def bdi: TypedTag[Builder, Output, FragT] 177 | /** 178 | * Represents the directionality of its children, in order to explicitly 179 | * override the Unicode bidirectional algorithm. 180 | * 181 | * MDN 182 | */ 183 | def bdo: TypedTag[Builder, Output, FragT] 184 | 185 | // Forms 186 | 187 | /** 188 | * A key-pair generator control. 189 | * 190 | * MDN 191 | */ 192 | def keygen: TypedTag[Builder, Output, FragT] 193 | /** 194 | * The result of a calculation 195 | * 196 | * MDN 197 | */ 198 | def output: TypedTag[Builder, Output, FragT] 199 | /** 200 | * A progress completion bar 201 | * 202 | * MDN 203 | */ 204 | def progress: TypedTag[Builder, Output, FragT] 205 | /** 206 | * A scalar measurement within a known range. 207 | * 208 | * MDN 209 | */ 210 | def meter: TypedTag[Builder, Output, FragT] 211 | 212 | 213 | // Interactive elements 214 | /** 215 | * A widget from which the user can obtain additional information 216 | * or controls. 217 | * 218 | * MDN 219 | */ 220 | def details: TypedTag[Builder, Output, FragT] 221 | /** 222 | * A summary, caption, or legend for a given details. 223 | * 224 | * MDN 225 | */ 226 | def summary: TypedTag[Builder, Output, FragT] 227 | /** 228 | * A command that the user can invoke. 229 | * 230 | * MDN 231 | */ 232 | def command: TypedTag[Builder, Output, FragT] 233 | /** 234 | * A list of commands 235 | * 236 | * MDN 237 | */ 238 | def menu: TypedTag[Builder, Output, FragT] 239 | } 240 | -------------------------------------------------------------------------------- /scalatags/src/scalatags/generic/Util.scala: -------------------------------------------------------------------------------- 1 | package scalatags 2 | package generic 3 | import java.util.Objects 4 | 5 | import scala.language.higherKinds 6 | import scala.language.implicitConversions 7 | 8 | /** 9 | * Created by haoyi on 6/2/14. 10 | */ 11 | trait Util[Builder, Output <: FragT, FragT] extends LowPriUtil[Builder, Output, FragT]{ 12 | 13 | type ConcreteHtmlTag[T <: Output] <: TypedTag[Builder, T, FragT] 14 | 15 | def frag(frags: Frag[Builder, FragT]*): Frag[Builder, FragT] = SeqFrag(frags) 16 | def modifier(mods: Modifier[Builder]*): Modifier[Builder] = SeqNode(mods) 17 | 18 | def tag(s: String, void: Boolean = false): TypedTag[Builder, Output, FragT] 19 | def makeAbstractTypedTag[T <: Output](tag: String, void: Boolean, namespaceConfig: Namespace): ConcreteHtmlTag[T] 20 | protected[this] implicit def stringAttrX: AttrValue[Builder, String] 21 | protected[this] implicit def stringStyleX: StyleValue[Builder, String] 22 | protected[this] implicit def stringPixelStyleX: PixelStyleValue[Builder, String] 23 | 24 | /** 25 | * Constructs an [[Attr]] attribute object from a string; can be used inline: 26 | * 27 | * {{{ 28 | * div( 29 | * attr("hello-world-special-attr") := "foo 30 | * ) 31 | * }}} 32 | * 33 | * Or assigned to a name and used later 34 | * 35 | * 36 | * {{{ 37 | * val hello = attr("hello-world-special-attr") 38 | * div( 39 | * hello := "foo 40 | * ) 41 | * }}} 42 | */ 43 | def attr(s: String, ns: Namespace = null, raw: Boolean = false) = Attr(s, Option(ns), raw) 44 | 45 | /** 46 | * Constructs a CSS [[Style]] from a string, can be used inline 47 | * 48 | * {{{ 49 | * div( 50 | * css("-moz-special-style") := "foo" 51 | * ) 52 | * }}} 53 | * 54 | * Or assigned to a name and used later 55 | * 56 | * {{{ 57 | * val mozSpecial := css("-moz-special-style") 58 | * div( 59 | * mozSpecial := "foo" 60 | * ) 61 | * }}} 62 | */ 63 | def css(s: String) = Style(camelCase(s), s) 64 | 65 | 66 | /** 67 | * Allows you to modify a [[ConcreteHtmlTag]] by adding a Seq containing other nest-able 68 | * objects to its list of children. 69 | */ 70 | implicit class SeqNode[A](xs: Seq[A])(implicit ev: A => Modifier[Builder]) extends Modifier[Builder]{ 71 | Objects.requireNonNull(xs) 72 | def applyTo(t: Builder) = xs.foreach(elem => ev(elem).applyTo(t)) 73 | } 74 | 75 | /** 76 | * Allows you to modify a [[ConcreteHtmlTag]] by adding an Option containing other nest-able 77 | * objects to its list of children. 78 | */ 79 | implicit def OptionNode[A](xs: Option[A])(implicit ev: A => Modifier[Builder]): SeqNode[A] = new SeqNode(xs.toSeq) 80 | 81 | /** 82 | * Allows you to modify a [[ConcreteHtmlTag]] by adding an Array containing other nest-able 83 | * objects to its list of children. 84 | */ 85 | implicit def ArrayNode[A](xs: Array[A])(implicit ev: A => Modifier[Builder]): SeqNode[A] = new SeqNode[A](xs.toSeq) 86 | 87 | 88 | } 89 | 90 | trait LowPriUtil[Builder, Output <: FragT, FragT]{ 91 | /** 92 | * Renders an Seq of [[FragT]] into a single [[FragT]] 93 | */ 94 | implicit def SeqFrag[A](xs: Seq[A])(implicit ev: A => Frag[Builder, FragT]): Frag[Builder, FragT] 95 | /** 96 | * Renders an Seq of [[FragT]] into a single [[FragT]] 97 | */ 98 | implicit def GeneratorFrag[A](xs: geny.Generator[A])(implicit ev: A => Frag[Builder, FragT]): Frag[Builder, FragT] 99 | 100 | /** 101 | * Renders an Option of [[FragT]] into a single [[FragT]] 102 | */ 103 | implicit def OptionFrag[A](xs: Option[A])(implicit ev: A => Frag[Builder, FragT]): Frag[Builder, FragT] = SeqFrag(xs.toSeq) 104 | 105 | /** 106 | * Renders an Seq of [[FragT]] into a single [[FragT]] 107 | */ 108 | implicit def ArrayFrag[A](xs: Array[A])(implicit ev: A => Frag[Builder, FragT]): Frag[Builder, FragT] = SeqFrag[A](xs.toSeq) 109 | 110 | /** 111 | * Lets you put Unit into a scalatags tree, as a no-op. 112 | */ 113 | implicit def UnitFrag(u: Unit): Frag[Builder, FragT] 114 | } 115 | -------------------------------------------------------------------------------- /scalatags/src/scalatags/package.scala: -------------------------------------------------------------------------------- 1 | import scala.language.implicitConversions 2 | import scalatags.generic.{Attr, Style} 3 | import language.experimental.macros 4 | /** 5 | * ScalaTags is a small XML/HTML construction library for Scala. See 6 | * [[https://github.com/lihaoyi/scalatags the Github page]] for an introduction 7 | * and documentation. 8 | */ 9 | package object scalatags { 10 | 11 | 12 | private[scalatags] def camelCase(dashedString: String) = { 13 | val first :: rest = dashedString.split("-").toList 14 | 15 | (first :: rest.map(s => s(0).toUpper.toString + s.drop(1))).mkString 16 | } 17 | 18 | implicit object styleOrdering extends Ordering[Style]{ 19 | override def compare(x: Style, y: Style): Int = x.cssName compareTo y.cssName 20 | } 21 | 22 | implicit object attrOrdering extends Ordering[Attr]{ 23 | override def compare(x: Attr, y: Attr): Int = x.name compareTo y.name 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /scalatags/src/scalatags/stylesheet/Core.scala: -------------------------------------------------------------------------------- 1 | package scalatags.stylesheet 2 | 3 | 4 | import scala.collection.immutable.SortedMap 5 | 6 | /** 7 | * A structure representing a set of CSS rules which has not been 8 | * rendered into a [[Cls]]. 9 | * 10 | * e.g. a StyleTree that looks like 11 | * 12 | * .cls1 13 | * .cls2 14 | * :hover 15 | * :hover 16 | * cls2 17 | * 18 | * Flattens out via `stringify` into CSS rules like 19 | * 20 | * .cls1 .cls2:hover 21 | * .cls1:hover .cls2 22 | */ 23 | case class StyleTree(selectors: Seq[String], 24 | styles: SortedMap[String, String], 25 | children: Seq[StyleTree]){ 26 | def stringify(prefix: Seq[String]): String = { 27 | val body = styles.map{case (k, v) => s" $k:$v"}.mkString("\n") 28 | val (first +: rest) = prefix ++ selectors 29 | val all = (first +: rest.map(x => if (x(0) == ':') x else " " + x)).mkString("") 30 | val ours = 31 | if (body == "") "" 32 | else s"$all{\n$body\n}\n" 33 | 34 | (ours +: children.map(_.stringify(prefix ++ selectors))).mkString 35 | } 36 | } 37 | 38 | object StyleTree{ 39 | def build(start: Seq[String], args: Seq[StyleSheetFrag]) = { 40 | args.foldLeft(StyleTree(start, SortedMap.empty, Nil))( 41 | (c, f) => f.applyTo(c) 42 | ) 43 | } 44 | } 45 | 46 | 47 | /** 48 | * Something which can be used as part of a [[StyleSheet]] 49 | */ 50 | trait StyleSheetFrag{ 51 | 52 | def applyTo(c: StyleTree): StyleTree 53 | } 54 | object StyleSheetFrag{ 55 | implicit class StyleTreeFrag(st: StyleTree) extends StyleSheetFrag{ 56 | def applyTo(c: StyleTree) = { 57 | new StyleTree( 58 | c.selectors, 59 | c.styles, 60 | c.children ++ Seq(st) 61 | ) 62 | } 63 | } 64 | 65 | } 66 | 67 | /** 68 | * Provides all the CSS pseudo-selectors as strongly-typed 69 | * properties when mixed in. The only requirement is that you 70 | * define `extend` to tell it what each of these properties 71 | * returns 72 | */ 73 | trait PseudoSelectors[T]{ 74 | 75 | def pseudoExtend(s: String): T 76 | // Pseudo-selectors 77 | // https://developer.mozilla.org/en-US/docs/Web/CSS/Pseudo-classes 78 | def active = pseudoExtend("active") 79 | def checked = pseudoExtend("checked") 80 | def default = pseudoExtend("default") 81 | def disabled = pseudoExtend("disabled") 82 | def empty = pseudoExtend("empty") 83 | def enabled = pseudoExtend("enabled") 84 | def first = pseudoExtend("first") 85 | def firstChild = pseudoExtend("first-child") 86 | def firstOfType = pseudoExtend("first-of-type") 87 | def fullscreen = pseudoExtend("fullscreen") 88 | def focus = pseudoExtend("focus") 89 | def hover = pseudoExtend("hover") 90 | def indeterminate = pseudoExtend("indeterminate") 91 | def inRange = pseudoExtend("in-range") 92 | def invalid = pseudoExtend("invalid") 93 | def lastChild = pseudoExtend("last-child") 94 | def lastOfType = pseudoExtend("last-of-type") 95 | def left = pseudoExtend("left") 96 | def link = pseudoExtend("link") 97 | def onlyChild = pseudoExtend("only-child") 98 | def onlyOfType = pseudoExtend("only-of-type") 99 | def optional = pseudoExtend("optional") 100 | def outOfRange = pseudoExtend("out-of-range") 101 | def readOnly = pseudoExtend("read-only") 102 | def readWrite = pseudoExtend("read-write") 103 | def required = pseudoExtend("required") 104 | def right = pseudoExtend("right") 105 | def root = pseudoExtend("root") 106 | def scope = pseudoExtend("scope") 107 | def target = pseudoExtend("target") 108 | def valid = pseudoExtend("valid") 109 | def visited = pseudoExtend("visited") 110 | } 111 | 112 | 113 | /** 114 | * Lets you chain pseudo-selectors e.g. `hover.visited` and have it properly 115 | * translate into `:hover:visited` when rendered. 116 | */ 117 | class Selector(val built: Seq[String] = Nil) extends PseudoSelectors[Selector]{ b => 118 | def pseudoExtend(s: String) = { 119 | if(b.built == Nil) new Selector(Seq(":"+s)) 120 | else new Selector(b.built.init :+ (b.built.last + ":"+s)) 121 | } 122 | 123 | /** 124 | * Builds this selector into a [[StyleTree]] using the given 125 | * [[StyleSheetFrag]]s. This doesn't create a [[Cls]] on its own, 126 | * but can be used as part of the definition of an outer [[Cls]]. 127 | */ 128 | def apply(args: StyleSheetFrag*) = StyleTree.build(built, args) 129 | 130 | /** 131 | * Combine these two selectors, allowing the right-hand-side 132 | * selector to cascade. 133 | */ 134 | def ~(other: Selector) = new Selector(built ++ other.built) 135 | 136 | /** 137 | * Combine these two selectors using the `>` child selector, 138 | * which prevents cascading. 139 | */ 140 | def >(other: Selector) = new Selector(built ++ Seq(">") ++ other.built) 141 | } 142 | 143 | object Selector{ 144 | def apply(s: String) = new Selector(Seq(s)) 145 | } 146 | /** 147 | * Provides a strongly-typed list of all the HTML tags that can be 148 | * used as [[Selector]]s. 149 | */ 150 | trait StyleSheetTags{ 151 | 152 | // Root Element 153 | protected[this] val html = Selector("html") 154 | // Document Metadata 155 | protected[this] val head = Selector("head") 156 | protected[this] val base = Selector("base") 157 | 158 | protected[this] val link = Selector("link") 159 | protected[this] val meta = Selector("meta") 160 | // Scripting 161 | protected[this] val script = Selector("script") 162 | // Sections 163 | protected[this] val body = Selector("body") 164 | protected[this] val h1 = Selector("h1") 165 | protected[this] val h2 = Selector("h2") 166 | protected[this] val h3 = Selector("h3") 167 | protected[this] val h4 = Selector("h4") 168 | protected[this] val h5 = Selector("h5") 169 | protected[this] val h6 = Selector("h6") 170 | protected[this] val header = Selector("header") 171 | protected[this] val footer = Selector("footer") 172 | // Grouping content 173 | protected[this] val p = Selector("p") 174 | protected[this] val hr = Selector("hr") 175 | protected[this] val pre = Selector("pre") 176 | protected[this] val blockquote = Selector("blockquote") 177 | protected[this] val ol = Selector("ol") 178 | protected[this] val ul = Selector("ul") 179 | protected[this] val li = Selector("li") 180 | protected[this] val dl = Selector("dl") 181 | protected[this] val dt = Selector("dt") 182 | protected[this] val dd = Selector("dd") 183 | protected[this] val figure = Selector("figure") 184 | protected[this] val figcaption = Selector("figcaption") 185 | protected[this] val div = Selector("div") 186 | // Text-level semantics 187 | protected[this] val a = Selector("a") 188 | protected[this] val em = Selector("em") 189 | protected[this] val strong = Selector("strong") 190 | protected[this] val small = Selector("small") 191 | protected[this] val s = Selector("s") 192 | protected[this] val cite = Selector("cite") 193 | protected[this] val code = Selector("code") 194 | protected[this] val sub = Selector("sub") 195 | protected[this] val sup = Selector("sup") 196 | protected[this] val i = Selector("i") 197 | protected[this] val b = Selector("b") 198 | protected[this] val u = Selector("u") 199 | protected[this] val span = Selector("span") 200 | protected[this] val br = Selector("br") 201 | protected[this] val wbr = Selector("wbr") 202 | // Edits 203 | protected[this] val ins = Selector("ins") 204 | protected[this] val del = Selector("del") 205 | // Embedded content 206 | protected[this] val img = Selector("img") 207 | protected[this] val iframe = Selector("iframe") 208 | protected[this] val embed = Selector("embed") 209 | protected[this] val `object` = Selector("object") 210 | protected[this] val param = Selector("param") 211 | protected[this] val video = Selector("video") 212 | protected[this] val audio = Selector("audio") 213 | protected[this] val source = Selector("source") 214 | protected[this] val track = Selector("track") 215 | protected[this] val canvas = Selector("canvas") 216 | protected[this] val map = Selector("map") 217 | protected[this] val area = Selector("area") 218 | // Tabular data 219 | protected[this] val table = Selector("table") 220 | protected[this] val caption = Selector("caption") 221 | protected[this] val colgroup = Selector("colgroup") 222 | protected[this] val col = Selector("col") 223 | protected[this] val tbody = Selector("tbody") 224 | protected[this] val thead = Selector("thead") 225 | protected[this] val tfoot = Selector("tfoot") 226 | protected[this] val tr = Selector("tr") 227 | protected[this] val td = Selector("td") 228 | protected[this] val th = Selector("th") 229 | // Forms 230 | protected[this] val form = Selector("form") 231 | protected[this] val fieldset = Selector("fieldset") 232 | protected[this] val legend = Selector("legend") 233 | protected[this] val label = Selector("label") 234 | protected[this] val input = Selector("input") 235 | protected[this] val button = Selector("button") 236 | protected[this] val select = Selector("select") 237 | protected[this] val datalist = Selector("datalist") 238 | protected[this] val optgroup = Selector("optgroup") 239 | protected[this] val option = Selector("option") 240 | protected[this] val textarea = Selector("textarea") 241 | } 242 | -------------------------------------------------------------------------------- /scalatags/src/scalatags/stylesheet/StyleSheet.scala: -------------------------------------------------------------------------------- 1 | package scalatags.stylesheet 2 | 3 | import scala.collection.immutable.SortedMap 4 | import scala.language.implicitConversions 5 | 6 | /** 7 | * A [[StyleSheet]] which lets you define cascading tag/class 8 | * selectors. Separate from [[StyleSheet]] because you almost 9 | * never need these things, so it's good to make it explicit 10 | * when you do to prevent accidental cascading. 11 | */ 12 | abstract class CascadingStyleSheet(implicit sourceName: sourcecode.FullName) extends StyleSheet with StyleSheetTags{ 13 | protected[this] implicit def clsSelector(c: Cls): Selector = new Selector(Seq("." + c.name)) 14 | } 15 | 16 | /** 17 | * Inherit from me to define a stylesheet which you can use to define 18 | * styles which get serialized to a `String`. Does not allow the use 19 | * of cascading tag/class selectors; use [[CascadingStyleSheet]] for that. 20 | */ 21 | abstract class StyleSheet(implicit sourceName: sourcecode.FullName){ 22 | /** 23 | * The name of this CSS stylesheet. Defaults to the name of the trait, 24 | * but you can override 25 | */ 26 | def customSheetName: Option[String] = None 27 | 28 | /** 29 | * Converts the name of the [[StyleSheet]]'s, the name of the member, and 30 | * any applicable pseudo-selectors into the name of the CSS class. 31 | */ 32 | protected[this] def nameFor(memberName: String, pseudoSelectors: String) = { 33 | customSheetName.getOrElse(defaultSheetName.replace(".", "-")) + "-" + memberName + pseudoSelectors 34 | } 35 | 36 | /** 37 | * Namespace that holds all the css pseudo-selectors, to avoid collisions 38 | * with tags and style-names and other things. 39 | */ 40 | protected[this] val & = new Selector 41 | 42 | /** 43 | * `*` in a CSS selector. 44 | */ 45 | protected[this] object * extends Selector(Seq("*")) 46 | 47 | /** 48 | * Used to define a new, uniquely-named class with a set of 49 | * styles associated with it. 50 | */ 51 | protected[this] object cls extends Creator(Nil) 52 | protected[this] class Creator(selectors: Seq[String]) extends PseudoSelectors[Creator]{ 53 | def pseudoExtend(s: String): Creator = new Creator(selectors :+ s) 54 | 55 | /** 56 | * Collapse the tree of [[StyleSheetFrag]]s into a single [[Cls]], 57 | * side-effect all the output into the styleSheetText, and return the 58 | * [[Cls]] 59 | */ 60 | def apply(args: StyleSheetFrag*)(implicit name: sourcecode.Name): Cls = { 61 | Cls(nameFor(name.value, ""), selectors, args) 62 | } 63 | } 64 | 65 | /** 66 | * The default name of a stylesheet, filled in with the [[StyleSheet]] implicit macro 67 | */ 68 | protected[this] def defaultSheetName = sourceName.value.replace(' ', '.').replace('#', '.') 69 | /** 70 | * All classes defined in this stylesheet, filled in with the [[StyleSheet]] implicit macro 71 | */ 72 | protected[this] def initStyleSheet()(implicit sourceClasses: SourceClasses[this.type]) = 73 | allClasses0 = Some(() => sourceClasses.value(this)) 74 | 75 | private[this] var allClasses0: Option[() => Seq[Cls]] = None 76 | def allClasses = allClasses0 match { 77 | case Some(f) => f() 78 | case None => 79 | 80 | throw new Exception( 81 | "No CSS classes found on stylesheet " + this + 82 | ". Did you forget to call `initStyleSheet()` in the body of the style sheet?" 83 | ) 84 | } 85 | 86 | def styleSheetText = allClasses.map(_.structure.stringify(Nil)).mkString("\n") 87 | } 88 | 89 | /** 90 | * A rendered class; both the class `name` (used when injected into Scalatags 91 | * fragments) and the `structure` (used when injected into further class definitions) 92 | */ 93 | case class Cls(name: String, pseudoSelectors: Seq[String], args: Seq[StyleSheetFrag]){ 94 | lazy val structure = args.foldLeft(StyleTree(Seq(s".$name${pseudoSelectors.map(s => s":$s").mkString}"), SortedMap.empty, Nil))( 95 | (c, f) => f.applyTo(c) 96 | ) 97 | def splice = new StyleSheetFrag{ 98 | def applyTo(st: StyleTree) = { 99 | new StyleTree( 100 | st.selectors, 101 | structure.styles ++ st.styles, 102 | structure.children ++ st.children 103 | ) 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /scalatags/src/scalatags/text/Builder.scala: -------------------------------------------------------------------------------- 1 | package scalatags 2 | package text 3 | import java.io.StringWriter 4 | import scala.reflect.ClassTag 5 | import scalatags.generic.Style 6 | 7 | /** 8 | * Object to aggregate the modifiers into one coherent data structure 9 | * so the final HTML string can be properly generated. It's really 10 | * gross internally, but bloody fast. Even using pre-built data structures 11 | * like `mutable.Buffer` slows down the benchmarks considerably. Also 12 | * exposes more of its internals than it probably should for performance, 13 | * so even though the stuff isn't private, don't touch it! 14 | */ 15 | class Builder(var children: Array[Frag] = new Array(4), 16 | var attrs: Array[(String, Builder.ValueSource)] = new Array(4)){ 17 | final var childIndex = 0 18 | final var attrIndex = 0 19 | 20 | private[this] def incrementChidren(arr: Array[Frag], index: Int) = { 21 | if (index >= arr.length){ 22 | val newArr = new Array[Frag](arr.length * 2) 23 | var i = 0 24 | while(i < arr.length){ 25 | newArr(i) = arr(i) 26 | i += 1 27 | } 28 | newArr 29 | }else{ 30 | null 31 | } 32 | } 33 | 34 | private[this] def incrementAttr(arr: Array[(String, Builder.ValueSource)], index: Int) = { 35 | if (index >= arr.length){ 36 | val newArr = new Array[(String, Builder.ValueSource)](arr.length * 2) 37 | var i = 0 38 | while(i < arr.length){ 39 | newArr(i) = arr(i) 40 | i += 1 41 | } 42 | newArr 43 | }else{ 44 | null 45 | } 46 | } 47 | 48 | private[this] def increment[T: ClassTag](arr: Array[T], index: Int) = { 49 | if (index >= arr.length){ 50 | val newArr = new Array[T](arr.length * 2) 51 | var i = 0 52 | while(i < arr.length){ 53 | newArr(i) = arr(i) 54 | i += 1 55 | } 56 | newArr 57 | }else{ 58 | null 59 | } 60 | } 61 | def addChild(c: Frag) = { 62 | val newChildren = incrementChidren(children, childIndex) 63 | if (newChildren != null) children = newChildren 64 | children(childIndex) = c 65 | childIndex += 1 66 | } 67 | def appendAttr(k: String, v: Builder.ValueSource) = { 68 | 69 | attrIndex(k) match{ 70 | case -1 => 71 | val newAttrs = incrementAttr(attrs, attrIndex) 72 | if (newAttrs!= null) attrs = newAttrs 73 | 74 | attrs(attrIndex) = k -> v 75 | attrIndex += 1 76 | case n => 77 | val (oldK, oldV) = attrs(n) 78 | attrs(n) = (oldK, Builder.ChainedAttributeValueSource(oldV, v)) 79 | } 80 | } 81 | def setAttr(k: String, v: Builder.ValueSource) = { 82 | attrIndex(k) match{ 83 | case -1 => 84 | val newAttrs = incrementAttr(attrs, attrIndex) 85 | if (newAttrs!= null) attrs = newAttrs 86 | attrs(attrIndex) = k -> v 87 | attrIndex += 1 88 | case n => 89 | val (oldK, oldV) = attrs(n) 90 | attrs(n) = (oldK, Builder.ChainedAttributeValueSource(oldV, v)) 91 | } 92 | } 93 | 94 | 95 | def appendAttrStrings(v: Builder.ValueSource, sb: java.io.Writer): Unit = { 96 | v.appendAttrValue(sb) 97 | } 98 | 99 | def attrsString(v: Builder.ValueSource): String = { 100 | val sb = new java.io.StringWriter 101 | appendAttrStrings(v, sb) 102 | sb.toString 103 | } 104 | 105 | 106 | 107 | def attrIndex(k: String): Int = { 108 | attrs.indexWhere(x => x != null && x._1 == k) 109 | } 110 | } 111 | object Builder{ 112 | 113 | /** 114 | * More-or-less internal trait, used to package up the parts of a textual 115 | * attribute or style so that we can append the chunks directly to the 116 | * output buffer. Improves perf over immediately combining them into a 117 | * string and storing that, since this avoids allocating that intermediate 118 | * string. 119 | */ 120 | trait ValueSource { 121 | def appendAttrValue(strb: java.io.Writer): Unit 122 | } 123 | case class StyleValueSource(s: Style, v: String) extends ValueSource { 124 | override def appendAttrValue(strb: java.io.Writer): Unit = { 125 | Escaping.escape(s.cssName, strb) 126 | strb.append(": ") 127 | Escaping.escape(v, strb) 128 | strb.append(";") 129 | } 130 | } 131 | 132 | case class GenericAttrValueSource(v: String) extends ValueSource { 133 | override def appendAttrValue(strb: java.io.Writer): Unit = { 134 | Escaping.escape(v, strb) 135 | } 136 | } 137 | 138 | case class ChainedAttributeValueSource(head: ValueSource, tail: ValueSource) extends ValueSource { 139 | override def appendAttrValue(strb: java.io.Writer): Unit = { 140 | head.appendAttrValue(strb) 141 | strb.append(" ") 142 | tail.appendAttrValue(strb) 143 | } 144 | } 145 | } 146 | 147 | trait Frag extends generic.Frag[Builder, String] { 148 | def writeTo(strb: java.io.Writer): Unit 149 | def writeBytesTo(out: java.io.OutputStream): Unit = { 150 | val w = new java.io.OutputStreamWriter(out, java.nio.charset.StandardCharsets.UTF_8) 151 | writeTo(w) 152 | w.flush() 153 | } 154 | def render: String 155 | def applyTo(b: Builder) = b.addChild(this) 156 | } 157 | 158 | -------------------------------------------------------------------------------- /scalatags/src/scalatags/text/SvgTags.scala: -------------------------------------------------------------------------------- 1 | package scalatags 2 | package text 3 | trait SvgTags extends generic.SvgTags[Builder, String, String] 4 | with TagFactory{ 5 | lazy val altGlyph: ConcreteHtmlTag[String] = tag("altGlyph") 6 | lazy val altGlyphDef: ConcreteHtmlTag[String] = tag("altGlyphDef") 7 | lazy val altGlyphItem: ConcreteHtmlTag[String] = tag("altGlyphItem") 8 | lazy val animate: ConcreteHtmlTag[String] = tag("animate") 9 | lazy val animateMotion: ConcreteHtmlTag[String] = tag("animateMotion") 10 | lazy val animateTransform: ConcreteHtmlTag[String] = tag("animateTransform") 11 | lazy val circle: ConcreteHtmlTag[String] = tag("circle") 12 | lazy val clipPath: ConcreteHtmlTag[String] = tag("clipPath") 13 | lazy val `color-profile`: ConcreteHtmlTag[String] = tag("color-profile") 14 | lazy val cursor: ConcreteHtmlTag[String] = tag("cursor") 15 | lazy val defs: ConcreteHtmlTag[String] = tag("defs") 16 | lazy val desc: ConcreteHtmlTag[String] = tag("desc") 17 | lazy val ellipse: ConcreteHtmlTag[String] = tag("ellipse") 18 | lazy val feBlend: ConcreteHtmlTag[String] = tag("feBlend") 19 | lazy val feColorMatrix: ConcreteHtmlTag[String] = tag("feColorMatrix") 20 | lazy val feComponentTransfer: ConcreteHtmlTag[String] = tag("feComponentTransfer") 21 | lazy val feComposite: ConcreteHtmlTag[String] = tag("feComposite") 22 | lazy val feConvolveMatrix: ConcreteHtmlTag[String] = tag("feConvolveMatrix") 23 | lazy val feDiffuseLighting: ConcreteHtmlTag[String] = tag("feDiffuseLighting") 24 | lazy val feDisplacementMap: ConcreteHtmlTag[String] = tag("feDisplacementMap") 25 | lazy val feDistantLighting: ConcreteHtmlTag[String] = tag("feDistantLighting") 26 | lazy val feFlood: ConcreteHtmlTag[String] = tag("feFlood") 27 | lazy val feFuncA: ConcreteHtmlTag[String] = tag("feFuncA") 28 | lazy val feFuncB: ConcreteHtmlTag[String] = tag("feFuncB") 29 | lazy val feFuncG: ConcreteHtmlTag[String] = tag("feFuncG") 30 | lazy val feFuncR: ConcreteHtmlTag[String] = tag("feFuncR") 31 | lazy val feGaussianBlur: ConcreteHtmlTag[String] = tag("feGaussianBlur") 32 | lazy val feImage: ConcreteHtmlTag[String] = tag("feImage") 33 | lazy val feMerge: ConcreteHtmlTag[String] = tag("feMerge") 34 | lazy val feMergeNode: ConcreteHtmlTag[String] = tag("feMergeNode") 35 | lazy val feMorphology: ConcreteHtmlTag[String] = tag("feMorphology") 36 | lazy val feOffset: ConcreteHtmlTag[String] = tag("feOffset") 37 | lazy val fePointLight: ConcreteHtmlTag[String] = tag("fePointLight") 38 | lazy val feSpecularLighting: ConcreteHtmlTag[String] = tag("feSpecularLighting") 39 | lazy val feSpotlight: ConcreteHtmlTag[String] = tag("feSpotlight") 40 | lazy val feTile: ConcreteHtmlTag[String] = tag("feTile") 41 | lazy val feTurbulance: ConcreteHtmlTag[String] = tag("feTurbulance") 42 | lazy val filter: ConcreteHtmlTag[String] = tag("filter") 43 | lazy val font: ConcreteHtmlTag[String] = tag("font") 44 | lazy val `font-face`: ConcreteHtmlTag[String] = tag("font-face") 45 | lazy val `font-face-format`: ConcreteHtmlTag[String] = tag("font-face-format") 46 | lazy val `font-face-name`: ConcreteHtmlTag[String] = tag("font-face-name") 47 | lazy val `font-face-src`: ConcreteHtmlTag[String] = tag("font-face-src") 48 | lazy val `font-face-uri`: ConcreteHtmlTag[String] = tag("font-face-uri") 49 | lazy val foreignObject: ConcreteHtmlTag[String] = tag("foreignObject") 50 | lazy val g: ConcreteHtmlTag[String] = tag("g") 51 | lazy val glyph: ConcreteHtmlTag[String] = tag("glyph") 52 | lazy val glyphRef: ConcreteHtmlTag[String] = tag("glyphRef") 53 | lazy val hkern: ConcreteHtmlTag[String] = tag("hkern") 54 | lazy val image: ConcreteHtmlTag[String] = tag("image") 55 | lazy val line: ConcreteHtmlTag[String] = tag("line") 56 | lazy val linearGradient: ConcreteHtmlTag[String] = tag("linearGradient") 57 | lazy val marker: ConcreteHtmlTag[String] = tag("marker") 58 | lazy val mask: ConcreteHtmlTag[String] = tag("mask") 59 | lazy val metadata: ConcreteHtmlTag[String] = tag("metadata") 60 | lazy val `missing-glyph`: ConcreteHtmlTag[String] = tag("missing-glyph") 61 | lazy val mpath: ConcreteHtmlTag[String] = tag("mpath") 62 | lazy val path: ConcreteHtmlTag[String] = tag("path") 63 | lazy val pattern: ConcreteHtmlTag[String] = tag("pattern") 64 | lazy val polygon: ConcreteHtmlTag[String] = tag("polygon") 65 | lazy val polyline: ConcreteHtmlTag[String] = tag("polyline") 66 | lazy val radialGradient: ConcreteHtmlTag[String] = tag("radialGradient") 67 | lazy val rect: ConcreteHtmlTag[String] = tag("rect") 68 | lazy val set: ConcreteHtmlTag[String] = tag("set") 69 | lazy val stop: ConcreteHtmlTag[String] = tag("stop") 70 | lazy val svg: ConcreteHtmlTag[String] = tag("svg") 71 | lazy val switch: ConcreteHtmlTag[String] = tag("switch") 72 | lazy val symbol: ConcreteHtmlTag[String] = tag("symbol") 73 | lazy val text: ConcreteHtmlTag[String] = tag("text") 74 | lazy val textPath: ConcreteHtmlTag[String] = tag("textPath") 75 | lazy val tref: ConcreteHtmlTag[String] = tag("tref") 76 | lazy val tspan: ConcreteHtmlTag[String] = tag("tspan") 77 | lazy val use: ConcreteHtmlTag[String] = tag("use") 78 | lazy val view: ConcreteHtmlTag[String] = tag("view") 79 | lazy val vkern: ConcreteHtmlTag[String] = tag("vkern") 80 | } 81 | 82 | -------------------------------------------------------------------------------- /scalatags/src/scalatags/text/TagFactory.scala: -------------------------------------------------------------------------------- 1 | package scalatags.text 2 | 3 | import scalatags.Escaping 4 | import scalatags.generic.Namespace 5 | 6 | /** 7 | * Created by haoyi on 7/9/16. 8 | */ 9 | trait TagFactory extends scalatags.generic.Util[Builder, String, String]{ 10 | 11 | def tag(s: String, void: Boolean = false): ConcreteHtmlTag[String] = { 12 | if (!Escaping.validTag(s)) 13 | throw new IllegalArgumentException( 14 | s"Illegal tag name: $s is not a valid XML tag name" 15 | ) 16 | makeAbstractTypedTag[String](s, void, Namespace.htmlNamespaceConfig) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /scalatags/src/scalatags/text/Tags.scala: -------------------------------------------------------------------------------- 1 | package scalatags 2 | package text 3 | trait Tags extends generic.Tags[Builder, String, String] with TagFactory{ 4 | // Root Element 5 | lazy val html: ConcreteHtmlTag[String] = tag("html") 6 | // Document Metadata 7 | lazy val head: ConcreteHtmlTag[String] = tag("head") 8 | lazy val base: ConcreteHtmlTag[String] = tag("base", void = true) 9 | lazy val link: ConcreteHtmlTag[String] = tag("link", void = true) 10 | lazy val meta: ConcreteHtmlTag[String] = tag("meta", void = true) 11 | // Scripting 12 | lazy val script: ConcreteHtmlTag[String] = tag("script") 13 | // Sections 14 | lazy val body: ConcreteHtmlTag[String] = tag("body") 15 | lazy val h1: ConcreteHtmlTag[String] = tag("h1") 16 | lazy val h2: ConcreteHtmlTag[String] = tag("h2") 17 | lazy val h3: ConcreteHtmlTag[String] = tag("h3") 18 | lazy val h4: ConcreteHtmlTag[String] = tag("h4") 19 | lazy val h5: ConcreteHtmlTag[String] = tag("h5") 20 | lazy val h6: ConcreteHtmlTag[String] = tag("h6") 21 | lazy val header: ConcreteHtmlTag[String] = tag("header") 22 | lazy val footer: ConcreteHtmlTag[String] = tag("footer") 23 | // Grouping content 24 | lazy val p: ConcreteHtmlTag[String] = tag("p") 25 | lazy val hr: ConcreteHtmlTag[String] = tag("hr", void = true) 26 | lazy val pre: ConcreteHtmlTag[String] = tag("pre") 27 | lazy val blockquote: ConcreteHtmlTag[String] = tag("blockquote") 28 | lazy val ol: ConcreteHtmlTag[String] = tag("ol") 29 | lazy val ul: ConcreteHtmlTag[String] = tag("ul") 30 | lazy val li: ConcreteHtmlTag[String] = tag("li") 31 | lazy val dl: ConcreteHtmlTag[String] = tag("dl") 32 | lazy val dt: ConcreteHtmlTag[String] = tag("dt") 33 | lazy val dd: ConcreteHtmlTag[String] = tag("dd") 34 | lazy val figure: ConcreteHtmlTag[String] = tag("figure") 35 | lazy val figcaption: ConcreteHtmlTag[String] = tag("figcaption") 36 | lazy val div: ConcreteHtmlTag[String] = tag("div") 37 | // Text-level semantics 38 | lazy val a: ConcreteHtmlTag[String] = tag("a") 39 | lazy val em: ConcreteHtmlTag[String] = tag("em") 40 | lazy val strong: ConcreteHtmlTag[String] = tag("strong") 41 | lazy val small: ConcreteHtmlTag[String] = tag("small") 42 | lazy val s: ConcreteHtmlTag[String] = tag("s") 43 | lazy val cite: ConcreteHtmlTag[String] = tag("cite") 44 | lazy val code: ConcreteHtmlTag[String] = tag("code") 45 | lazy val sub: ConcreteHtmlTag[String] = tag("sub") 46 | lazy val sup: ConcreteHtmlTag[String] = tag("sup") 47 | lazy val i: ConcreteHtmlTag[String] = tag("i") 48 | lazy val b: ConcreteHtmlTag[String] = tag("b") 49 | lazy val u: ConcreteHtmlTag[String] = tag("u") 50 | lazy val span: ConcreteHtmlTag[String] = tag("span") 51 | lazy val br: ConcreteHtmlTag[String] = tag("br", void = true) 52 | lazy val wbr: ConcreteHtmlTag[String] = tag("wbr", void = true) 53 | // Edits 54 | lazy val ins: ConcreteHtmlTag[String] = tag("ins") 55 | lazy val del: ConcreteHtmlTag[String] = tag("del") 56 | // Embedded content 57 | lazy val img: ConcreteHtmlTag[String] = tag("img", void = true) 58 | lazy val iframe: ConcreteHtmlTag[String] = tag("iframe") 59 | lazy val embed: ConcreteHtmlTag[String] = tag("embed", void = true) 60 | lazy val `object`: ConcreteHtmlTag[String] = tag("object") 61 | lazy val param: ConcreteHtmlTag[String] = tag("param", void = true) 62 | lazy val video: ConcreteHtmlTag[String] = tag("video") 63 | lazy val audio: ConcreteHtmlTag[String] = tag("audio") 64 | lazy val source: ConcreteHtmlTag[String] = tag("source", void = true) 65 | lazy val track: ConcreteHtmlTag[String] = tag("track", void = true) 66 | lazy val canvas: ConcreteHtmlTag[String] = tag("canvas") 67 | lazy val map: ConcreteHtmlTag[String] = tag("map") 68 | lazy val area: ConcreteHtmlTag[String] = tag("area", void = true) 69 | // Tabular data 70 | lazy val table: ConcreteHtmlTag[String] = tag("table") 71 | lazy val caption: ConcreteHtmlTag[String] = tag("caption") 72 | lazy val colgroup: ConcreteHtmlTag[String] = tag("colgroup") 73 | lazy val col: ConcreteHtmlTag[String] = tag("col", void = true) 74 | lazy val tbody: ConcreteHtmlTag[String] = tag("tbody") 75 | lazy val thead: ConcreteHtmlTag[String] = tag("thead") 76 | lazy val tfoot: ConcreteHtmlTag[String] = tag("tfoot") 77 | lazy val tr: ConcreteHtmlTag[String] = tag("tr") 78 | lazy val td: ConcreteHtmlTag[String] = tag("td") 79 | lazy val th: ConcreteHtmlTag[String] = tag("th") 80 | // Forms 81 | lazy val form: ConcreteHtmlTag[String] = tag("form") 82 | lazy val fieldset: ConcreteHtmlTag[String] = tag("fieldset") 83 | lazy val legend: ConcreteHtmlTag[String] = tag("legend") 84 | lazy val label: ConcreteHtmlTag[String] = tag("label") 85 | lazy val input: ConcreteHtmlTag[String] = tag("input", void = true) 86 | lazy val button: ConcreteHtmlTag[String] = tag("button") 87 | lazy val select: ConcreteHtmlTag[String] = tag("select") 88 | lazy val datalist: ConcreteHtmlTag[String] = tag("datalist") 89 | lazy val optgroup: ConcreteHtmlTag[String] = tag("optgroup") 90 | lazy val option: ConcreteHtmlTag[String] = tag("option") 91 | lazy val textarea: ConcreteHtmlTag[String] = tag("textarea") 92 | } 93 | -------------------------------------------------------------------------------- /scalatags/src/scalatags/text/Tags2.scala: -------------------------------------------------------------------------------- 1 | package scalatags 2 | package text 3 | trait Tags2 extends generic.Tags2[Builder, String, String] with TagFactory{ 4 | // Document Metadata 5 | lazy val title: ConcreteHtmlTag[String] = tag("title") 6 | lazy val style: ConcreteHtmlTag[String] = tag("style") 7 | // Scripting 8 | lazy val noscript: ConcreteHtmlTag[String] = tag("noscript") 9 | // Sections 10 | lazy val section: ConcreteHtmlTag[String] = tag("section") 11 | lazy val nav: ConcreteHtmlTag[String] = tag("nav") 12 | lazy val article: ConcreteHtmlTag[String] = tag("article") 13 | lazy val aside: ConcreteHtmlTag[String] = tag("aside") 14 | lazy val address: ConcreteHtmlTag[String] = tag("address") 15 | lazy val main: ConcreteHtmlTag[String] = tag("main") 16 | // Text level semantics 17 | lazy val q: ConcreteHtmlTag[String] = tag("q") 18 | lazy val dfn: ConcreteHtmlTag[String] = tag("dfn") 19 | lazy val abbr: ConcreteHtmlTag[String] = tag("abbr") 20 | lazy val data: ConcreteHtmlTag[String] = tag("data") 21 | lazy val time: ConcreteHtmlTag[String] = tag("time") 22 | lazy val `var`: ConcreteHtmlTag[String] = tag("var") 23 | lazy val samp: ConcreteHtmlTag[String] = tag("samp") 24 | lazy val kbd: ConcreteHtmlTag[String] = tag("kbd") 25 | lazy val math: ConcreteHtmlTag[String] = tag("math") 26 | lazy val mark: ConcreteHtmlTag[String] = tag("mark") 27 | lazy val ruby: ConcreteHtmlTag[String] = tag("ruby") 28 | lazy val rt: ConcreteHtmlTag[String] = tag("rt") 29 | lazy val rp: ConcreteHtmlTag[String] = tag("rp") 30 | lazy val bdi: ConcreteHtmlTag[String] = tag("bdi") 31 | lazy val bdo: ConcreteHtmlTag[String] = tag("bdo") 32 | // Forms 33 | lazy val keygen: ConcreteHtmlTag[String] = tag("keygen", void = true) 34 | lazy val output: ConcreteHtmlTag[String] = tag("output") 35 | lazy val progress: ConcreteHtmlTag[String] = tag("progress") 36 | lazy val meter: ConcreteHtmlTag[String] = tag("meter") 37 | // Interactive elements 38 | lazy val details: ConcreteHtmlTag[String] = tag("details") 39 | lazy val summary: ConcreteHtmlTag[String] = tag("summary") 40 | lazy val command: ConcreteHtmlTag[String] = tag("command", void = true) 41 | lazy val menu: ConcreteHtmlTag[String] = tag("menu") 42 | } 43 | -------------------------------------------------------------------------------- /scalatags/src/scalatags/vdom/Builder.scala: -------------------------------------------------------------------------------- 1 | package scalatags 2 | package vdom 3 | trait Builder[Output, FragT]{ 4 | def appendChild(child: FragT): Unit 5 | def appendClass(cls: String): Unit 6 | def appendStyle(cssName: String, value: String): Unit 7 | def setAttr(name: String, value: String): Unit 8 | def render(): Output 9 | } 10 | 11 | trait Frag[Output, FragT] extends scalatags.generic.Frag[Builder[Output, FragT], FragT]{ 12 | def render: FragT 13 | def applyTo(b: Builder[Output, FragT]) = b.appendChild(this.render) 14 | } -------------------------------------------------------------------------------- /scalatags/src/scalatags/vdom/SvgTags.scala: -------------------------------------------------------------------------------- 1 | package scalatags 2 | package vdom 3 | 4 | import scalatags.generic.{Namespace, Util} 5 | 6 | trait SvgTags[Output <: FragT, FragT] 7 | extends generic.SvgTags[Builder[Output, FragT], Output, FragT] with TagFactory[Output, FragT]{ 8 | implicit lazy val svgNamespaceConfig: Namespace = Namespace.svgNamespaceConfig 9 | lazy val altGlyph = tag("altGlyph") 10 | lazy val altGlyphDef = tag("altGlyphDef") 11 | lazy val altGlyphItem = tag("altGlyphItem") 12 | lazy val animate = tag("animate") 13 | lazy val animateMotion = tag("animateMotion") 14 | lazy val animateTransform = tag("animateTransform") 15 | lazy val circle = tag("circle") 16 | lazy val clipPath = tag("clipPath") 17 | lazy val `color-profile` = tag("color-profile") 18 | lazy val cursor = tag("cursor") 19 | lazy val defs = tag("defs") 20 | lazy val desc = tag("desc") 21 | lazy val ellipse = tag("ellipse") 22 | lazy val feBlend = tag("feBlend") 23 | lazy val feColorMatrix = tag("feColorMatrix") 24 | lazy val feComponentTransfer = tag("feComponentTransfer") 25 | lazy val feComposite = tag("feComposite") 26 | lazy val feConvolveMatrix = tag("feConvolveMatrix") 27 | lazy val feDiffuseLighting = tag("feDiffuseLighting") 28 | lazy val feDisplacementMap = tag("feDisplacementMap") 29 | lazy val feDistantLighting = tag("feDistantLighting") 30 | lazy val feFlood = tag("feFlood") 31 | lazy val feFuncA = tag("feFuncA") 32 | lazy val feFuncB = tag("feFuncB") 33 | lazy val feFuncG = tag("feFuncG") 34 | lazy val feFuncR = tag("feFuncR") 35 | lazy val feGaussianBlur = tag("feGaussianBlur") 36 | lazy val feImage = tag("feImage") 37 | lazy val feMerge = tag("feMerge") 38 | lazy val feMergeNode = tag("feMergeNode") 39 | lazy val feMorphology = tag("feMorphology") 40 | lazy val feOffset = tag("feOffset") 41 | lazy val fePointLight = tag("fePointLight") 42 | lazy val feSpecularLighting = tag("feSpecularLighting") 43 | lazy val feSpotlight = tag("feSpotlight") 44 | lazy val feTile = tag("feTile") 45 | lazy val feTurbulance = tag("feTurbulance") 46 | lazy val filter = tag("filter") 47 | lazy val font = tag("font") 48 | lazy val `font-face` = tag("font-face") 49 | lazy val `font-face-format` = tag("font-face-format") 50 | lazy val `font-face-name` = tag("font-face-name") 51 | lazy val `font-face-src` = tag("font-face-src") 52 | lazy val `font-face-uri` = tag("font-face-uri") 53 | lazy val foreignObject = tag("foreignObject") 54 | lazy val g = tag("g") 55 | lazy val glyph = tag("glyph") 56 | lazy val glyphRef = tag("glyphRef") 57 | lazy val hkern = tag("hkern") 58 | lazy val image = tag("image") 59 | lazy val line = tag("line") 60 | lazy val linearGradient = tag("linearGradient") 61 | lazy val marker = tag("marker") 62 | lazy val mask = tag("mask") 63 | lazy val metadata = tag("metadata") 64 | lazy val `missing-glyph` = tag("missing-glyph") 65 | lazy val mpath = tag("mpath") 66 | lazy val path = tag("path") 67 | lazy val pattern = tag("pattern") 68 | lazy val polygon = tag("polygon") 69 | lazy val polyline = tag("polyline") 70 | lazy val radialGradient = tag("radialGradient") 71 | lazy val rect = tag("rect") 72 | lazy val set = tag("set") 73 | lazy val stop = tag("stop") 74 | lazy val svg = tag("svg") 75 | lazy val switch = tag("switch") 76 | lazy val symbol = tag("symbol") 77 | lazy val text = tag("text") 78 | lazy val textPath = tag("textPath") 79 | lazy val tref = tag("tref") 80 | lazy val tspan = tag("tspan") 81 | lazy val use = tag("use") 82 | lazy val view = tag("view") 83 | lazy val vkern = tag("vkern") 84 | } 85 | -------------------------------------------------------------------------------- /scalatags/src/scalatags/vdom/TagFactory.scala: -------------------------------------------------------------------------------- 1 | package scalatags 2 | package vdom 3 | 4 | import scalatags.Escaping 5 | import scalatags.generic.Namespace 6 | 7 | 8 | /** 9 | * Created by haoyi on 7/9/16. 10 | */ 11 | trait TagFactory[Output <: FragT, FragT] extends scalatags.generic.Util[Builder[Output, FragT], Output, FragT]{ 12 | def tag(s: String, void: Boolean = false): ConcreteHtmlTag[Output] = { 13 | if (!Escaping.validTag(s)) 14 | throw new IllegalArgumentException( 15 | s"Illegal tag name: $s is not a valid XML tag name" 16 | ) 17 | makeAbstractTypedTag[Output](s, void, scalatags.generic.Namespace.htmlNamespaceConfig) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /scalatags/src/scalatags/vdom/Tags.scala: -------------------------------------------------------------------------------- 1 | 2 | package scalatags 3 | package vdom 4 | 5 | import scalatags.generic.Util 6 | 7 | trait Tags[Output <: FragT, FragT] 8 | extends generic.Tags[Builder[Output, FragT], Output, FragT] with TagFactory[Output, FragT]{ 9 | // Root Element 10 | lazy val html = tag("html") 11 | // Document Metadata 12 | lazy val head = tag("head") 13 | lazy val base = tag("base", void = true) 14 | lazy val link = tag("link", void = true) 15 | lazy val meta = tag("meta", void = true) 16 | // Scripting 17 | lazy val script = tag("script") 18 | // Sections 19 | lazy val body = tag("body") 20 | lazy val h1 = tag("h1") 21 | lazy val h2 = tag("h2") 22 | lazy val h3 = tag("h3") 23 | lazy val h4 = tag("h4") 24 | lazy val h5 = tag("h5") 25 | lazy val h6 = tag("h6") 26 | lazy val header = tag("header") 27 | lazy val footer = tag("footer") 28 | // Grouping content 29 | lazy val p = tag("p") 30 | lazy val hr = tag("hr", void = true) 31 | lazy val pre = tag("pre") 32 | lazy val blockquote = tag("blockquote") 33 | lazy val ol = tag("ol") 34 | lazy val ul = tag("ul") 35 | lazy val li = tag("li") 36 | lazy val dl = tag("dl") 37 | lazy val dt = tag("dt") 38 | lazy val dd = tag("dd") 39 | lazy val figure = tag("figure") 40 | lazy val figcaption = tag("figcaption") 41 | lazy val div = tag("div") 42 | // Text-level semantics 43 | lazy val a = tag("a") 44 | lazy val em = tag("em") 45 | lazy val strong = tag("strong") 46 | lazy val small = tag("small") 47 | lazy val s = tag("s") 48 | lazy val cite = tag("cite") 49 | lazy val code = tag("code") 50 | lazy val sub = tag("sub") 51 | lazy val sup = tag("sup") 52 | lazy val i = tag("i") 53 | lazy val b = tag("b") 54 | lazy val u = tag("u") 55 | lazy val span = tag("span") 56 | lazy val br = tag("br", void = true) 57 | lazy val wbr = tag("wbr", void = true) 58 | // Edits 59 | lazy val ins = tag("ins") 60 | lazy val del = tag("del") 61 | // Embedded content 62 | lazy val img = tag("img", void = true) 63 | lazy val iframe = tag("iframe") 64 | lazy val embed = tag("embed", void = true) 65 | lazy val `object` = tag("object") 66 | lazy val param = tag("param", void = true) 67 | lazy val video = tag("video") 68 | lazy val audio = tag("audio") 69 | lazy val source = tag("source", void = true) 70 | lazy val track = tag("track", void = true) 71 | lazy val canvas = tag("canvas") 72 | lazy val map = tag("map") 73 | lazy val area = tag("area", void = true) 74 | // Tabular data 75 | lazy val table = tag("table") 76 | lazy val caption = tag("caption") 77 | lazy val colgroup = tag("colgroup") 78 | lazy val col = tag("col", void = true) 79 | lazy val tbody = tag("tbody") 80 | lazy val thead = tag("thead") 81 | lazy val tfoot = tag("tfoot") 82 | lazy val tr = tag("tr") 83 | lazy val td = tag("td") 84 | lazy val th = tag("th") 85 | // Forms 86 | lazy val form = tag("form") 87 | lazy val fieldset = tag("fieldset") 88 | lazy val legend = tag("legend") 89 | lazy val label = tag("label") 90 | lazy val input = tag("input", void = true) 91 | lazy val button = tag("button") 92 | lazy val select = tag("select") 93 | lazy val datalist = tag("datalist") 94 | lazy val optgroup = tag("optgroup") 95 | lazy val option = tag("option") 96 | lazy val textarea = tag("textarea") 97 | } 98 | -------------------------------------------------------------------------------- /scalatags/src/scalatags/vdom/Tags2.scala: -------------------------------------------------------------------------------- 1 | package scalatags 2 | package vdom 3 | import scalatags.generic.Util 4 | 5 | trait Tags2[Output <: FragT, FragT] 6 | extends generic.Tags2[Builder[Output, FragT], Output, FragT] with TagFactory[Output, FragT]{ 7 | // Document Metadata 8 | lazy val title = tag("title") 9 | lazy val style = tag("style") 10 | // Scripting 11 | lazy val noscript = tag("noscript") 12 | // Sections 13 | lazy val section = tag("section") 14 | lazy val nav = tag("nav") 15 | lazy val article = tag("article") 16 | lazy val aside = tag("aside") 17 | lazy val address = tag("address") 18 | lazy val main = tag("main") 19 | // Text level semantics 20 | lazy val q = tag("q") 21 | lazy val dfn = tag("dfn") 22 | lazy val abbr = tag("abbr") 23 | lazy val data = tag("data") 24 | lazy val time = tag("time") 25 | lazy val `var` = tag("var") 26 | lazy val samp = tag("samp") 27 | lazy val kbd = tag("kbd") 28 | lazy val math = tag("math") 29 | lazy val mark = tag("mark") 30 | lazy val ruby = tag("ruby") 31 | lazy val rt = tag("rt") 32 | lazy val rp = tag("rp") 33 | lazy val bdi = tag("bdi") 34 | lazy val bdo = tag("bdo") 35 | // Forms 36 | lazy val keygen = tag("keygen", void = true) 37 | lazy val output = tag("output") 38 | lazy val progress = tag("progress") 39 | lazy val meter = tag("meter") 40 | // Interactive elements 41 | lazy val details = tag("details") 42 | lazy val summary = tag("summary") 43 | lazy val command = tag("command", void = true) 44 | lazy val menu = tag("menu") 45 | } 46 | -------------------------------------------------------------------------------- /scalatags/test/resource/page.jade: -------------------------------------------------------------------------------- 1 | -@ val titleString: String 2 | -@ val contentpara: String 3 | -@ val first: String 4 | -@ val firstParaString: String 5 | -@ val paras: Seq[(Int, String)] 6 | 7 | html 8 | head 9 | script console.log(1) 10 | 11 | body 12 | h1(style="color: red;") 13 | = titleString 14 | div(style="background-color: blue;") 15 | p(class="#{contentpara} #{first}" title="this is paragraph 0") 16 | = firstParaString 17 | 18 | a(href="www.google.com") 19 | p Goooogle 20 | 21 | - paras.foreach{ case (count, color) => 22 | - render("jvm/src/test/resource/para.jade", Map("count" -> count, "color" -> color)) 23 | - } 24 | 25 | 26 | -------------------------------------------------------------------------------- /scalatags/test/resource/page.mustache: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |

{{titleString}}

7 |
8 |

{{firstParaString}}

9 | 10 |

Goooogle

11 |
12 | {{#paras}} 13 | {{> jvm/src/test/resource/para}} 14 | {{/paras}} 15 |
16 | 17 | -------------------------------------------------------------------------------- /scalatags/test/resource/para.jade: -------------------------------------------------------------------------------- 1 | -@ val contentpara: String 2 | -@ val color: String 3 | -@ val count: Int 4 | 5 | p(class="#{contentpara}" style="color: #{color};" title="this is paragraph #{count}") Paragraph #{count} -------------------------------------------------------------------------------- /scalatags/test/resource/para.mustache: -------------------------------------------------------------------------------- 1 |

Paragraph {{count}}

-------------------------------------------------------------------------------- /scalatags/test/src-js/scalatags/jsdom/BasicTests.scala: -------------------------------------------------------------------------------- 1 | package scalatags 2 | package jsdom 3 | object BasicTests extends generic.BasicTests(scalatags.JsDom) 4 | -------------------------------------------------------------------------------- /scalatags/test/src-js/scalatags/jsdom/DomTests.scala: -------------------------------------------------------------------------------- 1 | package scalatags 2 | package jsdom 3 | import utest._ 4 | 5 | 6 | 7 | import org.scalajs.dom 8 | import org.scalajs.dom.html.Paragraph 9 | 10 | object DomTests extends TestSuite{ 11 | class SharedTemplates[Builder, Output <: FragT, FragT](val bundle: scalatags.generic.Bundle[Builder, Output, FragT]){ 12 | import bundle.all._ 13 | val widget: Tag = div("hello") 14 | } 15 | def tests = TestSuite{ 16 | test("basic"){ 17 | import scalatags.JsDom.all._ 18 | test("children"){ 19 | val elem = div.render 20 | assert(elem.children.length == 0) 21 | elem.appendChild(p(1, "wtf", "bbq").render) 22 | assert(elem.children.length == 1) 23 | val pElem = elem.children(0).asInstanceOf[Paragraph] 24 | assert(pElem.childNodes.length == 3) 25 | assert(pElem.textContent == "1wtfbbq") 26 | } 27 | 28 | test("attributes"){ 29 | val url = "https://www.google.com/" 30 | val elem = a( 31 | href := url, 32 | "Google" 33 | ).render 34 | 35 | assert(elem.href == url) 36 | assert(elem.children.length == 0) 37 | assert(elem.childNodes.length == 1) 38 | val textNode = elem.childNodes(0).asInstanceOf[dom.Text] 39 | assert(textNode.textContent == "Google") 40 | } 41 | 42 | test("styles"){ 43 | val elem = div( 44 | color := "red", 45 | float.left, 46 | backgroundColor := "yellow" 47 | ).render 48 | assert(elem.style.color == "red") 49 | assert(elem.style.cssFloat == "left") 50 | assert(elem.style.backgroundColor == "yellow") 51 | // styles end up being sorted in alphabetical order 52 | val styleAttr = elem.getAttribute("style") 53 | assert( 54 | styleAttr.trim == "color: red; float: left; background-color: yellow;" 55 | ) 56 | } 57 | } 58 | test("fancy"){ 59 | import scalatags.JsDom.all._ 60 | test("fragSeqsAreFrags"){ 61 | val rendered = Seq( 62 | h1("titless"), 63 | div("lol") 64 | ).render 65 | 66 | val wrapped = div(rendered).toString 67 | assert(wrapped == "

titless

lol
") 68 | } 69 | test("boundAttributes"){ 70 | var count = 0 71 | val elem = div( 72 | onclick := { () => count += 1}, 73 | tabindex := 1 74 | ).render 75 | 76 | assert(count == 0) 77 | elem.onclick(null) 78 | assert(count == 1) 79 | } 80 | test("triggers"){ 81 | val labelElem = label("Default").render 82 | 83 | val inputElem = input( 84 | `type`:="text", 85 | onfocus := { () => labelElem.textContent = ""} 86 | ).render 87 | 88 | val box = div( 89 | inputElem, 90 | labelElem 91 | ).render 92 | 93 | assert(labelElem.textContent == "Default") 94 | inputElem.onfocus(null) 95 | assert(labelElem.textContent == "") 96 | 97 | } 98 | } 99 | test("tagType"){ 100 | import scalatags.JsDom.all._ 101 | val thing: Tag = div 102 | } 103 | 104 | test("crossTag"){ 105 | object JsTemplates extends SharedTemplates(scalatags.JsDom) 106 | object TextTemplates extends SharedTemplates(scalatags.Text) 107 | 108 | val jsVersion: dom.Element = JsTemplates.widget.render 109 | val txtVersion: String = TextTemplates.widget.render 110 | 111 | assert( 112 | jsVersion.tagName.toLowerCase == "div", 113 | jsVersion.textContent == "hello", 114 | txtVersion == "
hello
" 115 | ) 116 | } 117 | } 118 | 119 | } 120 | -------------------------------------------------------------------------------- /scalatags/test/src-js/scalatags/jsdom/ExampleTests.scala: -------------------------------------------------------------------------------- 1 | package scalatags 2 | package jsdom 3 | 4 | 5 | object ExampleTests extends generic.ExampleTests(JsDom) 6 | 7 | -------------------------------------------------------------------------------- /scalatags/test/src-js/scalatags/jsdom/PerfTests.scala: -------------------------------------------------------------------------------- 1 | package scalatags 2 | package jsdom 3 | 4 | case object ScalatagsPerf extends generic.ScalatagsPerf(JsDom) 5 | -------------------------------------------------------------------------------- /scalatags/test/src-js/scalatags/jsdom/StyleSheetTests.scala: -------------------------------------------------------------------------------- 1 | package scalatags 2 | package jsdom 3 | import scalatags.JsDom.all._ 4 | import utest._ 5 | 6 | object StyleSheetTests 7 | extends generic.StyleSheetTests(scalatags.JsDom) 8 | 9 | -------------------------------------------------------------------------------- /scalatags/test/src-jvm/scalatags/generic/ExampleTestsJvmOnly.scala: -------------------------------------------------------------------------------- 1 | package scalatags 2 | package generic 3 | import utest._ 4 | 5 | import TestUtil.strCheck 6 | 7 | class ExampleTestsJvmOnly[Builder, Output <: FragT, FragT](bundle: Bundle[Builder, Output, FragT]) extends TestSuite{ 8 | val tests = Tests { 9 | import bundle.all._ 10 | test("properEscaping") - strCheck({ 11 | val evilInput1 = "\">" 12 | val evilInput2 = "" 13 | 14 | html( 15 | head( 16 | script("some script") 17 | ), 18 | body( 19 | h1( 20 | title:=evilInput1, 21 | "This is my title" 22 | ), 23 | evilInput2 24 | ) 25 | ) 26 | 27 | } 28 | , 29 | """ 30 | 31 | 32 | 33 | 34 | 35 |

36 | This is my title 37 |

38 | <script>alert('hello!')</script> 39 | 40 | 41 | """ 42 | ) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /scalatags/test/src-jvm/scalatags/text/PerfTests.scala: -------------------------------------------------------------------------------- 1 | package scalatags 2 | package text 3 | /*import org.fusesource.scalate.{DefaultRenderContext, RenderContext, TemplateSource, TemplateEngine} 4 | import java.io.{StringWriter, PrintWriter} 5 | 6 | case object JadePerf extends generic.PerfTest{ 7 | val engine = new TemplateEngine 8 | 9 | val template = engine.load(TemplateSource.fromFile("jvm/src/test/resource/page.jade")) 10 | 11 | def calc() = { 12 | val stringWriter = new StringWriter() 13 | val ctx = new DefaultRenderContext("", engine, new PrintWriter(stringWriter)) 14 | ctx.attributes("titleString") = generic.PerfTest.titleString 15 | ctx.attributes("firstParaString") = generic.PerfTest.firstParaString 16 | ctx.attributes("contentpara") = "contentpara" 17 | ctx.attributes("first") = "first" 18 | ctx.attributes("paras") = (0 until 5).map(i => 19 | (i, if (i % 2 == 0) "red" else "green") 20 | ) 21 | template.render(ctx) 22 | stringWriter.toString 23 | } 24 | } 25 | case object MustachePerf extends generic.PerfTest{ 26 | val engine = new TemplateEngine 27 | 28 | val template = engine.load(TemplateSource.fromFile("jvm/src/test/resource/page.mustache")) 29 | 30 | def calc() = { 31 | val stringWriter = new StringWriter() 32 | val ctx = new DefaultRenderContext("", engine, new PrintWriter(stringWriter)) 33 | ctx.attributes("titleString") = generic.PerfTest.titleString 34 | ctx.attributes("firstParaString") = generic.PerfTest.firstParaString 35 | ctx.attributes("contentpara") = "contentpara" 36 | ctx.attributes("first") = "first" 37 | ctx.attributes("paras") = (0 until 5).map(i => Map( 38 | "count" -> i, 39 | "color" -> (if (i % 2 == 0) "red" else "green") 40 | )) 41 | template.render(ctx) 42 | stringWriter.toString 43 | } 44 | }*/ 45 | 46 | //case object Twirl extends (() => String){ 47 | // def apply() = { 48 | // html.page( 49 | // "contentpara", 50 | // "first", 51 | // Scalatags.titleString, 52 | // Scalatags.firstParaString, 53 | // (0 until 5).map(i => 54 | // (i, if (i % 2 == 0) "red" else "green") 55 | // ) 56 | // ).toString 57 | // } 58 | //} 59 | -------------------------------------------------------------------------------- /scalatags/test/src/scalatags/Main.scala: -------------------------------------------------------------------------------- 1 | package scalatags 2 | 3 | 4 | 5 | object Main { 6 | def main(args: Array[String]): Unit = { 7 | 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /scalatags/test/src/scalatags/TestUtil.scala: -------------------------------------------------------------------------------- 1 | package scalatags 2 | object TestUtil { 3 | def clean(s: String) = { 4 | " +<".r.replaceAllIn("\\n\\s*".r.replaceAllIn(s, "").trim(), "<").replace(" ", "").replace("\"/>", "\">").sorted 5 | } 6 | def strCheck(x: Any*) = { 7 | for (Seq(a, b) <- x.grouped(2)) { 8 | 9 | try 10 | assert( 11 | clean(a.toString()) == clean(b.toString()) 12 | ) 13 | catch {case e: AssertionError => 14 | println(a) 15 | println(b) 16 | println(clean(a.toString())) 17 | println(clean(b.toString())) 18 | throw e 19 | } 20 | } 21 | } 22 | } 23 | 24 | -------------------------------------------------------------------------------- /scalatags/test/src/scalatags/generic/BasicTests.scala: -------------------------------------------------------------------------------- 1 | package scalatags 2 | package generic 3 | 4 | import utest._ 5 | 6 | import TestUtil._ 7 | 8 | 9 | class BasicTests[Builder, Output <: FragT, FragT](omg: Bundle[Builder, Output, FragT]) extends TestSuite{ 10 | import omg.all._ 11 | private[this] type Omg = Attrs 12 | val tests = TestSuite{ 13 | 14 | /** 15 | * Tests nesting tags in a simple hierarchy 16 | */ 17 | test("cssChaining"){ 18 | val x = script("") 19 | strCheck( 20 | html( 21 | head( 22 | x, 23 | tag("string-tag") 24 | ), 25 | body( 26 | div( 27 | p 28 | ) 29 | ) 30 | ), 31 | """ 32 | 33 | 34 | 35 | 36 | 37 | 38 |
39 |

40 |
41 | 42 | 43 | """ 44 | ) 45 | } 46 | test("cssChaining2") - strCheck( 47 | div( 48 | float.left, 49 | color:="red" 50 | ), 51 | """
""" 52 | ) 53 | 54 | 55 | test("attributeChaining") - strCheck( 56 | div( 57 | `class`:="thing lol", 58 | id:="cow" 59 | ), 60 | """
""" 61 | ) 62 | 63 | 64 | test("mixingAttributesStylesAndChildren") - strCheck( 65 | div( 66 | id:="cow", 67 | float.left, 68 | p("i am a cow") 69 | ), 70 | """

i am a cow

""" 71 | ) 72 | 73 | test("classStyleAttrOverwriting"){ 74 | strCheck( 75 | //class/style after attr appends, but attr after class/style overwrites 76 | div( 77 | cls:="my-class", 78 | style:="background-color: red;", 79 | float.left, 80 | p("i am a cow") 81 | ), 82 | """

i am a cow

""" 83 | ) 84 | } 85 | 86 | test("intSeq") - strCheck( 87 | div( 88 | h1("Hello"), 89 | for(i <- 0 until 5) yield i 90 | ), 91 | """

Hello

01234
""" 92 | ) 93 | 94 | test("stringArray"){ 95 | val strArr = Array("hello") 96 | strCheck( 97 | div( 98 | Some("lol"), 99 | Some(1), 100 | None: Option[String], 101 | h1("Hello"), 102 | Array(1, 2, 3), 103 | strArr, 104 | () 105 | ), 106 | """
lol1

Hello

123hello
""" 107 | ) 108 | } 109 | test("generator"){ 110 | val strArr = Array("hello") 111 | strCheck( 112 | div( 113 | geny.Generator[String]( 114 | "Hello", 115 | "World", 116 | "Moo" 117 | ) 118 | ), 119 | """
HelloWorldMoo
""" 120 | ) 121 | } 122 | test("applyChaining"){ 123 | strCheck( 124 | a( 125 | tabindex := 1, 126 | onclick := "lol" 127 | )( 128 | href := "boo", 129 | alt := "g" 130 | ), 131 | """""" 132 | ) 133 | } 134 | test("autoPixel"){ 135 | strCheck( 136 | div(width:=100, zIndex:=100, height:=100), 137 | """ 138 | |
139 | """.stripMargin 140 | ) 141 | } 142 | test("compileErrors"){ 143 | test("niceErrorsForAttributes"){ 144 | val msg = compileError("""a(onclick := {() => "lol"})""").msg 145 | assert(msg.contains(s"scalatags does not know how to use () => String as an attribute")) 146 | } 147 | test("niceErrorsForStyles"){ 148 | val msg = compileError("""a(opacity:= {() => "lol"})""").msg 149 | assert(msg.contains(s"scalatags does not know how to use () => String as an style")) 150 | } 151 | } 152 | test("nulls"){ 153 | val nullString: String = null 154 | test("1") { intercept[NullPointerException](div(nullString)) } 155 | test("2") { intercept[NullPointerException](div(null: Seq[Int])) } 156 | test("3") { intercept[NullPointerException](div(height := nullString)) } 157 | test("4") { intercept[NullPointerException](div(opacity := nullString)) } 158 | } 159 | test("rawAttrs"){ 160 | strCheck( 161 | button( 162 | attr("[class.active]", raw = true):= "isActive", 163 | attr("(click)", raw = true) := "myEvent()" 164 | ), 165 | """""" 166 | 167 | ) 168 | } 169 | test("specialChars"){ 170 | * - intercept[java.lang.IllegalArgumentException](div(attr("[(ngModel)]") := "myModel")) 171 | } 172 | test("repeatingAttrs"){ 173 | // #139 174 | object Foo extends scalatags.stylesheet.StyleSheet{ 175 | val myCls = cls(color := "red") 176 | } 177 | strCheck( 178 | div(Foo.myCls, cls := "red").render == 179 | """
""" 180 | ) 181 | strCheck( 182 | div(cls := "red", Foo.myCls).render == 183 | """
""" 184 | ) 185 | // #169 186 | strCheck( 187 | input(cls := "a", cls := "b").render == 188 | """""" 189 | ) 190 | strCheck( 191 | input(cls := "a")(cls := "b").render == 192 | """""" 193 | ) 194 | } 195 | 196 | } 197 | } 198 | 199 | -------------------------------------------------------------------------------- /scalatags/test/src/scalatags/generic/ExampleTests.scala: -------------------------------------------------------------------------------- 1 | package scalatags 2 | package generic 3 | import utest._ 4 | 5 | import TestUtil.strCheck 6 | class ExampleTests[Builder, Output <: FragT, FragT](bundle: Bundle[Builder, Output, FragT]) extends TestSuite{ 7 | 8 | 9 | val tests = TestSuite{ 10 | test("manualImports") - strCheck({ 11 | // bundle is scalatags.Text or scalatags.JsDom 12 | import bundle.short._ 13 | div( 14 | p(*.color:="red", *.fontSize:=64.pt)("Big Red Text"), 15 | img(*.href:="www.imgur.com/picture.jpg") 16 | ) 17 | } 18 | , 19 | { 20 | // bundle is scalatags.Text or scalatags.JsDom 21 | import bundle.{attrs => attr, styles => css, _} 22 | import bundle.tags._ 23 | import bundle.implicits._ 24 | div( 25 | p(css.color:="red", css.fontSize:=64.pt)("Big Red Text"), 26 | img(attr.href:="www.imgur.com/picture.jpg") 27 | ) 28 | } 29 | , 30 | """ 31 |
32 |

Red Text

33 | 34 |
35 | """ 36 | ) 37 | 38 | // bundle is scalatags.Text or scalatags.JsDom 39 | import bundle.all._ 40 | test("splashExample") - strCheck( 41 | // import scalatags.Text.all._ 42 | // OR 43 | // import scalatags.JsDom.all._ 44 | html( 45 | head( 46 | script(src:="..."), 47 | script( 48 | "alert('Hello World')" 49 | ) 50 | ), 51 | body( 52 | div( 53 | h1(id:="title", "This is a title"), 54 | p("This is a big paragraph of text") 55 | ) 56 | ) 57 | ) 58 | , 59 | """ 60 | 61 | 62 | 63 | 64 | 65 | 66 |
67 |

This is a title

68 |

This is a big paragraph of text

69 |
70 | 71 | 72 | """ 73 | ) 74 | test("helloWorld") - strCheck( 75 | html( 76 | head( 77 | script("some script") 78 | ), 79 | body( 80 | h1("This is my title"), 81 | div( 82 | p("This is my first paragraph"), 83 | p("This is my second paragraph") 84 | ) 85 | ) 86 | ) 87 | , 88 | """ 89 | 90 | 91 | 92 | 93 | 94 |

This is my title

95 |
96 |

This is my first paragraph

97 |

This is my second paragraph

98 |
99 | 100 | 101 | """ 102 | ) 103 | test("variables") - strCheck( 104 | { 105 | val title = "title" 106 | val numVisitors = 1023 107 | 108 | html( 109 | head( 110 | script("some script") 111 | ), 112 | body( 113 | h1("This is my ", title), 114 | div( 115 | p("This is my first paragraph"), 116 | p("you are the ", numVisitors.toString, "th visitor!") 117 | ) 118 | ) 119 | ) 120 | } 121 | , 122 | """ 123 | 124 | 125 | 126 | 127 | 128 |

This is my title

129 |
130 |

This is my first paragraph

131 |

you are the 1023th visitor!

132 |
133 | 134 | 135 | """ 136 | ) 137 | test("controlFlow") - strCheck( 138 | { 139 | val numVisitors = 1023 140 | val posts = Seq( 141 | ("alice", "i like pie"), 142 | ("bob", "pie is evil i hate you"), 143 | ("charlie", "i like pie and pie is evil, i hat myself") 144 | ) 145 | 146 | html( 147 | head( 148 | script("some script") 149 | ), 150 | body( 151 | h1("This is my title"), 152 | div("posts"), 153 | for ((name, text) <- posts) yield div( 154 | h2("Post by ", name), 155 | p(text) 156 | ), 157 | if(numVisitors > 100) p("No more posts!") 158 | else p("Please post below...") 159 | ) 160 | ) 161 | } 162 | , 163 | """ 164 | 165 | 166 | 167 | 168 | 169 |

This is my title

170 |
posts
171 |
172 |

Post by alice

173 |

i like pie

174 |
175 |
176 |

Post by bob

177 |

pie is evil i hate you

178 |
179 |
180 |

Post by charlie

181 |

i like pie and pie is evil, i hat myself

182 |
183 |

No more posts!

184 | 185 | 186 | """ 187 | ) 188 | test("functions") - strCheck( 189 | { 190 | def imgBox(source: String, text: String) = div( 191 | img(src:=source), 192 | div( 193 | p(text) 194 | ) 195 | ) 196 | 197 | html( 198 | head( 199 | script("some script") 200 | ), 201 | body( 202 | h1("This is my title"), 203 | imgBox("www.mysite.com/imageOne.png", "This is the first image displayed on the site"), 204 | div(`class`:="content")( 205 | p("blah blah blah i am text"), 206 | imgBox("www.mysite.com/imageTwo.png", "This image is very interesting") 207 | ) 208 | ) 209 | ) 210 | } 211 | , 212 | """ 213 | 214 | 215 | 216 | 217 | 218 |

This is my title

219 |
220 | 221 |
222 |

This is the first image displayed on the site

223 |
224 |
225 |
226 |

blah blah blah i am text

227 |
228 | 229 |
230 |

This image is very interesting

231 |
232 |
233 |
234 | 235 | 236 | """ 237 | ) 238 | test("attributes") - strCheck( 239 | html( 240 | head( 241 | script("some script") 242 | ), 243 | body( 244 | h1("This is my title"), 245 | div( 246 | p(onclick:="... do some js")( 247 | "This is my first paragraph" 248 | ), 249 | a(href:="www.google.com")( 250 | p("Goooogle") 251 | ), 252 | p(hidden)( 253 | "I am hidden" 254 | ) 255 | ) 256 | ) 257 | ) 258 | , 259 | html( 260 | head( 261 | script("some script") 262 | ), 263 | body( 264 | h1("This is my title"), 265 | div( 266 | p(attr("onclick"):="... do some js")( 267 | "This is my first paragraph" 268 | ), 269 | a(attr("href"):="www.google.com")( 270 | p("Goooogle") 271 | ), 272 | p(attr("hidden").empty)( 273 | "I am hidden" 274 | ) 275 | ) 276 | ) 277 | ) 278 | , 279 | """ 280 | 281 | 282 | 283 | 284 | 285 |

This is my title

286 |
287 |

288 | This is my first paragraph

289 | 290 |

Goooogle

291 |
292 | 294 |
295 | 296 | 297 | """ 298 | ) 299 | test("classesAndCssCustom") - strCheck( 300 | html( 301 | head( 302 | script("some script") 303 | ), 304 | body( 305 | h1(backgroundColor:="blue", color:="red")("This is my title"), 306 | div(backgroundColor:="blue", color:="red")( 307 | p(cls := "contentpara first")( 308 | "This is my first paragraph" 309 | ), 310 | a(opacity:=0.9)( 311 | p(cls := "contentpara")("Goooogle") 312 | ) 313 | ) 314 | ) 315 | ) 316 | , 317 | html( 318 | head( 319 | script("some script") 320 | ), 321 | body( 322 | h1(style:="background-color: blue; color: red;")("This is my title"), 323 | div(style:="background-color: blue; color: red;")( 324 | p(`class`:="contentpara first")( 325 | "This is my first paragraph" 326 | ), 327 | a(style:="opacity: 0.9;")( 328 | p(cls := "contentpara")("Goooogle") 329 | ) 330 | ) 331 | ) 332 | ) 333 | , 334 | """ 335 | 336 | 337 | 338 | 339 | 340 |

This is my title

341 |
342 |

This is my first paragraph

343 | 344 |

Goooogle

345 |
346 |
347 | 348 | 349 | """ 350 | ) 351 | test("nonStringAttributesAndStyles") - strCheck( 352 | div( 353 | p(float.left)( 354 | "This is my first paragraph" 355 | ), 356 | 357 | a(tabindex:=10)( 358 | p("Goooogle") 359 | ), 360 | input(disabled:=true) 361 | ) 362 | , 363 | """ 364 |
365 |

This is my first paragraph

366 | 367 |

Goooogle

368 |
369 | 370 |
371 | """ 372 | ) 373 | test("forceStringifyingAttributesAndStyles") - strCheck( 374 | div( 375 | p(float:="left")( 376 | "This is my first paragraph" 377 | ), 378 | a(tabindex:="10")( 379 | p("Goooogle") 380 | ), 381 | input(disabled:="true") 382 | ) 383 | , 384 | """ 385 |
386 |

This is my first paragraph

387 | 388 |

Goooogle

389 |
390 | 391 |
392 | """ 393 | ) 394 | test("booleanAttributes") - strCheck( 395 | div(input(readonly)) 396 | , 397 | """ 398 |
399 | """ 400 | ) 401 | test("booleanAttributes2") - strCheck( 402 | div(input(readonly:=1)) 403 | , 404 | """ 405 |
406 | """ 407 | ) 408 | 409 | test("layouts") - strCheck( 410 | { 411 | def page(scripts: Seq[Modifier], content: Seq[Modifier]) = 412 | html( 413 | head(scripts), 414 | body( 415 | h1("This is my title"), 416 | div(cls := "content")(content) 417 | ) 418 | ) 419 | 420 | page( 421 | Seq( 422 | script("some script") 423 | ), 424 | Seq( 425 | p("This is the first ", b("image"), " displayed on the ", a("site")), 426 | img(src:="www.myImage.com/image.jpg"), 427 | p("blah blah blah i am text") 428 | ) 429 | ) 430 | } 431 | , 432 | """ 433 | 434 | 435 | 436 | 437 | 438 |

This is my title

439 |
440 |

This is the first image displayed on the site

441 | 442 |

blah blah blah i am text

443 |
444 | 445 | 446 | """ 447 | ) 448 | 449 | test("inheritence") - strCheck( 450 | { 451 | class Parent{ 452 | def render = html( 453 | headFrag, 454 | bodyFrag 455 | 456 | ) 457 | def headFrag = head( 458 | script("some script") 459 | ) 460 | def bodyFrag = body( 461 | h1("This is my title"), 462 | div( 463 | p("This is my first paragraph"), 464 | p("This is my second paragraph") 465 | ) 466 | ) 467 | } 468 | 469 | object Child extends Parent{ 470 | override def headFrag = head( 471 | script("some other script") 472 | ) 473 | } 474 | 475 | 476 | Child.render 477 | } 478 | , 479 | """ 480 | 481 | 482 | 483 | 484 | 485 |

This is my title

486 |
487 |

This is my first paragraph

488 |

This is my second paragraph

489 |
490 | 491 | 492 | """ 493 | ) 494 | 495 | test("unsanitizedInput") - strCheck({ 496 | val evilInput = "" 497 | 498 | html( 499 | head( 500 | script("some script") 501 | ), 502 | body( 503 | h1("This is my title"), 504 | raw(evilInput) 505 | ) 506 | ) 507 | } 508 | , 509 | """ 510 | 511 | 512 | 513 | 514 | 515 |

This is my title

516 | 517 | 518 | 519 | """ 520 | ) 521 | test("additionalImports") - strCheck({ 522 | // bundle is scalatags.Text or scalatags.JsDom 523 | import bundle._ 524 | import styles2.pageBreakBefore 525 | import tags2.address 526 | import svgTags.svg 527 | import svgAttrs.stroke 528 | div( 529 | p(pageBreakBefore.always, "a long paragraph which should not be broken"), 530 | address("500 Memorial Drive, Cambridge MA"), 531 | svg(stroke:="#0000ff") 532 | ) 533 | } 534 | , 535 | """ 536 |
537 |

538 | a long paragraph which should not be broken 539 |

540 |
500 Memorial Drive, Cambridge MA
541 | 542 |
543 | """ 544 | ) 545 | test("typesafeCSS") - strCheck( 546 | div(zIndex:=10), 547 | """
""" 548 | ) 549 | test("customAttributesAndStyles") - strCheck({ 550 | div( 551 | attr("data-app-key") := "YOUR_APP_KEY", 552 | css("background-color") := "red" 553 | ) 554 | } 555 | , 556 | { 557 | val dataAppKey = attr("data-app-key") 558 | val customBackgroundColorStyle = css("background-color") 559 | div( 560 | dataAppKey := "YOUR_APP_KEY", 561 | customBackgroundColorStyle := "red" 562 | ) 563 | } 564 | , 565 | """ 566 |
567 | """ 568 | ) 569 | 570 | test("customTagNames") - strCheck({ 571 | html( 572 | head( 573 | script("some script") 574 | ), 575 | body( 576 | h1("This is my title"), 577 | p("Hello") 578 | ) 579 | ) 580 | 581 | } 582 | , 583 | tag("html")( 584 | tag("head")( 585 | tag("script")("some script") 586 | ), 587 | tag("body")( 588 | tag("h1")("This is my title"), 589 | tag("p")("Hello") 590 | ) 591 | ) 592 | , 593 | """ 594 | 595 | 596 | 597 | 598 | 599 |

This is my title

600 |

Hello

601 | 602 | 603 | """ 604 | ) 605 | test("aria") - strCheck( 606 | div( 607 | div(id:="ch1Panel", role:="tabpanel", aria.labelledby:="ch1Tab")( 608 | "Chapter 1 content goes here" 609 | ), 610 | div(id:="ch2Panel", role:="tabpanel", aria.labelledby:="ch2Tab")( 611 | "Chapter 2 content goes here" 612 | ), 613 | div(id:="quizPanel", role:="tabpanel", aria.labelledby:="quizTab")( 614 | "Quiz content goes here" 615 | ) 616 | ) 617 | , 618 | """ 619 |
620 |
621 | Chapter 1 content goes here 622 |
623 |
624 | Chapter 2 content goes here 625 |
626 |
627 | Quiz content goes here 628 |
629 |
630 | """ 631 | ) 632 | 633 | test("data") - strCheck( 634 | div( 635 | id:="electriccars", 636 | data.columns:="3", 637 | data.index.number:="12314", 638 | data.parent:="cars", 639 | "..." 640 | ) 641 | , 642 | """ 643 |
648 | ... 649 |
650 | """ 651 | ) 652 | 653 | test("frag") - strCheck( 654 | { 655 | div( 656 | h1("Hello"), 657 | p("World") 658 | ) 659 | } 660 | , 661 | { 662 | val children = Seq[Frag]( 663 | h1("Hello"), 664 | p("World") 665 | ) 666 | div( 667 | children 668 | ) 669 | } 670 | , 671 | { 672 | val children: Frag = frag( 673 | h1("Hello"), 674 | p("World") 675 | ) 676 | div( 677 | children 678 | ) 679 | } 680 | , 681 | """ 682 |
683 |

Hello

684 |

World

685 |
686 | """ 687 | ) 688 | test("modifier") - strCheck( 689 | { 690 | div( 691 | cls := "my-bootstrap-class", 692 | color := "red" 693 | ) 694 | } 695 | , 696 | { 697 | val mods = Seq[Modifier]( 698 | cls := "my-bootstrap-class", 699 | color := "red" 700 | ) 701 | div( 702 | mods 703 | ) 704 | } 705 | , 706 | { 707 | val mods: Modifier = modifier( 708 | cls := "my-bootstrap-class", 709 | color := "red" 710 | ) 711 | div( 712 | mods 713 | ) 714 | } 715 | , 716 | """ 717 |
718 | """ 719 | ) 720 | } 721 | } 722 | -------------------------------------------------------------------------------- /scalatags/test/src/scalatags/generic/PerfTests.scala: -------------------------------------------------------------------------------- 1 | package scalatags 2 | package generic 3 | import utest._ 4 | 5 | class ScalatagsPerf[Builder, Output <: FragT, FragT](val bundle: Bundle[Builder, Output, FragT]) extends PerfTests { 6 | import bundle.all._ 7 | import generic.PerfTests._ 8 | 9 | def para(n: Int, m: generic.Modifier[Builder]*) = p( 10 | m, 11 | title := ("this is paragraph " + n) 12 | ) 13 | 14 | def calc() = { 15 | html( 16 | head( 17 | script("console.log(1)") 18 | ), 19 | body( 20 | h1(color := "red")(titleString), 21 | div(backgroundColor := "blue")( 22 | para(0, 23 | cls := contentpara + " " + first, 24 | firstParaString 25 | ), 26 | a(href := "www.google.com")( 27 | p("Goooogle") 28 | ), 29 | for (i <- 0 until 5) yield { 30 | para(i, 31 | cls := contentpara, 32 | color := (if (i % 2 == 0) "red" else "green"), 33 | "Paragraph ", 34 | i 35 | ) 36 | } 37 | ) 38 | ) 39 | ).toString 40 | } 41 | 42 | } 43 | object PerfTests{ 44 | val titleString = "This is my title" 45 | val firstParaString = "This is my first paragraph" 46 | val contentpara = "contentpara" 47 | val first = "first" 48 | val expected = 49 | """ 50 | 51 | 52 | 53 | 54 | 55 |

This is my title

56 |
57 |

This is my first paragraph

58 | 59 |

Goooogle

60 |
61 |

Paragraph 0

62 |

Paragraph 1

63 |

Paragraph 2

64 |

Paragraph 3

65 |

Paragraph 4

66 |
67 | 68 | 69 | """ 70 | } 71 | trait PerfTests extends TestSuite{ 72 | 73 | def calc(): String 74 | def name: String = this.toString 75 | def tests = TestSuite{ 76 | test("correctness"){ 77 | 78 | TestUtil.strCheck(calc(), PerfTests.expected) 79 | test("performance"){ 80 | println("Benchmarking " + this.name) 81 | val start = System.currentTimeMillis() 82 | var i = 0 83 | val d = 10000 84 | 85 | while(System.currentTimeMillis() - start < d){ 86 | i += 1 87 | calc() 88 | } 89 | 90 | name.padTo(20, ' ') + i + " in " + d 91 | 92 | } 93 | } 94 | } 95 | } -------------------------------------------------------------------------------- /scalatags/test/src/scalatags/generic/StyleSheetTests.scala: -------------------------------------------------------------------------------- 1 | package scalatags 2 | package generic 3 | 4 | import utest._ 5 | 6 | import scalatags.stylesheet._ 7 | 8 | abstract class StyleSheetTests[Builder, Output <: FragT, FragT] 9 | (val bundle: Bundle[Builder, Output, FragT]) extends TestSuite{ 10 | 11 | import bundle.all._ 12 | 13 | val pkg = "scalatags-generic-StyleSheetTests" 14 | object Simple extends StyleSheet { 15 | initStyleSheet() 16 | 17 | val x = cls( 18 | backgroundColor := "red", 19 | height := 125 20 | ) 21 | val y = cls.hover( 22 | opacity := 0.5 23 | ) 24 | 25 | val z = cls(x.splice, y.splice) 26 | } 27 | 28 | 29 | object Inline extends StyleSheet{ 30 | initStyleSheet() 31 | 32 | val w = cls( 33 | &.hover( 34 | backgroundColor := "red" 35 | ), 36 | &.active( 37 | backgroundColor := "blue" 38 | ), 39 | &.hover.active( 40 | backgroundColor := "yellow" 41 | ), 42 | opacity := 0.5 43 | ) 44 | } 45 | 46 | object Cascade extends CascadingStyleSheet{ 47 | initStyleSheet() 48 | 49 | val y = cls() 50 | val x = cls( 51 | a( 52 | backgroundColor := "red", 53 | textDecoration.none 54 | ), 55 | a.hover( 56 | backgroundColor := "blue", 57 | textDecoration.underline 58 | ), 59 | (a.hover ~ div ~ y)( 60 | opacity := 0 61 | ), 62 | div.hover( 63 | div( 64 | y( 65 | opacity := 0 66 | ) 67 | ) 68 | ) 69 | ) 70 | } 71 | 72 | object Custom extends CascadingStyleSheet{ 73 | initStyleSheet() 74 | 75 | override def customSheetName = Some("CuStOm") 76 | val x = cls( 77 | backgroundColor := "red", 78 | height := 125 79 | ) 80 | val y = cls.hover( 81 | opacity := 0.5 82 | ) 83 | 84 | } 85 | 86 | object Defs extends CascadingStyleSheet{ 87 | initStyleSheet() 88 | 89 | def x = cls( 90 | backgroundColor := "red", 91 | height := 125 92 | ) 93 | 94 | def y = cls.hover( 95 | opacity := 0.5 96 | ) 97 | } 98 | 99 | 100 | def check(txt: String, expected: String) = { 101 | // augmentString = work around scala/bug#11125 on JDK 11 102 | def normalize(s: String) = Predef.augmentString(s).linesIterator.map(_.trim).mkString 103 | 104 | assert(normalize(txt) == normalize(expected)) 105 | } 106 | val tests = TestSuite{ 107 | test("feature"){ 108 | test("hello"){ 109 | 110 | check( 111 | Simple.styleSheetText, 112 | s""" 113 | .$pkg-Simple-x{ 114 | background-color: red; 115 | height: 125px; 116 | } 117 | .$pkg-Simple-y:hover{ 118 | opacity: 0.5; 119 | } 120 | .$pkg-Simple-z{ 121 | background-color: red; 122 | height: 125px; 123 | opacity: 0.5; 124 | } 125 | """ 126 | ) 127 | } 128 | 129 | test("inline"){ 130 | check( 131 | Inline.styleSheetText, 132 | s""" 133 | .$pkg-Inline-w{ 134 | opacity: 0.5; 135 | } 136 | .$pkg-Inline-w:hover{ 137 | background-color: red; 138 | } 139 | .$pkg-Inline-w:active{ 140 | background-color: blue; 141 | } 142 | .$pkg-Inline-w:hover:active{ 143 | background-color: yellow; 144 | } 145 | """.stripMargin 146 | ) 147 | } 148 | test("cascade"){ 149 | check( 150 | Cascade.styleSheetText, 151 | s""" 152 | .$pkg-Cascade-x a{ 153 | background-color: red; 154 | text-decoration: none; 155 | } 156 | .$pkg-Cascade-x a:hover{ 157 | background-color: blue; 158 | text-decoration: underline; 159 | } 160 | .$pkg-Cascade-x a:hover div .$pkg-Cascade-y{ 161 | opacity: 0; 162 | } 163 | .$pkg-Cascade-x div:hover div .$pkg-Cascade-y{ 164 | opacity: 0; 165 | } 166 | """ 167 | ) 168 | } 169 | } 170 | test("customization"){ 171 | check( 172 | Custom.styleSheetText, 173 | s""" 174 | .CuStOm-x{ 175 | background-color: red; 176 | height: 125px; 177 | } 178 | .CuStOm-y:hover{ 179 | opacity: 0.5; 180 | } 181 | """ 182 | ) 183 | } 184 | test("defs"){ 185 | check( 186 | Defs.styleSheetText, 187 | s""" 188 | .$pkg-Defs-x{ 189 | background-color: red; 190 | height: 125px; 191 | } 192 | .$pkg-Defs-y:hover{ 193 | opacity: 0.5; 194 | } 195 | """ 196 | ) 197 | } 198 | test("failure"){ 199 | test("noDirectInstantiation"){ 200 | // This doesn't seem to work, even though that snippet does indeed 201 | // cause a compilation error. Maybe a bug in uTest? 202 | 203 | // compileError(""" 204 | // object Funky extends StyleSheet 205 | // """).check(""" 206 | // object Funky extends StyleSheet 207 | // ^ 208 | // """, "object creation impossible") 209 | } 210 | test("noCascade"){ 211 | // crashes compiler in 2.10.x 212 | // compileError(""" 213 | // val Cascade1 = Sheet[Cascade1] 214 | // trait Cascade1 extends StyleSheet{ 215 | // def c = cls() 216 | // def x = cls( 217 | // c( 218 | // backgroundColor := "red", 219 | // textDecoration.none 220 | // ) 221 | // ) 222 | // } 223 | // """).check(""" 224 | // c( 225 | // ^ 226 | // """, "Cls does not take parameters") 227 | // Cascading stylesheets have to be enabled manually, to encourage 228 | // usage only for the rare cases you actually want things to cascade 229 | // compileError(""" 230 | // val Cascade2 = Sheet[Cascade2] 231 | // trait Cascade2 extends StyleSheet{ 232 | // def x = cls( 233 | // a( 234 | // backgroundColor := "red", 235 | // textDecoration.none 236 | // ) 237 | // ) 238 | // } 239 | // """).check(""" 240 | // a( 241 | // ^ 242 | // """, "type mismatch") 243 | } 244 | } 245 | test("htmlFrag"){ 246 | val x = div( 247 | Simple.x, 248 | Simple.y 249 | ) 250 | val expected = s""" 251 |
252 | """ 253 | assert( 254 | x.toString.replaceAll("\\s", "") == 255 | expected.replaceAll("\\s", "") 256 | ) 257 | } 258 | } 259 | } 260 | -------------------------------------------------------------------------------- /scalatags/test/src/scalatags/text/BasicTests.scala: -------------------------------------------------------------------------------- 1 | package scalatags 2 | package text 3 | object BasicTests extends generic.BasicTests(scalatags.Text) -------------------------------------------------------------------------------- /scalatags/test/src/scalatags/text/BundlingTests.scala: -------------------------------------------------------------------------------- 1 | package scalatags 2 | package text 3 | import utest._ 4 | object BundlingTests extends TestSuite{ 5 | object CustomBundle extends Text.Cap with text.Tags with text.Tags2 with Text.Aggregate{ 6 | object st extends Text.Cap with Text.Styles with Text.Styles2 7 | object at extends Text.Cap with Text.Attrs 8 | } 9 | val tests = TestSuite{ 10 | val expected = """ 11 | 12 | 13 | 14 | 15 | 16 |

This is my title

17 |
18 |

This is my first paragraph

19 | 20 |

Goooogle

21 |
22 |
23 | 24 | 25 | """ 26 | test("custom"){ 27 | import CustomBundle._ 28 | TestUtil.strCheck( 29 | html( 30 | head( 31 | script("some script") 32 | ), 33 | body( 34 | h1(st.backgroundColor:="blue", st.color:="red")("This is my title"), 35 | div(st.backgroundColor:="blue", st.color:="red")( 36 | p(at.cls := "contentpara first")( 37 | "This is my first paragraph" 38 | ), 39 | a(st.opacity:=0.9)( 40 | p(at.cls := "contentpara")("Goooogle") 41 | ) 42 | ) 43 | ) 44 | ), 45 | expected 46 | ) 47 | } 48 | test("interop"){ 49 | import CustomBundle.{st, at} 50 | import Text.all._ 51 | TestUtil.strCheck( 52 | CustomBundle.html( 53 | head( 54 | script("some script") 55 | ), 56 | CustomBundle.body( 57 | h1(backgroundColor:="blue", st.color:="red")("This is my title"), 58 | div(st.backgroundColor:="blue", color:="red")( 59 | p(cls := "contentpara first")( 60 | "This is my first paragraph" 61 | ), 62 | CustomBundle.a(st.opacity:=0.9)( 63 | p(at.cls := "contentpara")("Goooogle") 64 | ) 65 | ) 66 | ) 67 | ), 68 | expected 69 | ) 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /scalatags/test/src/scalatags/text/ExampleTests.scala: -------------------------------------------------------------------------------- 1 | package scalatags 2 | package text 3 | 4 | object ExampleTests extends generic.ExampleTests(Text) 5 | 6 | -------------------------------------------------------------------------------- /scalatags/test/src/scalatags/text/PerfTests.scala: -------------------------------------------------------------------------------- 1 | package scalatags 2 | package text 3 | import scalatags.generic 4 | 5 | 6 | case object ScalatagsPerf extends generic.ScalatagsPerf(scalatags.Text) 7 | 8 | // 9 | //case object ScalaXMLPerf extends generic.PerfTests{ 10 | // import generic.PerfTests._ 11 | // 12 | // def calc() = { 13 | // def para(i: Int) = { 14 | // val color = if (i % 2 == 0) "red" else "green" 15 | //

Paragraph {i}

18 | // } 19 | // 20 | // 21 | // 22 | // 23 | // 24 | //

{titleString}

25 | //
26 | //

{firstParaString}

27 | // 28 | //

Goooogle

29 | //
30 | // {0 until 5 map para} 31 | //
32 | // 33 | // 34 | // }.toString 35 | //} 36 | -------------------------------------------------------------------------------- /scalatags/test/src/scalatags/text/StyleSheetTests.scala: -------------------------------------------------------------------------------- 1 | package scalatags 2 | package text 3 | import scalatags.Text.all._ 4 | import utest._ 5 | 6 | object StyleSheetTests 7 | extends generic.StyleSheetTests(scalatags.Text) 8 | 9 | -------------------------------------------------------------------------------- /scalatags/test/src/scalatags/text/TextTests.scala: -------------------------------------------------------------------------------- 1 | package scalatags.text 2 | import utest._ 3 | 4 | object TextTests extends TestSuite{ 5 | 6 | import scalatags.Text.all._ 7 | def tests = TestSuite{ 8 | 9 | test("helloWorld"){ 10 | val sample = div("omg") 11 | assert(sample.toString == "
omg
") 12 | } 13 | 14 | /** 15 | * Tests the usage of the pre-defined tags, as well as creating 16 | * the tags on the fly from Strings 17 | */ 18 | test("tagCreation"){ 19 | assert(a.toString == "") 20 | assert(html.toString == "") 21 | assert(tag("this_is_an_unusual_tag").toString == "") 22 | assert(tag("this-is-a-string-with-dashes", void = true).toString == "") 23 | } 24 | 25 | test("cssHelpers"){ 26 | assert(10.px == "10px") 27 | assert(10.0.px == "10.0px" || 10.0.px == "10px") 28 | assert(10.em == "10em") 29 | assert(10.pt == "10pt") 30 | } 31 | test("fragSeqsAreFrags"){ 32 | val frag1: Frag = Seq( 33 | h1("titless"), 34 | div("lol") 35 | ) 36 | val frag2: Frag = Seq("i", "am", "cow") 37 | val frag3: Frag = Seq(frag1, frag2) 38 | val wrapped = div(frag1, frag2, frag3).toString 39 | assert(wrapped == "

titless

lol
iamcow

titless

lol
iamcow
") 40 | } 41 | test("tagType"){ 42 | import scalatags.Text.all._ 43 | val thing: Tag = div 44 | } 45 | test("namespaced"){ 46 | val sample = tag("abc:def")(attr("hello:world") := "moo") 47 | assert(sample.toString == """""") 48 | } 49 | test("doctype"){ 50 | // Just make sure that this compiles. 51 | val example = doctype("html")(html()) 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /scalatags/test/twirl/page.scala.html: -------------------------------------------------------------------------------- 1 | @(contentpara: String, first: String, titleString: String, firstParaString: String, paras: Seq[(Int, String)]) 2 | 3 | 4 | 5 | 6 | 7 |

@titleString

8 |
9 |

@firstParaString

10 | 11 |

Goooogle

12 |
13 | @for((i, color) <- paras){ 14 | @para(contentpara, i, color) 15 | } 16 |
17 | 18 | -------------------------------------------------------------------------------- /scalatags/test/twirl/para.scala.html: -------------------------------------------------------------------------------- 1 | @(contentpara: String, index: Int, color: String) 2 |

Paragraph @index

3 | --------------------------------------------------------------------------------