summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLi Haoyi <haoyi@dropbox.com>2014-11-12 00:04:54 -0800
committerLi Haoyi <haoyi@dropbox.com>2014-11-12 00:04:54 -0800
commit428976e0c29599e29623c63ba22a12a53a342b1e (patch)
treec4198b5bd414088317294cc0721d387ab6711669
parentdab933c342d755086253b1465a04caeeaa2af823 (diff)
downloadhands-on-scala-js-428976e0c29599e29623c63ba22a12a53a342b1e.tar.gz
hands-on-scala-js-428976e0c29599e29623c63ba22a12a53a342b1e.tar.bz2
hands-on-scala-js-428976e0c29599e29623c63ba22a12a53a342b1e.zip
Lots of link insertion and standardization
-rw-r--r--book/src/main/scala/book/Utils.scala51
-rw-r--r--book/src/main/scalatex/book/Index.scalatex2
-rw-r--r--book/src/main/scalatex/book/Intro.scalatex20
-rw-r--r--book/src/main/scalatex/book/handson/CanvasApp.scalatex22
-rw-r--r--book/src/main/scalatex/book/handson/ClientServer.scalatex10
-rw-r--r--book/src/main/scalatex/book/handson/CommandLine.scalatex8
-rw-r--r--book/src/main/scalatex/book/handson/GettingStarted.scalatex16
-rw-r--r--book/src/main/scalatex/book/handson/PublishingModules.scalatex6
-rw-r--r--book/src/main/scalatex/book/handson/WebPage.scalatex7
-rw-r--r--book/src/main/scalatex/book/indepth/CompilationPipeline.scalatex12
-rw-r--r--book/src/main/scalatex/book/indepth/DesignSpace.scalatex4
-rw-r--r--book/src/main/scalatex/book/indepth/SemanticDifferences.scalatex20
-rw-r--r--examples/demos/Controller.scala47
-rw-r--r--examples/demos/src/main/scala/canvasapp/FlappyLine.scala1
14 files changed, 139 insertions, 87 deletions
diff --git a/book/src/main/scala/book/Utils.scala b/book/src/main/scala/book/Utils.scala
index ab6005b..07f462c 100644
--- a/book/src/main/scala/book/Utils.scala
+++ b/book/src/main/scala/book/Utils.scala
@@ -3,6 +3,8 @@ package book
import acyclic.file
import scala.collection.mutable
import scalatags.Text.all._
+import scalatags.text.Builder
+
case class pureTable(header: Frag*){
def apply(content: Frag*) = {
table(cls:="pure-table pure-table-horizontal half-table")(
@@ -36,9 +38,9 @@ object sect{
val usedRefs = mutable.Set.empty[String]
- def ref(s: String) = {
+ def ref(s: String, txt: String = "") = {
usedRefs += s
- a(s, href:=s"#${munge(s)}")
+ a(if (txt == "") s else txt, href:=s"#${munge(s)}")
}
def munge(name: String) = {
@@ -80,6 +82,51 @@ object lnk{
usedLinks.add(url)
a(name, href:=url)
}
+ object dom{
+ class MdnThing(name: String = toString) extends Frag{
+ def render = lnk.apply(name, "https://developer.mozilla.org/en-US/docs/Web/API/" + name).render
+ def applyTo(t: Builder) = t.addChild(render)
+ }
+ class MdnEvent extends Frag {
+ def render = lnk.apply(toString, "https://developer.mozilla.org/en-US/docs/Web/Events/" + toString).render
+ def applyTo(t: Builder) = t.addChild(render)
+ }
+ object CanvasRenderingContext2D extends MdnThing()
+ object HTMLCanvasElement extends MdnThing()
+ object Element extends MdnThing()
+ object HTMLElement extends MdnThing()
+ object HTMLInputElement extends MdnThing()
+ object HTMLSpanElement extends MdnThing()
+ object XMLHttpRequest extends MdnThing()
+ object getElementById extends MdnThing("document.getElementById")
+ object setInterval extends MdnThing("WindowTimers.setInterval")
+ object mousedown extends MdnThing
+ object mouseup extends MdnThing
+ object onclick extends MdnThing
+ object onkeyup extends MdnThing
+ val JSONparse = lnk("Json.parse", "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse")
+ }
+ object scala{
+ def apply(s: String) = {
+ lnk(s, "http://www.scala-lang.org/files/archive/nightly/docs/library/index.html#" + s)
+ }
+ }
+ object misc{
+ val IntelliJ = lnk("IntelliJ", "http://blog.jetbrains.com/scala/")
+ val Eclipse = lnk("Eclipse", "http://scala-ide.org/")
+ val Rhino = lnk("Rhino", "https://developer.mozilla.org/en-US/docs/Mozilla/Projects/Rhino")
+ val Nodejs = lnk("Nodejs", "http://nodejs.org/")
+ val PhantomJS = lnk("PhantomJS", "http://phantomjs.org/")
+ val Play = lnk("Play", "https://www.playframework.com/")
+ val Scalatra = lnk("Scalatra", "http://www.scalatra.org/")
+ val ScalaTest = lnk("ScalaTest", "http://www.scalatest.org/")
+ val Scalate = lnk("Scalate", "https://github.com/scalate/scalate")
+ }
+ object github{
+ val Scalatags = lnk("Scalatags", "https://github.com/lihaoyi/scalatags")
+ val uPickle= lnk("Scalatags", "https://github.com/lihaoyi/upickle")
+ val scalaPickling = lnk("scala-pickling", "https://github.com/scala/pickling")
+ }
}
object hl{
diff --git a/book/src/main/scalatex/book/Index.scalatex b/book/src/main/scalatex/book/Index.scalatex
index 7d342cd..59d474e 100644
--- a/book/src/main/scalatex/book/Index.scalatex
+++ b/book/src/main/scalatex/book/Index.scalatex
@@ -12,7 +12,7 @@
@lnk("Scala.js", "http://www.scala-js.org/") is a compiler that compiles Scala source code to equivalent Javascript code. That lets you write Scala code that you can run in a web browser, or other environments (Chrome plugins, Node.js, etc.) where Javascript is supported.
@p
- This book contains something for all levels of experience with Scala.js: absolute beginners can get started with the @sect.ref{Intro to Scala.js} and @sect.ref{Hands On} tutorial, people who have used it before can skip ahead to the later parts of the tutorial, @sect.ref{Making a Canvas App} or @sect.ref{Interactive Web Pages}. Intermediate users will find the chapters on @sect.ref{Cross Publishing Libraries} with Scala.js or @sect.ref{Integrating Client-Server}, and even experienced users will find the @sect.ref{In Depth} documention useful.
+ This book contains something for all levels of experience with Scala.js: absolute beginners can get started with the @sect.ref{Intro to Scala.js} and @sect.ref{Hands On} tutorial, people who have used it before can skip ahead to the later parts of the tutorial: @sect.ref{Making a Canvas App} or @sect.ref{Interactive Web Pages}. Intermediate users will find interest in the chapters on @sect.ref{Cross Publishing Libraries} with Scala.js or @sect.ref{Integrating Client-Server}, and even experienced users will find the @sect.ref{In Depth} documention useful.
@p
This book does not spend time on pontifying a philosophy or ideology behind Scala.js or Scala. It instead spends its words on hands-on tutorials and in-depth dives into parts of the Scala.js platform, to try and get you acquainted with Scala.js as soon as possible, so you can make your own decisions about its merits or qualities.
diff --git a/book/src/main/scalatex/book/Intro.scalatex b/book/src/main/scalatex/book/Intro.scalatex
index 1b95c32..33e0188 100644
--- a/book/src/main/scalatex/book/Intro.scalatex
+++ b/book/src/main/scalatex/book/Intro.scalatex
@@ -94,7 +94,7 @@
While not useful for small applications, where most of the logic is gluing together external APIs, this comes in very useful in large applications where a lot of the complexity and room-for-error is entirely internal. With larger apps, you can no longer blame browser vendors for confusing APIs that make your code terrible: these confusing APIs only lurk in the peripherals around a larger, complex application. One thing you learn working in large-ish web client-side code-bases is that the bulk of the confusion and complexity is no-one's fault but your own, as a team.
@p
- At this point, all of Google, Facebook, and Microsoft have all announced work on a typed variant of Javascript. These are not academic exercises: Dart/AtScript/Flow/Typescript are all problems that solve a real need, that these large companies have all faced once they've grown beyond a certain size. Clearly, Javascript isn't cutting it anymore, and the convenience and "native-ness" of the language is more than made up for in the constant barrage of self-inflicted problems. Scala.js takes this idea and runs with it!
+ At this point, all of Google, Facebook, and Microsoft have all announced work on a typed variant of Javascript. These are not academic exercises: @lnk("Dart", "https://www.dartlang.org/")/@lnk("AtScript", "https://docs.google.com/document/d/11YUzC-1d0V1-Q3V0fQ7KSit97HnZoKVygDxpWzEYW0U/edit")/@lnk("Flow", "https://lobste.rs/s/fp9ibi/flow_facebook_s_new_javascript_type_checker")/@lnk("Typescript", "http://www.typescriptlang.org/") are all problems that solve a real need, that these large companies have all faced once they've grown beyond a certain size. Clearly, Javascript isn't cutting it anymore, and the convenience and "native-ness" of the language is more than made up for in the constant barrage of self-inflicted problems. Scala.js takes this idea and runs with it!
@sect{Sharing Code}
@p
@@ -116,7 +116,7 @@
Not having to resort to awkward Ajax-calls or pre-computation to avoid duplicating logic between the client and server
@p
- Shared code doesn't just mean sharing pre-made libraries between the client and server. You can easily publish your own libraries that can be used on both Scala-JVM and Scala.js. This means that as a library author, you can at once target two completely different platforms, and (with some work) take advantage of the intricacies of each platform to optimize your library for each one. Take Scalatags as an example: as the first client-server Scala.js-ScalaJVM shared libraries, it enjoys a roughly event split of downloads from people using it on both platforms:
+ Shared code doesn't just mean sharing pre-made libraries between the client and server. You can easily @sect.ref("Cross Publishing Libraries", "publish your own libraries") that can be used on both Scala-JVM and Scala.js. This means that as a library author, you can at once target two completely different platforms, and (with some work) take advantage of the intricacies of each platform to optimize your library for each one. Take Scalatags as an example: as the first client-server Scala.js-ScalaJVM shared libraries, it enjoys a roughly event split of downloads from people using it on both platforms:
@img(src:="images/Scalatags Downloads.png", width:="100%")
@@ -132,7 +132,7 @@
@p
One common theme in all these platforms is that their main selling point is their tight, seamless client-server integration, to the point where you can just make method calls across the client-server boundary and the platform/language/compiler figures out what to do.
@p
- With Scala.js and Scala-JVM, such conveniences like making method calls across the client-server boundary is the boring reality. Not only are the calls transparent, they are also statically checked, so any mistake in the route name or the parameters it expects, or the result type it returns to you, will be caught by the compiler long before even manual testing.
+ With Scala.js and Scala-JVM, such conveniences like making method calls across the client-server boundary is the @sect.ref("Integrating Client-Server", "boring reality"). Not only are the calls transparent, they are also statically checked, so any mistake in the route name or the parameters it expects, or the result type it returns to you, will be caught by the compiler long before even manual testing.
@hr
@@ -149,27 +149,27 @@
@sect{Part 1: Hands On}
@p
- A whirlwind tour of the various things that Scala.js can be used for. We will cover:
+ A @sect.ref("Hands On", "whirlwind tour") of the various things that Scala.js can be used for. We will cover:
@ul
@li
- Your first Scala.js application: setting up your development environment, cloning the example repository, debugging and finally publishing your first toy application
+ @sect.ref("Getting Started"): setting up your development environment, cloning the example repository, debugging and finally publishing your first toy application
@li
- An interactive web app using Scala.js: how you interact with the HTML DOM, how you utilize Ajax calls and other browser APIs that are common in Javascript-heavy applications
+ @sect.ref("Interactive Web Pages"): how you interact with the HTML DOM, how you utilize Ajax calls and other browser APIs that are common in Javascript-heavy applications
@li
- A Scala.js library: how to write a module that can be depended on by applications both your own and by others, and be used both with Scala.js and Scala-on-the-JVM
+ @sect.ref("Cross Publishing Libraries"): how to write a module that can be depended on by applications both your own and by others, and be used both with Scala.js and Scala-on-the-JVM
@li
- Client-Server integration: We will build a simple web application with a Scala server and Scala.js client. In the process, we'll explore how to share code between client and server, how to get compiler-checked/boilerplate-free Ajax calls between client and server, and many other long-standing holy-grails of web development
+ @sect.ref("Client-Server Integration"): We will build a simple web application with a Scala server and Scala.js client. In the process, we'll explore how to share code between client and server, how to get compiler-checked/boilerplate-free Ajax calls between client and server, and many other long-standing holy-grails of web development
@p
After going through this chapter and following along with the exercises, you should have a good sense of how Scala.js works and how it feels building things in Scala.js. You would not be an expert, but you'll know where to get started if you decide to try out Scala.js for your next project.
@sect{Part 2: In Depth}
@p
- This section of the book will cover lots of content that does not fit in the earlier Hands-On portion of the book. Things that aren't immediately necessary to get something up and running, things that only advanced users would care about, things that you probably don't need to know but you'd like to know out of intellectual curiosity.
+ @sect.ref("In Depth", "This section of the book") will cover lots of content that does not fit in the earlier Hands-On portion of the book. Things that aren't immediately necessary to get something up and running, things that only advanced users would care about, things that you probably don't need to know but you'd like to know out of intellectual curiosity.
@p
- In general, this section of the book will go much deeper into Scala.js, much more than is necessary to get your first applications built. We will talk about the small number of semantic differences between Scala.js and Scala, details of the foreign-function-interface with Javascript, the various optimization levels and what they do. Nothing pressing or urgently needed, but all very interesting, and worth reading if you want to really understand Scala.js in depth.
+ In general, this section of the book will go much deeper into Scala.js, much more than is necessary to get your first applications built. We will talk about the small number of @sect.ref("Deviations from Scala-JVM", "Semantic Differences") between Scala.js and Scala, details of the foreign-function-interface used for @sect.ref("Javascript Interoperability"), @sect.ref("The Compilation Pipeline") with its various optimization levels and what they do. Nothing pressing or urgently needed, but all very interesting, and worth reading if you want to really understand Scala.js in depth.
@hr
diff --git a/book/src/main/scalatex/book/handson/CanvasApp.scalatex b/book/src/main/scalatex/book/handson/CanvasApp.scalatex
index e352e0e..662f541 100644
--- a/book/src/main/scalatex/book/handson/CanvasApp.scalatex
+++ b/book/src/main/scalatex/book/handson/CanvasApp.scalatex
@@ -24,7 +24,7 @@
@hl.ref("examples/demos/src/main/scala/canvasapp/ScratchPad.scala", "/*setup*/", end = "/*code*/")
@p
- As described earlier, this code uses the @hl.javascript{document.getElementById} function to fish out the @code{canvas} element that we interested in from the DOM. It then gets a rendering context from that @code{canvas}, and sets the height and width of the canvas to completely fill its containing element. Lastly, it fills out the canvas light-gray, so that we can see it on the page.
+ As described earlier, this code uses the @lnk.dom.getElementById} function to fish out the @code{canvas} element that we interested in from the DOM. It then gets a rendering context from that @code{canvas}, and sets the height and width of the canvas to completely fill its containing element. Lastly, it fills out the canvas light-gray, so that we can see it on the page.
@p
Next, let's set some event handlers on the canvas:
@@ -38,10 +38,10 @@
@script("ScratchPad().main(document.getElementById('canvas2'))")
@p
- This code sets up the @code{mousedown} and @code{mouseup} events to keep track of whether or not the mouse has currently been clicked. It then draws black squares any time you move the mouse while the button is down. This lets you basically click-and-drag to draw pictures on the canvas. Try it out!
+ This code sets up the @lnk.dom.mousedown and @lnk.dom.mouseup events to keep track of whether or not the mouse has currently been clicked. It then draws black squares any time you move the mouse while the button is down. This lets you basically click-and-drag to draw pictures on the canvas. Try it out!
@p
- In general, you have access to all the DOM APIs through the @hl.scala{dom} package as well as through Javascript objects such as the @hl.scala{dom.HTMLCanvasElement}. Setting the @code{onmouseXXX} callbacks is just one way of interacting with the DOM. With Scala.js, you also get a very handy autocomplete in the editor, which you can use to browse the various other APIs that are available for use:
+ In general, you have access to all the DOM APIs through the @hl.scala{dom} package as well as through Javascript objects such as the @lnk.dom.HTMLCanvasElement. Setting the @code{onmouseXXX} callbacks is just one way of interacting with the DOM. With Scala.js, you also get a very handy autocomplete in the editor, which you can use to browse the various other APIs that are available for use:
@img(src:="images/Dropdown.png", maxWidth:="100%")
@@ -51,7 +51,7 @@
@sect{Making a Clock using setInterval}
@p
- You've already seen this in the previous example, but @hl.scala{dom.setInterval} can be used to schedule recurring, periodic events in your program. Common use cases include running the event loop for a game, making smooth animations, and other tasks of that sort which require some work to happen over a period of time.
+ You've already seen this in the previous example, but @lnk.dom.setInterval can be used to schedule recurring, periodic events in your program. Common use cases include running the @lnk("event loop for a game", "http://gameprogrammingpatterns.com/game-loop.html"), making smooth animations, and other tasks of that sort which require some work to happen over a period of time.
@p
Again, we need roughly the same boilerplate as just now to set up the canvas:
@@ -73,7 +73,7 @@
@script("Clock().main(document.getElementById('canvas3'))")
@p
- As you can see, we're using more @lnk("Canvas APIs", "https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D"), in this case dealing with rendering text on the canvas. Another thing we're using is the Javascript @lnk("Date", "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date") class, in Scala.js under the fully name @hl.scala{scala.scalajs.js.Date}, here imported as @hl.scala{js.Date}.
+ As you can see, we're using more @lnk("Canvas APIs", "https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D"), in this case dealing with rendering text on the canvas. Another thing we're using is the Javascript @lnk("Date", "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date") class, in Scala.js under the full name @hl.scala{scala.scalajs.js.Date}, here imported as @hl.scala{js.Date}.
@sect{Tying it together: Flappy Box}
@@ -114,7 +114,7 @@
This is where we start defining things that are relevant to Flappy Box. There are roughly two groups of values here: immutable constants in the top group, and mutable variables in the bottom. The rough meaning of each variable is documented in the comments, and we'll see exactly how we use them later.
@p
- One notable thing is that we're using a @hl.scala{collection.mutable.Queue} to store the list of obstacles. This is defined in the Scala standard library; in general, all the collections in the Scala standard library can be used without issue in Scala.js.
+ One notable thing is that we're using a @lnk("collection.mutable.Queue", "http://docs.scala-lang.org/overviews/collections/concrete-mutable-collection-classes.html") to store the list of obstacles. This is defined in the Scala standard library; in general, all the collections in the Scala standard library can be used without issue in Scala.js.
@sect{Game Logic}
@hl.ref("examples/demos/src/main/scala/canvasapp/FlappyLine.scala", "def runLive", "def runDead")
@@ -146,7 +146,7 @@
@hl.ref("examples/demos/src/main/scala/canvasapp/FlappyLine.scala", "def run()")
@p
- And finally, this is the code that kicks everything off: we define the @hl.scala{run} function to swap between @hl.scala{runLive} and @hl.scala{runDead}, register an @hl.scala{canvas.onclick} handler to make the player jump by tweaking his velocity, and we call @code{setInterval} to run the @hl.scala{run} function every 20 milliseconds.
+ And finally, this is the code that kicks everything off: we define the @hl.scala{run} function to swap between @hl.scala{runLive} and @hl.scala{runDead}, register an @lnk.dom.onclick handler to make the player jump by tweaking his velocity, and we call @lnk.dom.setInterval to run the @hl.scala{run} function every 20 milliseconds.
@p
At almost 100 lines of code, this is quite a meaty example! Nonetheless, when all is said and done, you will find that the example actually works! Try it out!
@@ -163,7 +163,7 @@
@p
We've by now written a good chunk of Scala.js code, and perhaps debugged some mysterious errors, and tried some new things. One thing you've probably noticed is the efficiency of the process: you make a change in your editor, the browser reloads itself, and life goes on. There is a compile cycle, but after a few runs the compiler warms up and the compilation cycle drops to less than a second.
@p
- Apart from the compilation/reload speed, you've probably noticed the benefit of tooling around Scala.js. Unlike Javascript editors, your existin Scala IDEs like IntelliJ or Eclipse can give very useful help when you're working with Scala.js. Autocomplete, error-highlghting, jump-to-definition, and a myriad other modern conveniences that are missing when working in dynamically-typed languages are present when working in Scala.js. This makes the code much less mysterious: you're no longer trying to guess what methods a value has, or what a method returns: it's all laid out in front of you in plain sight.
+ Apart from the compilation/reload speed, you've probably noticed the benefit of tooling around Scala.js. Unlike Javascript editors, your existin Scala IDEs like @lnk.misc.IntelliJ or @lnk.misc.Eclipse can give very useful help when you're working with Scala.js. Autocomplete, error-highlghting, jump-to-definition, and a myriad other modern conveniences that are missing when working in dynamically-typed languages are present when working in Scala.js. This makes the code much less mysterious: you're no longer trying to guess what methods a value has, or what a method returns: it's all laid out in front of you in plain sight.
@sect{Full Scala}
@p
@@ -178,9 +178,9 @@
@ul
@li
- @hl.scala{obstacles} is a Scala @hl.scala{mutable.Queue}, as we defined it earlier, and all the methods on it are Scala method calls
+ @hl.scala{obstacles} is a Scala @lnk("mutable.Queue", "http://docs.scala-lang.org/overviews/collections/concrete-mutable-collection-classes.html"), as we defined it earlier, and all the methods on it are Scala method calls
@li
- @hl.scala{renderer} is a Javascript @hl.javascript{CanvasRenderingContext2D}, and all the methods on it are Javascript method calls directly on the Javascript object
+ @hl.scala{renderer} is a Javascript @lnk.dom.CanvasRenderingContext2D, and all the methods on it are Javascript method calls directly on the Javascript object
@li
@hl.scala{frame} is a Scala @hl.scala{Int}, and obeys Scala semantics, though it is implemented as a Javascript @hl.javascript{Number} under the hood.
@li
@@ -189,7 +189,7 @@
@p
This reveals something pretty interesting about Scala.js: even though Scala at-first-glance is a very different language from Javascript, the interoperation with Javascript is so seamless that you can't even tell from the code which values/methods are defined in Scala and which values/methods come from Javascript!
@p
- These two classes of values/methods are treated very differently by the compiler when it comes to emitting the executable Javascript blob, but the compiler does not need extra syntax telling it which things belong to Scala and which to Javascript: the types are sufficient. @hl.scala{renderer}, for example is of type @hl.scala{dom.CanvasRenderContext2D} which is a subtype of @hl.scala{scalajs.js.Object}, indicating to the compiler that it needs special treatment. Primitives like @hl.scala{Double}s and @hl.scala{Int}s have similar treatment
+ These two classes of values/methods are treated very differently by the compiler when it comes to emitting the executable Javascript blob, but the compiler does not need extra syntax telling it which things belong to Scala and which to Javascript: the types are sufficient. @hl.scala{renderer}, for example is of type @lnk.dom.CanvasRenderingContext2D which is a subtype of @hl.scala{scalajs.js.Object}, indicating to the compiler that it needs special treatment. Primitives like @hl.scala{Double}s and @hl.scala{Int}s have similar treatment
@p
Overall, this seamless mix of Scala and Javascript values/methods/functions is a common theme in Scala.js applications, so you should expect to see more of it in later chapters of the book.
diff --git a/book/src/main/scalatex/book/handson/ClientServer.scalatex b/book/src/main/scalatex/book/handson/ClientServer.scalatex
index 29b253d..247d9ac 100644
--- a/book/src/main/scalatex/book/handson/ClientServer.scalatex
+++ b/book/src/main/scalatex/book/handson/ClientServer.scalatex
@@ -15,7 +15,7 @@
@sect{A Client-Server Setup}
@p
- Getting started with client-server integration, let's go with the simplest configuration possible: a Spray server and a Scala.js client. Most of the other web-frameworks (Play, Scalatra, etc.) will have more complex configurations, but the basic mechanism of wiring up Scala.js to your web framework will be the same. Our project will look like this:
+ Getting started with client-server integration, let's go with the simplest configuration possible: a Spray server and a Scala.js client. Most of the other web-frameworks (@lnk.misc.Play, @lnk.misc.Scalatra, etc.) will have more complex configurations, but the basic mechanism of wiring up Scala.js to your web framework will be the same. Our project will look like this:
@hl.bash
$ tree
@@ -38,7 +38,7 @@
@hl.ref("examples/crossBuilds/clientserver/build.sbt")
@p
- We have two projects: @code{client} and @code{server}, one of which is a Scala.js project (indicated by the presence of @hl.scala{scalaJSSettings}). Both projects share a number of settings: the presence of the @code{shared/} folder, which shared code can live in (similar to what we saw in Cross-platform Modules) and the settings to add Scalatags and uPickle to the build. Note that those two dependencies use the triple @code{%%%} instead of the double @code{%%} to declare: this means that for each dependency, we will pull in the Scala-JVM or Scala.js version depending on whether it's being used in a Scala.js project.
+ We have two projects: @code{client} and @code{server}, one of which is a Scala.js project (indicated by the presence of @hl.scala{scalaJSSettings}). Both projects share a number of settings: the presence of the @code{shared/} folder, which shared code can live in (similar to what we saw in @sect.ref{Cross Publishing Libraries}) and the settings to add @lnk.github.Scalatags and @lnk.github.uPickle to the build. Note that those two dependencies use the triple @code{%%%} instead of the double @code{%%} to declare: this means that for each dependency, we will pull in the Scala-JVM or Scala.js version depending on whether it's being used in a Scala.js project.
@p
The @code{client} subproject is uneventful, with a dependency on the by-now-familiar @code{scalajs-dom} library. The @code{server} project, on the other hand, is interesting: it contains the dependencies required for us to set up out Spray server, and one additioanl thing: we add the output of @code{fastOptJS} from the client to the @code{resources} on the server. This will allow the @code{server} to serve the compiled-javascript from our @code{client} project from its resources.
@@ -56,7 +56,7 @@
@hl.ref("examples/crossBuilds/clientserver/server/src/main/scala/simple/Page.scala")
@p
- This is a typical Scalatags HTML snippet. Note that since we're serving it directly from the server in Scala code, we do not need to leave a @code{.html} file somewhere on the filesystem! We can declare all HTML, including the skeleton of the page, in Scalatags. Otherwise it's the same as what we saw in earlier chapters: A simple HTML page which includes a script tag to run our Scala.js application.
+ This is a typical @lnk.github.Scalatags HTML snippet. Note that since we're serving it directly from the server in Scala code, we do not need to leave a @code{.html} file somewhere on the filesystem! We can declare all HTML, including the skeleton of the page, in Scalatags. Otherwise it's the same as what we saw in earlier chapters: A simple HTML page which includes a script tag to run our Scala.js application.
@p
Lastly, we'll set up the Scala.js main method, which we are calling in the @hl.html{<script>} tag above to kick off the client-side application.
@@ -82,7 +82,7 @@
@p
In both the client code and the server code, we made use of the same Scalatags HTML generation library. This is pretty neat: transferring rendering logic between client and server no longer means an annoying/messy rewrite! You can simply C&P the Scalatags snippet over. That means it's easy if you want to e.g. shift the logic from one side to the other in order to optimize for performance or time-to-load or other things.
@p
- One thing to take note of is that we're actually using subtly @i{different} implementations of Scalatags on both sides: on the server, we're importing from @hl.scala{scalatags.Text}, while on the client we're using @hl.scala{scalatags.JsDom}. The @hl.scala{Text} backend renders directly to Strings, and is available on both Scala-JVM and Scala.js. The @hl.scala{JsDom} backend, on the other hand, renders to @hl.scala{dom.HTMLElement}s which only exist on Scala.js. Thus while on the client you can do things like attach event listeners to the rendered @hl.scala{dom.HTMLElement} objects, or checking their runtime @code{.value}, on the server you can't. And that's exactly what you want!
+ One thing to take note of is that we're actually using subtly @i{different} implementations of Scalatags on both sides: on the server, we're importing from @hl.scala{scalatags.Text}, while on the client we're using @hl.scala{scalatags.JsDom}. The @hl.scala{Text} backend renders directly to Strings, and is available on both Scala-JVM and Scala.js. The @hl.scala{JsDom} backend, on the other hand, renders to @lnk.dom.HTMLElement-s which only exist on Scala.js. Thus while on the client you can do things like attach event listeners to the rendered @lnk.dom.HTMLElement objects, or checking their runtime @code{.value}, on the server you can't. And that's exactly what you want!
@sect{Shared Code}
@p
@@ -95,7 +95,7 @@
The Ajax/RPC layer is one of the more fragile parts of web applications. Often, you have your various Ajax endpoints written once on the server, have a set of routes written to connect those Ajax endpoints to URLs, and client code (traditionally Javascript) made calls to those URLs with "raw" data: basically whatever you wanted, packed in an ad-hoc mix of CSV and JSON and raw-strings.
@p
- This has always been annoying boilerplate, and Scala.js removes it. With uPickle, you can simply call @hl.scala{upickle.write(...)} and @hl.scala{upickle.read[T](...)} to convert your collections, primitives or case-classes to and from JSON. This means you do not need to constantly re-invent different ways of making Ajax calls: you can just fling the data right across the network from client to server and back again.
+ This has always been annoying boilerplate, and Scala.js removes it. With @lnk.github.uPickle, you can simply call @hl.scala{upickle.write(...)} and @hl.scala{upickle.read[T](...)} to convert your collections, primitives or case-classes to and from JSON. This means you do not need to constantly re-invent different ways of making Ajax calls: you can just fling the data right across the network from client to server and back again.
@hr
diff --git a/book/src/main/scalatex/book/handson/CommandLine.scalatex b/book/src/main/scalatex/book/handson/CommandLine.scalatex
index 77312ae..29bd77f 100644
--- a/book/src/main/scalatex/book/handson/CommandLine.scalatex
+++ b/book/src/main/scalatex/book/handson/CommandLine.scalatex
@@ -9,7 +9,7 @@
The Scala.js command line is where you go to do things to your Scala.js code. Although Scala.js comes with @lnk("standalone executables", "http://www.scala-js.org/downloads.html") that can be used from any shell (sh, bash, zsh, etc.) the primary supported way of setting up, compiling and testing your Scala.js applications is through @lnk("SBT", "http://www.scala-sbt.org/"): Scala's primary build tool.
@p
- You've already used fragments of the Scala.js CLI in earlier chapters of this book: @code{~fastOptJS} is what you used for development, @code{fullOptJS} for publishing. Apart from these, Scala.js allows you to execute code via Rhino/Node.js/Phantom.js from the command-line, as well as running test-suites under the same conditions. This chapter will go deeper into the various things you can do with the Scala.js command line:
+ You've already used fragments of the Scala.js CLI in earlier chapters of this book: @code{~fastOptJS} is what you used for development, @code{fullOptJS} for publishing. Apart from these, Scala.js allows you to execute code via @lnk.misc.Rhino/@lnk.misc.Nodejs/@lnk.misc.PhantomJS from the command-line, as well as running test-suites under the same conditions. This chapter will go deeper into the various things you can do with the Scala.js command line:
@ul
@li
@@ -147,11 +147,11 @@
@sect{Headless Runtimes}
@ul
@li
- @b{Rhino} is the default way of running Scala.js applications, and occurs when you hit @code{sbt run} from the terminal. The upside of using @lnk("Rhino", "https://developer.mozilla.org/en-US/docs/Mozilla/Projects/Rhino") is that it is pure-Java, and doesn't need any additional binaries or installation. The downside of using Rhino is that it is slow: maybe a hundred times slower than the alternatives, making it not suitable for running long-running, compute-intensive programs. Furthermore, it's a very sparse runtime environment, with no DOM access or similar.
+ @b{Rhino} is the default way of running Scala.js applications, and occurs when you hit @code{sbt run} from the terminal. The upside of using @lnk.misc.Rhino is that it is pure-Java, and doesn't need any additional binaries or installation. The downside of using Rhino is that it is slow: maybe a hundred times slower than the alternatives, making it not suitable for running long-running, compute-intensive programs. Furthermore, it's a very sparse runtime environment, with no DOM access or similar.
@li
- @b{Node.js}, a relatively new Javascript runtime based on Google's V8 Javascript engine, @lnk("Node.js", "http://nodejs.org/") lets you run your Scala.js application from the command line much faster than in Rhino, with performance that matches that of modern browsers. However, you need to separately @lnk("install Node.js", "http://nodejs.org/download/") in order to use it. Like Rhino, it comes with a bare-minimal runtime environment, with no DOM or browser-related functionality. You need to run @code{sbt fastOptStage::run} to run using Node.js.
+ @b{Node.js}, a relatively new Javascript runtime based on Google's V8 Javascript engine, @lnk.misc.Nodejs lets you run your Scala.js application from the command line much faster than in Rhino, with performance that matches that of modern browsers. However, you need to separately @lnk("install Node.js", "http://nodejs.org/download/") in order to use it. Like Rhino, it comes with a bare-minimal runtime environment, with no DOM or browser-related functionality. You need to run @code{sbt fastOptStage::run} to run using Node.js.
@li
- @b{PhantomJS} is a headless Webkit browser. This means that unlike Node.js or Rhino, @lnk("Phantom.js", "http://phantomjs.org/") provides you with a full DOM and all its APIs to use in your tests, if you wish to e.g. test interactions with the HTML of the web page. On the other hand, it is somewhat slower than Node.js, though still much faster than Rhino. Like Node.js, it needs to be installed separately. You need to run You need to run @code{sbt fastOptStage::run}, as well as setting the @hl.scala{requiresDOM := true} flag in your SBT configuration, to use PhantomJS.
+ @b{PhantomJS} is a headless Webkit browser. This means that unlike Node.js or Rhino, @lnk.misc.PhantomJS provides you with a full DOM and all its APIs to use in your tests, if you wish to e.g. test interactions with the HTML of the web page. On the other hand, it is somewhat slower than Node.js, though still much faster than Rhino. Like Node.js, it needs to be installed separately. You need to run You need to run @code{sbt fastOptStage::run}, as well as setting the @hl.scala{requiresDOM := true} flag in your SBT configuration, to use PhantomJS.
@p
These are your three options to run your Scala.js code via the command-line. Generally, it's easiest to get started with Rhino since it's the default and requires no setup, though you may find it worthwhile to setup Node or Phantom if you need additional speed or DOM-integration in your runs.
diff --git a/book/src/main/scalatex/book/handson/GettingStarted.scalatex b/book/src/main/scalatex/book/handson/GettingStarted.scalatex
index a5e74a9..b766ea7 100644
--- a/book/src/main/scalatex/book/handson/GettingStarted.scalatex
+++ b/book/src/main/scalatex/book/handson/GettingStarted.scalatex
@@ -114,18 +114,18 @@
This @hl.scala("@JSExport") annotation is used to tell Scala.js that you want this method to be visible and callable from Javascript. By default, Scala.js does dead code elimination and removes any methods or classes which are not used. This is done to keep the compiled executables a reasonable size, since most projects use only a small fraction of e.g. the standard library. @hl.scala("@JSExport") is used to tell Scala.js that the @hl.scala{ScalaJSExample} object and its @hl.scala{def main} method are entry points to the program. Even if they aren't called anywhere internally, they are called externally by Javascript that the Scala.js compiler is not aware of, and should not be removed.
@p
- Apart from this annotation, @hl.scala{ScalaJSExample} is just a normal Scala @hl.scala{object}, and behaves like one in every way. Note that the main-method in this case takes a @hl.scala{dom.HTMLCanvasElement}: your exported methods can have any signature, with arbitrary arity or types for parameters or the return value. This is in contrast to the main method on the JVM which always takes an @hl.scala{Array[String]} and returns @hl.scala{Unit}. In fact, there's nothing special about this method at all! It's like any other exported method, we just happen to attribute it the "main" entry point.
+ Apart from this annotation, @hl.scala{ScalaJSExample} is just a normal Scala @hl.scala{object}, and behaves like one in every way. Note that the main-method in this case takes a @lnk.dom.HTMLCanvasElement: your exported methods can have any signature, with arbitrary arity or types for parameters or the return value. This is in contrast to the main method on the JVM which always takes an @hl.scala{Array[String]} and returns @hl.scala{Unit}. In fact, there's nothing special about this method at all! It's like any other exported method, we just happen to attribute it the "main" entry point.
@hl.ref("output/workbench-example-app/src/main/scala/example/ScalaJSExample.scala", "val ctx", "var count")
@p
- Here we are retrieving a handle to the canvas we will draw on using @hl.scala{document.getElementById}, and from it we can get a @hl.scala{dom.CanvasRenderingContext2D} which we actually use to draw on it.
+ Here we are retrieving a handle to the canvas we will draw on using @hl.scala{document.getElementById}, and from it we can get a @lnk.dom.CanvasRenderingContext2D which we actually use to draw on it.
@p
@hl.scala{document.getElementById} is the exact same API that's used in normal Javascript, as documented @lnk("here", "https://developer.mozilla.org/en-US/docs/Web/API/document.getElementById"). In fact, the entire @hl.scala{org.scalajs.dom} namespace (imported at the top of the file) comprises statically typed facades for the javascript APIs provided by the browser.
@p
- We need to perform the @hl.scala{asInstanceOf} call because depending on what you pass to @hl.scala{getElementById} and @hl.scala{getContext}, you could be returned elements and contexts of different types. Hence we need to tell the compiler explicitly that we're expecting a @hl.scala{dom.HTMLCanvasElement} and @hl.scala{dom.CanvasRenderingContext2D} back from these methods for the strings we passed in.
+ We need to perform the @hl.scala{asInstanceOf} call because depending on what you pass to @hl.scala{getElementById} and @hl.scala{getContext}, you could be returned elements and contexts of different types. Hence we need to tell the compiler explicitly that we're expecting a @lnk.dom.HTMLCanvasElement and @lnk.dom.CanvasRenderingContext2D back from these methods for the strings we passed in.
@hl.ref("output/workbench-example-app/src/main/scala/example/ScalaJSExample.scala", "def run", "dom.setInterval")
@@ -252,7 +252,7 @@
144K target/scala-2.11/example-opt.js
@p
- 144 Kilobytes! Better. Not great, though! In general, Scala.js does not produce tiny executables, although the output size of the compiled executables is dropping all the time. If you look inside that file, you'll see all of the long identifiers have been replaced by short ones by the Google Closure Compiler.
+ 144 Kilobytes! Better. Not great, though! In general, Scala.js does not produce tiny executables, although the output size of the compiled executables is dropping all the time. If you look inside that file, you'll see all of the long identifiers have been replaced by short ones by the @lnk("Google Closure Compiler", "https://developers.google.com/closure/compiler/").
@hl.javascript("""
Rb(P(),N(r(L(Ea),["rgb(",", ",", ",")"])))),Sb(P(),r(L(K),[n,s,C])));fe().te.fillRect(fe().Oc.jc,fe().Oc.kc,1,1);e=e+1|0;c=c+k|0}},50))}ae.prototype.Zf=function(){this.te.fillStyle="black";this.te.fillRect(0,0,255,255)};ae.prototype.main=function(){return ee(),void 0};ae.prototype.a=new I({lj:0},!1,"example.ScalaJSExample$",K,{lj:1,b:1});var be=void 0;function fe(){be||(be=(new ae).c());return be}ba.ScalaJSExample=fe;function le(){}le.prototype=new J;function me(){}me.prototype=le.prototype;
@@ -270,14 +270,14 @@
@ul
@li
- A large portion of this 144k is the Scala standard library, and so the size of the compiled blob does not grow that fast as your program grows. For example, while this ~50 line application is 144k, a much larger ~2000 line application is only 288k.
+ A large portion of this 144k is the Scala standard library, and so the size of the compiled blob does not grow that fast as your program grows. For example, while this ~50 line application is 144k, a @lnk("much larger ~2000 line application", "https://github.com/lihaoyi/roll") is only 288k.
@li
This size is pre-@lnk("gzip", "http://en.wikipedia.org/wiki/Gzip"), and most webservers serve their contents compressed via gzip to reduce the download size. Gzip cuts the actual download size down to 43k, which is more acceptable.
@li
You will likely have other portions of the page that are of similar size: e.g. @lnk("JQuery", "http://jquery.com/") is extremely popular, and weights in at a comparable 32kb minified and gzipped, while @lnk("React.js", "http://facebook.github.io/react/downloads.html") weighs in at a cool 150kb gzipped. Scala.js arguably provides more than either of these libraries.
@p
- Regardless, there is ongoing work to shrink the size of these executables. If you want to read more about this, check out the section on the Scala.js File Encoding and the Optimization Pipeline.
+ Regardless, there is ongoing work to shrink the size of these executables. If you want to read more about this, check out the section on @sect.ref("The Compilation Pipeline") to learn about what we currently do to crunch the executables down.
@hr
@@ -285,7 +285,7 @@
In general, all the output of the Scala.js compiler is bundled up into the @code{example-fastopt.js} and @code{example-opt.js} files. As a first approximation, these files can be included directly on a HTML page (as we have here, with @code{index-dev.html} and @code{index-opt.html}) and published together as a working web app. Even zipping them up and emailing them to a friend is sufficient to give someone a working, live version of your hard work!
@p
- More advanced users would want to integrate them into their build process or serve them from a web server, all of which is entirely possible. You just need to run the Scala.js compiler and place the output @code{.js} file somewhere your web server can pick it up, e.g. in some static-resource folder. We cover an example setup of this with a Scala webserver in our Client Server Integration chapter.
+ More advanced users would want to integrate them into their build process or serve them from a web server, all of which is entirely possible. You just need to run the Scala.js compiler and place the output @code{.js} file somewhere your web server can pick it up, e.g. in some static-resource folder. We cover an example setup of this with a Scala webserver in our chapter @sect.ref("Integrating Client-Server").
@sect{Recap}
@@ -316,7 +316,7 @@
@li
Javascript includes APIs for @lnk("getting the size of the window", "http://stackoverflow.com/questions/1248081/get-the-browser-viewport-dimensions-with-javascript") and @lnk("changing the size of a canvas", "http://stackoverflow.com/questions/9251480/set-canvas-size-using-javascript"). These Javascript APIs are available in Scala.js, and we've already used some of them in the course of this example. Try making the Canvas full-screen, and re-positioning the corners of the triangle to match.
@li
- The @hl.scala{dom.CanvasRenderingContext2D} has a bunch of methods on it that can be used to draw things. Here we only draw 1x1 rectangles to put points on the canvas; try modifying the code to make it draw something else.
+ The @lnk.dom.CanvasRenderingContext2D has a bunch of methods on it that can be used to draw things. Here we only draw 1x1 rectangles to put points on the canvas; try modifying the code to make it draw something else.
@li
We've look at the @code{master} branch of @code{workbench-example-app}, but this project also has several other branches showing off different facets of Scala.js: @lnk("dodge-the-dots", "https://github.com/lihaoyi/workbench-example-app/tree/dodge-the-dots") and @lnk("space-invaders", "https://github.com/lihaoyi/workbench-example-app/tree/space-invaders") are both interesting branches worth playing with as a beginner. Check them out!
@li
diff --git a/book/src/main/scalatex/book/handson/PublishingModules.scalatex b/book/src/main/scalatex/book/handson/PublishingModules.scalatex
index 357cbf6..7c55cd0 100644
--- a/book/src/main/scalatex/book/handson/PublishingModules.scalatex
+++ b/book/src/main/scalatex/book/handson/PublishingModules.scalatex
@@ -65,7 +65,7 @@
In the @code{js/} version, we are using the Javascript @hl.javascript{Date} object to take the millis and do what we want. In the @code{jvm/} version, we instead use @hl.scala{java.text.SimpleDateFormat} with a custom formatter (The syntax is defined @lnk("here", "http://docs.oracle.com/javase/7/docs/api/java/text/SimpleDateFormat.html")).
@p
- Again, you can put as much platform-specific logic in these files as you want, to account for differences in the available APIs. Maybe you want to use @hl.scala{js.JSON.parse} for parsing JSON blobs in @code{js/}, but @hl.scala{Jackson} or @hl.scala{GSON} for parsing them in @code{jvm/}.
+ Again, you can put as much platform-specific logic in these files as you want, to account for differences in the available APIs. Maybe you want to use @lnk.dom.JSONparse for parsing JSON blobs in @code{js/}, but @lnk("Jackson", "http://jackson.codehaus.org/") or @lnk("GSON", "https://code.google.com/p/google-gson/") for parsing them in @code{jvm/}.
@p
Lastly, you'll also have noticed the two identical @hl.scala{main} methods in the platform-specific code. This is an implementation detail around the fact that Scala.js picks up the main method differently from Scala-JVM, using @hl.scala{js.JSApp} instead of looking for a @hl.scala{main(args: Array[String]): Unit} method. These two main methods allow us to test our implementations
@sect{Running the Module}
@@ -89,7 +89,7 @@
@li
The "Running on XXX!" statement which shows us we're actually running on two platforms.
@li
- The other hint is the time taken: the second run is instant while the first takes three seconds! This is because by default we run on @lnk("Rhino", "https://developer.mozilla.org/en-US/docs/Mozilla/Projects/Rhino"), which is a simple interpreter hundreds of times slower than running code natively on the JVM.
+ The other hint is the time taken: the second run is instant while the first takes three seconds! This is because by default we run on @lnk.misc.Rhino, which is a simple interpreter hundreds of times slower than running code natively on the JVM.
@li
In Scala-JVM the double 1.0 is printed as @code{1.0}, while in Scala.js it's printed as @code{1}. This is one of a small number of differences between Scala.js and Scala-JVM, and verifies that we are indeed running on both platforms!
@@ -190,7 +190,7 @@
@code{js/src/main/scala} Scala.js only code
@p
- It is entirely possible for your modules to have slightly different implementations and APIs on Scala.js and Scala-JVM. @lnk("Scalatags", "https://github.com/lihaoyi/scalatags") exposes additional DOM-related functionality only for it's Scala.js version, while @lnk("uPickle", "https://github.com/lihaoyi/upickle") uses different JSON libraries (@lnk("Jawn", "https://github.com/non/jawn") v.s. @lnk("DOM", "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse")) on the backend while the exposed interface remains the same. You have the flexibility to pick and choose which bits of your library you wish to share and which bits will be different.
+ It is entirely possible for your modules to have slightly different implementations and APIs on Scala.js and Scala-JVM. @lnk.github.Scalatags exposes additional DOM-related functionality only for it's Scala.js version, while @lnk.github.uPickle uses different JSON libraries (@lnk("Jawn", "https://github.com/non/jawn") v.s. @lnk("DOM", "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse")) on the backend while the exposed interface remains the same. You have the flexibility to pick and choose which bits of your library you wish to share and which bits will be different.
@p
Everything above also applies to your unit tests, which fall in @code{test/} folders mirroring the @code{main/} folders listed above. You can also choose to share or not-share your unit test code as you see fit. \ No newline at end of file
diff --git a/book/src/main/scalatex/book/handson/WebPage.scalatex b/book/src/main/scalatex/book/handson/WebPage.scalatex
index ef9ac6c..2bcdf1c 100644
--- a/book/src/main/scalatex/book/handson/WebPage.scalatex
+++ b/book/src/main/scalatex/book/handson/WebPage.scalatex
@@ -19,7 +19,6 @@
@div(id:="div1")
@script("HelloWorld0().main(document.getElementById('div1'))")
-
@p
This approach works, as the above example shows, but has a couple of disadvantages:
@@ -65,7 +64,7 @@
@script("Inputs().main(document.getElementById('div3'))")
@p
- In Scalatags, you build up fragments of type @hl.scala{Frag} using functions like @hl.scala{div}, @hl.scala{h1}, etc., and call @hl.scala{.render} on it to turn it into a real @hl.scala{dom.Element}. Different fragments render to different things: e.g. @hl.scala{input.render} gives you a @hl.scala{dom.HTMLInputElement}, @hl.scala{span.render} gives you a @hl.scala{dom.HTMLSpanElement}. You can then access the properties of these elements: adding callbacks, checking their value, anything you want.
+ In Scalatags, you build up fragments of type @hl.scala{Frag} using functions like @hl.scala{div}, @hl.scala{h1}, etc., and call @hl.scala{.render} on it to turn it into a real @lnk.dom.Element. Different fragments render to different things: e.g. @hl.scala{input.render} gives you a @lnk.dom.HTMLInputElement, @hl.scala{span.render} gives you a @lnk.dom.HTMLSpanElement. You can then access the properties of these elements: adding callbacks, checking their value, anything you want.
@p
In this example, we render and @hl.scala{input} element and a @hl.scala{span}, wire up the input to set the value of the span whenever you press a key in the input, and then stuff both of them into a larger HTML fragment that forms the contents of our @hl.scala{target} element.
@@ -88,7 +87,7 @@
Using a @hl.scala{for}-loop with a filter inside the Scalatags fragment is just normal Scala, since you can nest arbitrary Scala expressions inside a Scalatags snippet. In this case, we're converting both the fruit and the search query to lower case so we can compare them case-insensitively.
@p
- Lastly, we just need to define the input box and output-container (as we did earlier), set the @hl.scala{onkeyup} event handler, and place it in a larger fragment, and then into our target:
+ Lastly, we just need to define the input box and output-container (as we did earlier), set the @lnk.dom.onkeyup event handler, and place it in a larger fragment, and then into our target:
@div(cls:="pure-g")
@div(cls:="pure-u-1 pure-u-md-13-24")
@@ -180,7 +179,7 @@
@hl.ref("examples/demos/src/main/scala/webpage/Weather1.scala", "import dom", "val url =")
@p
- The first import brings in Scala adapters to several DOM APIs, which allow you to use them more idiomatically from Scala. The second brings in an implicit @hl.scala{ExecutionContext} that we'll need to run our asynchronous operations.
+ The first import brings in Scala adapters to several DOM APIs, which allow you to use them more idiomatically from Scala. The second brings in an implicit @hl.scala{scala.concurrent.ExecutionContext} that we'll need to run our asynchronous operations.
@p
Then we need the code itself:
diff --git a/book/src/main/scalatex/book/indepth/CompilationPipeline.scalatex b/book/src/main/scalatex/book/indepth/CompilationPipeline.scalatex
index 1080fc4..96cbb8b 100644
--- a/book/src/main/scalatex/book/indepth/CompilationPipeline.scalatex
+++ b/book/src/main/scalatex/book/indepth/CompilationPipeline.scalatex
@@ -78,12 +78,12 @@
@li
@b{Initial Compilation}: @code{.scala} files to @code{.class} and @code{.sjsir} files
@li
- @b{Optimization}: @code{.sjsir} files to smallish/fast @code{.js} files
+ @b{Fast Optimization}: @code{.sjsir} files to smallish/fast @code{.js} files
@li
- @b{Closure-Compiler}: @code{.js} files to smaller/faster @code{.js} files
+ @b{Full Optimization}: @code{.js} files to smaller/faster @code{.js} files
@p
- @code{.scala} files are the source code you're familiar with. @code{.class} files are the JVM-targetted artifacts which aren't used, but we keep around: tools such as IntelliJ or Eclipse use these files to provide IDE support for Scala.js code, even if they take no part in compilation. @code{.js} files are the output Javascript, which we can execute in a web browser.
+ @code{.scala} files are the source code you're familiar with. @code{.class} files are the JVM-targetted artifacts which aren't used, but we keep around: tools such as @lnk.misc.IntelliJ or @lnk.misc.Eclipse use these files to provide IDE support for Scala.js code, even if they take no part in compilation. @code{.js} files are the output Javascript, which we can execute in a web browser.
@p
@code{.sjsir} files are worth calling out: the name stands for "ScalaJS Intermediate Representation", and these files contain compiled code half-way between Scala and Javascript: most Scala features have by this point been replaced their Java/Javascript equivalents, but it still contains Types (which have all been inferred) that can aid in analysis. Many Scala.js specific optimizations take place on this IR.
@@ -97,7 +97,7 @@
@p
But produced far larger (20mb) and slower executables. This section will explore each stage and we'll learn what these stages do.
- @sect{Initial Compilation}
+ @sect{Compilation}
@p
As described earlier, the Scala.js compiler is implemented as a Scala compiler plugin, and lives in the main repository in @lnk("compiler/", "https://github.com/scala-js/scala-js/tree/master/compiler"). The bulk of the plugin runs after the @code{mixin} phase in the @lnk("Scala compilation pipeline", "http://stackoverflow.com/a/4528092/871202"). By this point:
@@ -135,7 +135,7 @@
@p
This is the only phase in the Scala.js compilation pipeline that separate compilation is possible: you can compile many different sets of Scala.js @code{.scala} files separately, only to combine them later. This is used e.g. for distributing Scala.js libraries as Maven Jars, which are compiled separately by library authors to be combined into a final executable later.
- @sect{Optimization}
+ @sect{Fast Optimization}
@p
This phase is a whole-program optimization of the @code{.sjsir} files, and lives in the @lnk("tools/", "https://github.com/scala-js/scala-js/tree/master/tools") folder of the Scala.js repository. The rough operations that get performed are:
@@ -150,7 +150,7 @@
@p
While the input for this phase is the aggregate @code{.sjsir} files from your project and all your dependencies, the output is executable Javascript. This phase usually runs in less than a second, outputs a Javascript blob in the 700kb-1mb range, and is suitable for repeated use during development. This corresponds to the @code{fastOptJS} command in SBT.
- @sect{Closure-Compiler}
+ @sect{Full Optimization}
@p
The @lnk("Google Closure Compiler", "https://developers.google.com/closure/compiler/") (GCC) is a set of tools that work with Javascript. It has multiple @lnk("levels of optimization", "https://developers.google.com/closure/compiler/docs/compilation_levels"), doing everything from basic whitespace-removal to heavy optimization. It is a old, relatively mature project that is relied on both inside and outside google to optimize the delivery of Javascript to the browser.
@p
diff --git a/book/src/main/scalatex/book/indepth/DesignSpace.scalatex b/book/src/main/scalatex/book/indepth/DesignSpace.scalatex
index 1285900..da17a4f 100644
--- a/book/src/main/scalatex/book/indepth/DesignSpace.scalatex
+++ b/book/src/main/scalatex/book/indepth/DesignSpace.scalatex
@@ -4,7 +4,7 @@
@sect("Why No Reflection?")
@p
- Scala.js prohibits reflection as it makes dead-code elimination difficult, and the compiler relies heavily on dead-code elimination to generate reasonably-sized executables. The chapter on the Compilation Pipeline goes into more detail of why, but a rough estimate of the effect of various optimizations on a small application is:
+ Scala.js prohibits reflection as it makes dead-code elimination difficult, and the compiler relies heavily on dead-code elimination to generate reasonably-sized executables. The chapter on @sect.ref("The Compilation Pipeline") goes into more detail of why, but a rough estimate of the effect of various optimizations on a small application is:
@ul
@li
@@ -38,7 +38,7 @@
}
@p
- When the Scala.js optimizer looks at this application, it is able to deduce certain things immediately:
+ When the @sect.ref("Fast Optimization", "Scala.js optimizer"), looks at this application, it is able to deduce certain things immediately:
@ul
@li
diff --git a/book/src/main/scalatex/book/indepth/SemanticDifferences.scalatex b/book/src/main/scalatex/book/indepth/SemanticDifferences.scalatex
index 4283c13..7bc1a41 100644
--- a/book/src/main/scalatex/book/indepth/SemanticDifferences.scalatex
+++ b/book/src/main/scalatex/book/indepth/SemanticDifferences.scalatex
@@ -60,7 +60,7 @@
// Scala.js: 1.0
@sect{Unit}
@p
- scala.Unit is represented using JavaScript's undefined. Therefore, calling toString() on Unit will return undefined rather than ().
+ scala.Unit is represented using JavaScript's undefined. Therefore, calling @hl.scala{toString()} on @hl.scala{Unit} will return @hl.scala{"undefined"} rather than @hl.scala{"()""}.
@sect{Strings}
@p
@@ -68,7 +68,7 @@
@sect{Reflection}
@p
- Java reflection and, a fortiori, Scala reflection, are not supported. There is limited support for java.lang.Class, e.g., obj.getClass.getName will work for any Scala.js object (not for objects that come from JavaScript interop).
+ Java reflection and, a fortiori, Scala reflection, are not supported. There is limited support for @lnk("java.lang.Class", "https://docs.oracle.com/javase/7/docs/api/java/lang/Class.html"), e.g., obj.getClass.getName will work for any Scala.js object (not for objects that come from JavaScript interop).
@sect{Exceptions}
@p
@@ -96,7 +96,7 @@
@sect{Enumerations}
@p
- The methods @hl.scala{Value()} and @hl.scala{Value(i: Int)} on @hl.scala{scala.Enumeration} use reflection to retrieve a string representation of the member name and are therefore -- in principle -- unsupported. However, since Enumerations are an integral part of the Scala library, Scala.js adds limited support for these two methods:
+ The methods @hl.scala{Value()} and @hl.scala{Value(i: Int)} on @lnk.scala{scala.Enumeration} use reflection to retrieve a string representation of the member name and are therefore -- in principle -- unsupported. However, since Enumerations are an integral part of the Scala library, Scala.js adds limited support for these two methods:
@p
Calls to either of these two methods of the forms:
@hl.scala
@@ -152,7 +152,7 @@
@p
You can use more-or-less the whole Scala standard library in Scala.js, sans some more esoteric components like the parallel collections or the tools. Furthermore, we've ported some subset of the Java standard library that many common Scala libraries depends on, including most of @hl.scala{java.lang.*} and some of @hl.scala{java.util.*}.
@p
- There isn't a full list of library APIs which are available from Scala.js, but a rough idea can be had from looking the the source code @lnk("on", "https://github.com/scala-js/scala-js/tree/master/javalanglib/src/main/scala/java/lang") @lnk("github", "https://github.com/scala-js/scala-js/tree/master/javalib/src/main/scala/java").
+ There isn't a full list of standard library library APIs which are available from Scala.js, but it should be enough to give you a rough idea of what is supported. The full list of classes that have been ported to Scala.js is available under @sect.ref{Available Java APIs}
@sect{Reflection v.s. Macros}
@tableHead
@@ -161,10 +161,10 @@
@td{@tuple._1}@td{@tuple._2}
@p
- As described @a("here"), in Reflection is not supported in Scala.js, due to the way it inhibits optimization. This doesn't just mean you can't use reflection yourself: many third-party libraries also use reflection, and you won't be able to use them either.
+ As described @sect.ref("Why No Reflection?", "here"), Reflection is not supported in Scala.js, due to the way it inhibits optimization. This doesn't just mean you can't use reflection yourself: many third-party libraries also use reflection, and you won't be able to use them either.
@p
- On the other hand, Scala.js does support Macros, and macros can in many ways substitute many of the use cases that people have traditionally used reflection for. For example, instead of using a reflection-based serialization library like @code{scala-pickling}, you can use a macro-based library such as @code{upickle}.
+ On the other hand, Scala.js does support Macros, and macros can in many ways substitute many of the use cases that people have traditionally used reflection for (see @sect.ref("Macros", "here"). For example, instead of using a reflection-based serialization library like @lnk.github.scalaPickling, you can use a macro-based library such as @lnk.github.uPickle.
@sect{Pure-Scala v.s. Java Libraries}
@tableHead
@@ -174,7 +174,7 @@
@p
Scala.js has access to any pure-Scala libraries that you have cross-compiled to Scala.js, and cross-compiling a pure-Scala library with no dependencies is straightforward. Many of them, such as the ones listed above, have already been cross-compiled and can be used via their maven coordinates.
@p
- You cannot use any libraries which have a Java dependency. This means libraries like Scalatest or Scalate, which depend on a number of external Java libraries or source files, cannot be used from Scala.js. You can only use libraries which have no dependency on Java libraries or sources.
+ You cannot use any libraries which have a Java dependency. This means libraries like @lnk.misc.ScalaTest or @lnk.misc.Scalate, which depend on a number of external Java libraries or source files, cannot be used from Scala.js. You can only use libraries which have no dependency on Java libraries or sources.
@sect{Javascript APIs v.s. JVM APIs}
@tableHead
@@ -185,7 +185,7 @@
@p
Apart from depending on Java sources, the other thing that you can't use in Scala.js are JVM-specific APIs. This means that anything which goes down to the underlying operating system, filesystem, GUI or network are unavailable in Scala.js. This makes sense when you consider that these capabilities are no provided by the browser which Scala.js runs in, and it's impossible to re-implement them ourselves.
@p
- In exchange for this, Scala.js provides you access to Browser APIs that do related things. Although you can't set up a HTTP server to take in-bound requests, you can make out-bound requests using @hl.scala{dom.XMLHttpRequest} to other servers. You can't write to the filesystem or databases directly, but you can write to the @hl.scala{dom.localStorage} provided by the browser. You can't use Swing or AWT or WebGL but instead work with the DOM and Canvas and WebGL.
+ In exchange for this, Scala.js provides you access to Browser APIs that do related things. Although you can't set up a HTTP server to take in-bound requests, you can make out-bound requests using @lnk.dom.XMLHttpRequest to other servers. You can't write to the filesystem or databases directly, but you can write to the @hl.scala{dom.localStorage} provided by the browser. You can't use Swing or AWT or WebGL but instead work with the DOM and Canvas and WebGL.
@p
Naturally, none of these are an exact replacement, as the browser environment is fundamentally different from that of a desktop application running on the JVM. Nonetheless, there are many analogues, and if so desired you can write code to abstract away these differences and run on both Scala.js and Scala-JVM
@@ -198,8 +198,8 @@
@p
- Lastly, there is the matter of tools. Naturally, all the Scala tools which depend on the JVM are out. This means things like the Yourkit, VisualVM and JProfiler profilers, as well as things like the Scala command-line REPL which relies on classloaders and other such things to run on the JVM
+ Lastly, there is the matter of tools. Naturally, all the Scala tools which depend on the JVM are out. This means things like the @lnk("Yourkit", "http://www.yourkit.com/"), @lnk("VisualVM", "http://visualvm.java.net/") and @lnk("JProfiler", "https://www.ej-technologies.com/products/jprofiler/overview.html") profilers, as well as things like the Scala command-line REPL which relies on classloaders and other such things to run on the JVM
@p
- On the other hand, you do get to keep and continue using many tools which are build for Scala but JVM-agnostic. For example, IDEs such a IntelliJ and Eclipse work great with Scala.js; from their point of view, it's just Scala, and things like code-navigation, refactoring and error-highlighting all work out of the box. SBT works with Scala.js too, and you see the same compile-erorrs in the command-line as you would in vanilla Scala, and even things like incremental compilation work un-changed.
+ On the other hand, you do get to keep and continue using many tools which are build for Scala but JVM-agnostic. For example, IDEs such a @lnk.misc.IntelliJ and @lnk.misc.Eclipse work great with Scala.js; from their point of view, it's just Scala, and things like code-navigation, refactoring and error-highlighting all work out of the box. SBT works with Scala.js too, and you see the same compile-erorrs in the command-line as you would in vanilla Scala, and even things like incremental compilation work un-changed.
@p
Lastly, you gain access to browser tools that don't work with normal Scala: you can use the Chrome or Firefox consoles to poke at your Scala.js application from the command line, or their profilers/debuggers. With source maps set up, you can even step-through debug your Scala.js application directly in Chrome.
diff --git a/examples/demos/Controller.scala b/examples/demos/Controller.scala
index d27c68e..c32c293 100644
--- a/examples/demos/Controller.scala
+++ b/examples/demos/Controller.scala
@@ -24,11 +24,10 @@ object Controller{
val structure = upickle.readJs[Node](upickle.json.readJs(data))
- val main = dom.document.getElementById("main")
- val menu = dom.document.getElementById("menu")
+ val Seq(main, menu, layout, menuLink) = Seq(
+ "main", "menu", "layout", "menuLink"
+ ).map(dom.document.getElementById)
- val layout = dom.document.getElementById("layout")
- val menuLink = dom.document.getElementById("menuLink")
val snippets = dom.document.getElementsByClassName("highlight-me")
snippets.foreach(js.Dynamic.global.hljs.highlightBlock(_))
@@ -54,12 +53,7 @@ object Controller{
}
structure.children.flatMap(rec(_, 0))
}
- def menuItems = {
- def rec(current: Node): Seq[String] = {
- current.name +: current.children.flatMap(rec)
- }
- rec(structure).tail
- }
+
val frag = div(cls:="pure-menu pure-menu-open")(
a(cls:="pure-menu-heading", href:="#")(
"Contents"
@@ -70,14 +64,23 @@ object Controller{
)
menu.appendChild(frag.render)
- def offset(el: dom.HTMLElement, parent: dom.HTMLElement): Double = {
- if (el == parent) 0
- else el.offsetTop + offset(el.offsetParent.asInstanceOf[dom.HTMLElement], parent)
+
+ val headers = {
+ def offset(el: dom.HTMLElement, parent: dom.HTMLElement): Double = {
+ if (el == parent) 0
+ else el.offsetTop + offset(el.offsetParent.asInstanceOf[dom.HTMLElement], parent)
+ }
+ val menuItems = {
+ def rec(current: Node): Seq[String] = {
+ current.name +: current.children.flatMap(rec)
+ }
+ rec(structure).tail
+ }
+ menuItems.map(munge)
+ .map(dom.document.getElementById)
+ .map(offset(_, main))
+ .toArray
}
- val headers = menuItems.map(munge)
- .map(dom.document.getElementById)
- .map(offset(_, main))
- .toArray
scrollSpy(main, headers, contentBar)
@@ -85,14 +88,18 @@ object Controller{
toggleClass(layout, "active")
toggleClass(menu, "active")
toggleClass(menuLink, "active")
- };
+ }
}
-
-
+ /**
+ * Needs to be done in a sketchy imperative fashion for performance:
+ * onscroll gets called quite a lot, so any additional work makes it
+ * noticeable jerky
+ */
def scrollSpy(main: dom.HTMLElement,
headers: Seq[Double],
contentBar: Seq[dom.HTMLElement]) = {
+
def isElementInViewport(el: dom.HTMLElement) = {
val rect = el.getBoundingClientRect()
rect.top >= 0 && rect.bottom <= dom.innerHeight
diff --git a/examples/demos/src/main/scala/canvasapp/FlappyLine.scala b/examples/demos/src/main/scala/canvasapp/FlappyLine.scala
index 57a1f2e..02cb91b 100644
--- a/examples/demos/src/main/scala/canvasapp/FlappyLine.scala
+++ b/examples/demos/src/main/scala/canvasapp/FlappyLine.scala
@@ -11,7 +11,6 @@ object FlappyLine extends{
@JSExport
def main(canvas: dom.HTMLCanvasElement) = {
/*setup*/
-
val renderer = canvas.getContext("2d")
.asInstanceOf[dom.CanvasRenderingContext2D]