From 7006cebd061784900233c405ce03665151b86010 Mon Sep 17 00:00:00 2001 From: Li Haoyi Date: Thu, 12 Mar 2015 20:13:25 +0800 Subject: better scrolling --- book/src/main/resources/css/side-menu.css | 6 +- book/src/main/scalatex/Index.scalatex | 83 ++++++ book/src/main/scalatex/Intro.scalatex | 183 ++++++++++++ book/src/main/scalatex/book/Index.scalatex | 83 ------ book/src/main/scalatex/book/Intro.scalatex | 183 ------------ .../main/scalatex/book/handson/CanvasApp.scalatex | 211 ------------- .../scalatex/book/handson/ClientServer.scalatex | 271 ----------------- .../scalatex/book/handson/CommandLine.scalatex | 184 ------------ .../scalatex/book/handson/GettingStarted.scalatex | 332 --------------------- .../book/handson/PublishingModules.scalatex | 150 ---------- .../main/scalatex/book/handson/WebPage.scalatex | 269 ----------------- .../book/indepth/AdvancedTechniques.scalatex | 304 ------------------- .../book/indepth/CompilationPipeline.scalatex | 210 ------------- .../scalatex/book/indepth/DesignSpace.scalatex | 241 --------------- .../main/scalatex/book/indepth/JavaAPIs.scalatex | 46 --- .../book/indepth/JavascriptInterop.scalatex | 1 - .../book/indepth/SemanticDifferences.scalatex | 267 ----------------- book/src/main/scalatex/handson/CanvasApp.scalatex | 211 +++++++++++++ .../main/scalatex/handson/ClientServer.scalatex | 271 +++++++++++++++++ .../src/main/scalatex/handson/CommandLine.scalatex | 184 ++++++++++++ .../main/scalatex/handson/GettingStarted.scalatex | 332 +++++++++++++++++++++ .../scalatex/handson/PublishingModules.scalatex | 150 ++++++++++ book/src/main/scalatex/handson/WebPage.scalatex | 269 +++++++++++++++++ .../scalatex/indepth/AdvancedTechniques.scalatex | 304 +++++++++++++++++++ .../scalatex/indepth/CompilationPipeline.scalatex | 210 +++++++++++++ .../src/main/scalatex/indepth/DesignSpace.scalatex | 241 +++++++++++++++ book/src/main/scalatex/indepth/JavaAPIs.scalatex | 46 +++ .../scalatex/indepth/JavascriptInterop.scalatex | 1 + .../scalatex/indepth/SemanticDifferences.scalatex | 267 +++++++++++++++++ build.sbt | 4 +- examples/crossBuilds/clientserver/build.sbt | 4 +- .../crossBuilds/clientserver/project/build.sbt | 2 +- examples/crossBuilds/clientserver2/build.sbt | 6 +- .../crossBuilds/clientserver2/project/build.sbt | 2 +- examples/demos/build.sbt | 6 +- .../src/main/scala/scrollmenu/Controller.scala | 2 +- .../src/main/scala/scrollmenu/ScrollSpy.scala | 33 +- project/build.sbt | 2 +- 38 files changed, 2780 insertions(+), 2791 deletions(-) create mode 100644 book/src/main/scalatex/Index.scalatex create mode 100644 book/src/main/scalatex/Intro.scalatex delete mode 100644 book/src/main/scalatex/book/Index.scalatex delete mode 100644 book/src/main/scalatex/book/Intro.scalatex delete mode 100644 book/src/main/scalatex/book/handson/CanvasApp.scalatex delete mode 100644 book/src/main/scalatex/book/handson/ClientServer.scalatex delete mode 100644 book/src/main/scalatex/book/handson/CommandLine.scalatex delete mode 100644 book/src/main/scalatex/book/handson/GettingStarted.scalatex delete mode 100644 book/src/main/scalatex/book/handson/PublishingModules.scalatex delete mode 100644 book/src/main/scalatex/book/handson/WebPage.scalatex delete mode 100644 book/src/main/scalatex/book/indepth/AdvancedTechniques.scalatex delete mode 100644 book/src/main/scalatex/book/indepth/CompilationPipeline.scalatex delete mode 100644 book/src/main/scalatex/book/indepth/DesignSpace.scalatex delete mode 100644 book/src/main/scalatex/book/indepth/JavaAPIs.scalatex delete mode 100644 book/src/main/scalatex/book/indepth/JavascriptInterop.scalatex delete mode 100644 book/src/main/scalatex/book/indepth/SemanticDifferences.scalatex create mode 100644 book/src/main/scalatex/handson/CanvasApp.scalatex create mode 100644 book/src/main/scalatex/handson/ClientServer.scalatex create mode 100644 book/src/main/scalatex/handson/CommandLine.scalatex create mode 100644 book/src/main/scalatex/handson/GettingStarted.scalatex create mode 100644 book/src/main/scalatex/handson/PublishingModules.scalatex create mode 100644 book/src/main/scalatex/handson/WebPage.scalatex create mode 100644 book/src/main/scalatex/indepth/AdvancedTechniques.scalatex create mode 100644 book/src/main/scalatex/indepth/CompilationPipeline.scalatex create mode 100644 book/src/main/scalatex/indepth/DesignSpace.scalatex create mode 100644 book/src/main/scalatex/indepth/JavaAPIs.scalatex create mode 100644 book/src/main/scalatex/indepth/JavascriptInterop.scalatex create mode 100644 book/src/main/scalatex/indepth/SemanticDifferences.scalatex diff --git a/book/src/main/resources/css/side-menu.css b/book/src/main/resources/css/side-menu.css index ed06b5f..dafa1b5 100755 --- a/book/src/main/resources/css/side-menu.css +++ b/book/src/main/resources/css/side-menu.css @@ -1,10 +1,8 @@ #main{ - position: fixed; max-width: 100%; right: 0px; left: 0px; - height: 100%; overflow-y: scroll } @@ -200,8 +198,8 @@ Hides the menu at `48em`, but modify this based on your app's needs. padding-left: 250px; /* left col width "#menu" */ left: 0; } - #main{ - left: 250px; + #main-box{ + padding-left: 250px; } #menu { left: 250px; diff --git a/book/src/main/scalatex/Index.scalatex b/book/src/main/scalatex/Index.scalatex new file mode 100644 index 0000000..e9f65bb --- /dev/null +++ b/book/src/main/scalatex/Index.scalatex @@ -0,0 +1,83 @@ +@import book.BookData._ + +@val firstHalfDescription = Seq[Frag](""" +is a set of tutorials that walks you through getting started with Scala.js. You'll build a range of small projects, from """, sect.ref("Making a Canvas App"), " to ", sect.ref("Interactive Web Pages"), " to ", sect.ref("Integrating Client-Server"), """, and in the process will get a good overview of both Scala.js's use cases as well as the development experience +""") +@val secondHalfDescription = Seq(""" +is a set of detailed expositions on various parts of the Scala.js platform. Nothing in here is necessary for you to make your first demos, but as you dig deeper into the platform, you will likely need or want to care about these things so you can properly understand what's going on "under the hood" +""") +@sect("Hands-on Scala.js", "Writing client-side web applications in Scala") + @split + @more + @hl.ref(wd/'examples/'demos/'src/'main/'scala/"Splash.scala", "var x") + + @less + @BookData.example(canvas, "Splash().main") + + @p + @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. This book is an introduction to Scala.js, which aims to get you from knowing-nothing about it to being relatively proficient. + + @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 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. Feel free to explore the navigation bar on the left to find chapters of interest. + + @p + Even if we do not require any familiarity of Scala.js, this book nonetheless assumes a good amount of background knowledge: of Scala, of Javascript, and of web development as a whole. In general, you will not need deep knowledge of any of these subjects, though if you are coming in entirely without knowledge of any one of them, you'll have to be willing to spend time Google-ing things and picking things up as we go along. Someone who comes in without previous web-dev experience may miss or not-notice many of the nice touches and benefits that Scala.js brings to the table, having never done web-dev any other way, + + @p + Many of the code samples are taken from examples available on the book's @lnk("Github Page", "https://github.com/lihaoyi/scala-js-book"); for those code samples (e.g. the animation above), there is a @i(cls:="fa fa-link ") link in the top-right corner of the snippet that you can click on to go to the original code. These come in handy if you find you need additional context around the snippet, e.g. what imports you need for the code to work, or what the complete executable example looks like. + + @p + This book is roughly divided into two sections: + + @ul + @li + @sect.ref{Hands On} @firstHalfDescription + @li + @sect.ref{In Depth} @secondHalfDescription + + @p + Feel free to jump ahead to either of them if you have some prior exposure to Scala.js. If not, it is best to start with the introduction... + +@sect{Intro to Scala.js} + @Intro() + +@sect("Hands On", "Writing your first Scala.js programs") + @p + This half of the book @firstHalfDescription + + @sect{Getting Started} + @handson.GettingStarted() + + @sect{Making a Canvas App} + @handson.CanvasApp() + + @sect{Interactive Web Pages} + @handson.WebPage() + + @sect{The Command Line} + @handson.CommandLine() + + @sect{Cross Publishing Libraries} + @handson.PublishingModules() + + @sect{Integrating Client-Server} + @handson.ClientServer() + +@sect("In Depth", "Exploring Scala.js") + @p + This half of the book @secondHalfDescription + + @sect{Advanced Techniques} + @indepth.AdvancedTechniques() + + @sect{Deviations from Scala-JVM} + @indepth.SemanticDifferences() + + @sect{The Compilation Pipeline} + @indepth.CompilationPipeline() + + @sect{Scala.js' Design Space} + @indepth.DesignSpace() + + @sect{Java APIs} + @indepth.JavaAPIs() diff --git a/book/src/main/scalatex/Intro.scalatex b/book/src/main/scalatex/Intro.scalatex new file mode 100644 index 0000000..b72b6e2 --- /dev/null +++ b/book/src/main/scalatex/Intro.scalatex @@ -0,0 +1,183 @@ +@import book.BookData._ +@p + Scala.js compiles Scala code to equivalent, executable Javascript. Here's the compilation of a trivial hello-world example: + +@split + @half + @hl.scala + object Main extends js.JSApp{ + def main() = { + var x = 0 + while(x < 10) x += 3 + println(x) + // 12 + } + } + + @half + @hl.javascript + ScalaJS.c.LMain$.prototype.main__V = (function() { + var x = 0; + while ((x < 10)) { + x = ((x + 3) | 0) + }; + ScalaJS.m.s_Predef$() + .println__O__V(x) + // 12 + }); + +@p + As you can see, both of the above programs do identical things: they'll count the variable @hl.scala{x} from @hl.scala{0}, @hl.scala{3}, @hl.scala{9}, and @hl.scala{12} before finally printing it out. It's just that the first is written in Scala and the second is in Javascript. + +@p + Traditionally, Scala has been a language which runs on the JVM. This eliminates it from consideration in many cases, e.g. when you need to build interactive web apps, the browser-client only runs Javascript. Even if your back-end is all written in Scala, you need to fall back to Javascript to run your client-side code, at a great loss in terms of toolability and maintainability. Scala.js lets you to develop web applications with the safety and toolability that comes with a statically typed language: + +@ul + @li + Typo-safety due to its compiler which catches many silly errors before the code is run + @li + In-editor support for autocomplete, error-highlighting, refactors, and intelligent navigation + @li + Moderate sized compiled executables, in the 100-400kb range + @li + Source-maps for ease of debugging + +@p + The value proposition is that due to the superior language and tooling, writing a web application in Scala.js will result in a codebase that is more flexible and robust than an equivalent application written in Javascript. The hope is that the benefits of using Scala.js will outweigh the additional (non-trivial) messiness of adding a whole new toolchain, as compared to directly writing raw Javascript. + +@p + I won't spend time on a detailed discussion on why Scala is good or why Javascript is bad; people's opinions on both sides can be found on the internet. The assumption is, going in, that you either already know and like Scala, or you are familiar with Javascript and are willing to try something new. + +@sect{About Javascript} + @p + Javascript is the language supported by web browsers, and is the only language available if you wish to write interactive web applications. As more and more activity moves online, the importance of web apps will only increase over time. Adobe Flash, Java Applets and Silverlight (which have historically allowed browser-client development in other languages) are all but dead: historically they have been the source of security vulnerabilities, none of them are available on the mobile browsers of Android or iOS or Windows8+. That leaves Javascript. + + @sect{Javascript-the-language} + @p + Javascript is an OK language to do small-scale development: an animation here, an on-click transition there. There are a number of warts in the language, e.g. its verbosity, and a large amount of surprising behavior, but while your code-base doesn't extend past a few hundred lines of code, you often will not mind or care. + + @p + However, Javascript is not an easy language to work in at scale: when your code-base extends to thousands, tens or hundreds of thousands of lines of code. The un-typed nature of the language, which is fine for small applications, becomes an issue when you are mainly working with code that you did not write. + + @p + In a large code-base, finding out what methods or properties a variable has is often a long chase through dozens of files to see how it ended up being passed to the current function. Refactorings, which are OK when you can just test the code to see if it works, become dangerous when your code base is large enough that "just test all the code" would take hours. Language-warts which are slightly annoying in small programs become a minefield in large ones: it's only a matter of time before you hit one, often in code you did-not/cannot test, resulting in breakages in production. + + @p + Apart from the inherent danger of the language, Javascript has another major problem: the language has left many things unspecified, yet at the same time provides the ability to emulate these things in a variety of ways. This means that rather than having a single way of e.g. defining a class and instantiating an object, there is a decade-long debate between a dozen different and equally-bad, hand-crafted alternatives. Large code-bases use third-party libraries, and most are guaranteed (purely due to how stastistics work) to do these basic things differently from your own code, making understanding these disparate code-bases (e.g. when something goes wrong) very difficult. + + @p + To work in Javascript, you need the discipline to limit yourself to the sane subset of the language, avoiding all the pitfalls along the way: + + @img(src:="images/javascript-the-good-parts-the-definitive-guide.jpg", margin.auto, display.block) + + @p + Even if you manage to do so, what constitutes a pitfall and what constitutes a clever-language-feature changes yearly, making it difficult to maintain cohesiveness over time. This is compounded by the fact that refactoring is difficult, and so removing "unwanted" patterns from a large code-base a difficult (often multi-year) process. + + @sect{Javascript-the-platform} + @p + However, even though Javascript-the-language is pretty bad, Javascript-the-platform has some very nice properties that make it a good target for application developers: + + @ul + @li + Zero-install distribution: just go to a URL and have the application downloaded and ready to use. + @li + Hyperlinks: being able to link to a particular page or item within a web app is a feature other platforms lack, and makes it much easier to cross-reference between different systems + @li + Sandboxed security: web applications are secure by default. No matter how sketchy the websites you visit, you can be sure that once you close the page, they're gone + + @p + These features are all very nice to have, and together have made the web platform the success it is today. When you compare it to traditional applications, you can see the draw: + @ul + @li + Installing traditional desktop applications is usually a several-minute-long process. If something goes wrong in the installation, that often leaves a botched half-install on your computer which makes installing, uninstalling, or running the program impossible without manual surgery to excise the broken files. + @li + Desktop applications generally do not talk to each other at all. While on the web you can easily link a page to someone, trying to get someone to a particular screen in desktop software often involves a chain of screenshots with detailed instructions of which buttons to click at each stage. + @li + Desktop application security is non-existent. Install one rogue application and it can take over your computer, steal your credit card number, use your email for sending spam, and all sorts of other nasty things. Removing these for-good sometimes involves re-installing your entire operating system. Hence people are much more wary about only installing desktop software from people they "trust". + + @p + In many ways, mobile App platforms like Android and iOS have closed the gap between "native" and "web" applications. Installing a new App may take 30 seconds, you can often deep-link to certain pages within an App, and Apps have a much tighter security model than desktop software does. Nevertheless, 30 seconds is still much longer than the 0.5 seconds it takes to open a web page, deep-linking in apps is not very prevalent, and the security model still often leaves space for rogue Apps to misbehave and steal data. + + @hr + @p + Despite the problems with Javascript (and other tools like HTML an CSS, which have their own problems) the Web platform got a lot of things right, and the Desktop and Mobile platforms have a lot of catching up to do. If only we could improve upon the parts that aren't so great. This is where Scala.js comes in. + +@sect{About Scala.js} + + @p + With Scala.js, you can cross compile your Scala code to a Javascript executable that can run on all major web browsers. You get all the benefits of the web platform in terms of deployability, security, and hyperlinking, with none of the problems of writing your software in Javascript. Scala.js provides a @sect.ref("The Language", "better language") to do your work in, but also provides some other goodies that have in-so-far never been seen in mainstream web development: @sect.ref("Sharing Code", "shared-code") and @sect.ref("Client-Server Integration", "client-server integration"). + + @sect{The Language} + @p + At a first approximation, Scala.js provides you a sane language to do development in the web browser. This saves you from an endless stream of Javascript warts like this one: + + @hl.javascript + javascript> ["10", "10", "10", "10"].map(parseInt) + [10, NaN, 2, 3] // WTF + + @hl.scala + scala> List("10", "10", "10", "10").map(parseInt) + List(10, 10, 10, 10) // Yay! + + + @p + Not only do you have an expressive language with static types, you also have great tooling with IDEs like IntelliJ and Eclipse, a rich library of standard collections, and many other modern conveniences that we take for granted but are curiously missing when working in the wild west of web development: the browser! You get all of the upside of developing for the web platform. + + @p + 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: @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 + Shared code is one of the holy-grails of web development. Traditionally the client-side code and server-side code has been written in separate languages: PHP or Perl or Python or Ruby or Java on the server, with only Javascript on the client. This means that algorithms were often implemented twice, constants copied-&-pasted, or awkward Ajax calls are made in an attempt to centralize the logic in one place (the server). With the advent of Node.js in the last few years, you can finally re-use the same code on the server as you can on the client, but with the cost of having all the previously client-only @sect.ref("Javascript-the-language", "problems with Javascript") now inflicted upon your server code base. Node.js expanded your range-of-options for writing shared client/server logic from "Write everything twice" to "Write everything twice, or write everything in Javascript". More options is always good, but it's not clear which of the two choices is more painful! + + @p + Scala.js provides an alternative to this dilemma. With Scala.js, you can utilize the same libraries you use writing your Scala servers when writing your Scala web clients! On one end, you are sharing your templating language with @lnk("Scalatags", "https://github.com/lihaoyi/scalatags") or sharing your serialization logic with @lnk("uPickle", "https://github.com/lihaoyi/upickle"). At the other, you are sharing large, abstract libraries like @lnk("Scalaz", "https://github.com/japgolly/scalaz") or @lnk("Shapeless", "https://groups.google.com/forum/#!searchin/scala-js/shapeless/scala-js/5Sf2up0z3PU/9F9SYB0qHEcJ"). + + @p + Sharing code means several things: + + @ul + @li + Not having to find two libraries to do a particular common task + @li + Not having to re-learn two different ways of doing the exact same thing + @li + Not needing to implement the same algorithms twice, for the times you can't find a good library to do what you want + @li + Not having to debug problems caused by subtle differences in the two implementations + @li + 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 @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 even split of downloads from people using it on both platforms: + + @img(src:="images/Scalatags Downloads.png", width:="100%") + + @p + Shared code means that if you, as an application writer, want some logic to be available on both the client and server, you simply @sect.ref("A Client-Server Setup", "put it in a shared/ folder"), and that's the end of the discussion. No architectural patterns to follow, no clever techniques need to be involved. Shared logic, whether that means constants, functions, data structures, all the way to algorithms and entire libraries, can simply be placed in @code{shared/} and be instantly accessible from both your client-side web code and your server. + + @p + Shared code has long been the holy-grail of web development. Even now, people speak of shared code as if it were a myth. With Scala.js, shared code is the simple, boring reality. And all this while, just as importantly, you don't need to re-write your large enterprise back-end systems in a language that doesn't scale well beyond 100s of lines of code. + + @sect{Client-Server Integration} + @p + There is an endless supply of new platforms which have promised to change-the-way-we-do-web-development-forever. From old-timers like @lnk("Ur-Web", "http://www.impredicative.com/ur/"), to @lnk("GWT", "http://www.gwtproject.org/"), to Asana's @lnk("LunaScript", "https://asana.com/luna"), to more recently things like @lnk("Meteor.js", "https://www.meteor.com/"). + @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 @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. It becomes @sect.ref("What's Left?", "impossible to make a malformed Ajax call"). + @p + There's a lot to be said for automating things using a computer. The entire field of software engineering is basically about automating tasks that were previously done manually: accounting, banking, making travel arrangements, and all that. However, in the world of web-development, there has always been one set of tasks that has traditionally be done manually: the task of ensuring the web-clients are properly synchronized with the web-servers. Communication between the two has always been a manual, tedious, error-prone process, and mistakes often end un-noticed until something breaks in production. + @p + With Scala.js, like the other experimental platforms that have come before us, we attempt to provide a way forward from this manual-tedium. + + @hr + + @p + In many ways, Scala.js all-at-once provides many of the traditional holy-grails of web development: People have always dreamed about doing web development in a sane, best-of-breed language that compiles to both client and server. Of not having to worry too hard about whether code goes on the client or on the server, and being able to move or share it if necessary. Of having a compiler that will verify and check that your entire system is correct. + + @p + Scala.js provides all these things, and much more. If you're interested enough to want to make use of Scala.js, read on! diff --git a/book/src/main/scalatex/book/Index.scalatex b/book/src/main/scalatex/book/Index.scalatex deleted file mode 100644 index e1a7f69..0000000 --- a/book/src/main/scalatex/book/Index.scalatex +++ /dev/null @@ -1,83 +0,0 @@ -@import BookData._ - -@val firstHalfDescription = Seq[Frag](""" -is a set of tutorials that walks you through getting started with Scala.js. You'll build a range of small projects, from """, sect.ref("Making a Canvas App"), " to ", sect.ref("Interactive Web Pages"), " to ", sect.ref("Integrating Client-Server"), """, and in the process will get a good overview of both Scala.js's use cases as well as the development experience -""") -@val secondHalfDescription = Seq(""" -is a set of detailed expositions on various parts of the Scala.js platform. Nothing in here is necessary for you to make your first demos, but as you dig deeper into the platform, you will likely need or want to care about these things so you can properly understand what's going on "under the hood" -""") -@sect("Hands-on Scala.js", "Writing client-side web applications in Scala") - @split - @more - @hl.ref(wd/'examples/'demos/'src/'main/'scala/"Splash.scala", "var x") - - @less - @BookData.example(canvas, "Splash().main") - - @p - @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. This book is an introduction to Scala.js, which aims to get you from knowing-nothing about it to being relatively proficient. - - @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 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. Feel free to explore the navigation bar on the left to find chapters of interest. - - @p - Even if we do not require any familiarity of Scala.js, this book nonetheless assumes a good amount of background knowledge: of Scala, of Javascript, and of web development as a whole. In general, you will not need deep knowledge of any of these subjects, though if you are coming in entirely without knowledge of any one of them, you'll have to be willing to spend time Google-ing things and picking things up as we go along. Someone who comes in without previous web-dev experience may miss or not-notice many of the nice touches and benefits that Scala.js brings to the table, having never done web-dev any other way, - - @p - Many of the code samples are taken from examples available on the book's @lnk("Github Page", "https://github.com/lihaoyi/scala-js-book"); for those code samples (e.g. the animation above), there is a @i(cls:="fa fa-link ") link in the top-right corner of the snippet that you can click on to go to the original code. These come in handy if you find you need additional context around the snippet, e.g. what imports you need for the code to work, or what the complete executable example looks like. - - @p - This book is roughly divided into two sections: - - @ul - @li - @sect.ref{Hands On} @firstHalfDescription - @li - @sect.ref{In Depth} @secondHalfDescription - - @p - Feel free to jump ahead to either of them if you have some prior exposure to Scala.js. If not, it is best to start with the introduction... - -@sect{Intro to Scala.js} - @Intro() - -@sect("Hands On", "Writing your first Scala.js programs") - @p - This half of the book @firstHalfDescription - - @sect{Getting Started} - @handson.GettingStarted() - - @sect{Making a Canvas App} - @handson.CanvasApp() - - @sect{Interactive Web Pages} - @handson.WebPage() - - @sect{The Command Line} - @handson.CommandLine() - - @sect{Cross Publishing Libraries} - @handson.PublishingModules() - - @sect{Integrating Client-Server} - @handson.ClientServer() - -@sect("In Depth", "Exploring Scala.js") - @p - This half of the book @secondHalfDescription - - @sect{Advanced Techniques} - @indepth.AdvancedTechniques() - - @sect{Deviations from Scala-JVM} - @indepth.SemanticDifferences() - - @sect{The Compilation Pipeline} - @indepth.CompilationPipeline() - - @sect{Scala.js' Design Space} - @indepth.DesignSpace() - - @sect{Java APIs} - @indepth.JavaAPIs() diff --git a/book/src/main/scalatex/book/Intro.scalatex b/book/src/main/scalatex/book/Intro.scalatex deleted file mode 100644 index c9248f8..0000000 --- a/book/src/main/scalatex/book/Intro.scalatex +++ /dev/null @@ -1,183 +0,0 @@ -@import BookData._ -@p - Scala.js compiles Scala code to equivalent, executable Javascript. Here's the compilation of a trivial hello-world example: - -@split - @half - @hl.scala - object Main extends js.JSApp{ - def main() = { - var x = 0 - while(x < 10) x += 3 - println(x) - // 12 - } - } - - @half - @hl.javascript - ScalaJS.c.LMain$.prototype.main__V = (function() { - var x = 0; - while ((x < 10)) { - x = ((x + 3) | 0) - }; - ScalaJS.m.s_Predef$() - .println__O__V(x) - // 12 - }); - -@p - As you can see, both of the above programs do identical things: they'll count the variable @hl.scala{x} from @hl.scala{0}, @hl.scala{3}, @hl.scala{9}, and @hl.scala{12} before finally printing it out. It's just that the first is written in Scala and the second is in Javascript. - -@p - Traditionally, Scala has been a language which runs on the JVM. This eliminates it from consideration in many cases, e.g. when you need to build interactive web apps, the browser-client only runs Javascript. Even if your back-end is all written in Scala, you need to fall back to Javascript to run your client-side code, at a great loss in terms of toolability and maintainability. Scala.js lets you to develop web applications with the safety and toolability that comes with a statically typed language: - -@ul - @li - Typo-safety due to its compiler which catches many silly errors before the code is run - @li - In-editor support for autocomplete, error-highlighting, refactors, and intelligent navigation - @li - Moderate sized compiled executables, in the 100-400kb range - @li - Source-maps for ease of debugging - -@p - The value proposition is that due to the superior language and tooling, writing a web application in Scala.js will result in a codebase that is more flexible and robust than an equivalent application written in Javascript. The hope is that the benefits of using Scala.js will outweigh the additional (non-trivial) messiness of adding a whole new toolchain, as compared to directly writing raw Javascript. - -@p - I won't spend time on a detailed discussion on why Scala is good or why Javascript is bad; people's opinions on both sides can be found on the internet. The assumption is, going in, that you either already know and like Scala, or you are familiar with Javascript and are willing to try something new. - -@sect{About Javascript} - @p - Javascript is the language supported by web browsers, and is the only language available if you wish to write interactive web applications. As more and more activity moves online, the importance of web apps will only increase over time. Adobe Flash, Java Applets and Silverlight (which have historically allowed browser-client development in other languages) are all but dead: historically they have been the source of security vulnerabilities, none of them are available on the mobile browsers of Android or iOS or Windows8+. That leaves Javascript. - - @sect{Javascript-the-language} - @p - Javascript is an OK language to do small-scale development: an animation here, an on-click transition there. There are a number of warts in the language, e.g. its verbosity, and a large amount of surprising behavior, but while your code-base doesn't extend past a few hundred lines of code, you often will not mind or care. - - @p - However, Javascript is not an easy language to work in at scale: when your code-base extends to thousands, tens or hundreds of thousands of lines of code. The un-typed nature of the language, which is fine for small applications, becomes an issue when you are mainly working with code that you did not write. - - @p - In a large code-base, finding out what methods or properties a variable has is often a long chase through dozens of files to see how it ended up being passed to the current function. Refactorings, which are OK when you can just test the code to see if it works, become dangerous when your code base is large enough that "just test all the code" would take hours. Language-warts which are slightly annoying in small programs become a minefield in large ones: it's only a matter of time before you hit one, often in code you did-not/cannot test, resulting in breakages in production. - - @p - Apart from the inherent danger of the language, Javascript has another major problem: the language has left many things unspecified, yet at the same time provides the ability to emulate these things in a variety of ways. This means that rather than having a single way of e.g. defining a class and instantiating an object, there is a decade-long debate between a dozen different and equally-bad, hand-crafted alternatives. Large code-bases use third-party libraries, and most are guaranteed (purely due to how stastistics work) to do these basic things differently from your own code, making understanding these disparate code-bases (e.g. when something goes wrong) very difficult. - - @p - To work in Javascript, you need the discipline to limit yourself to the sane subset of the language, avoiding all the pitfalls along the way: - - @img(src:="images/javascript-the-good-parts-the-definitive-guide.jpg", margin.auto, display.block) - - @p - Even if you manage to do so, what constitutes a pitfall and what constitutes a clever-language-feature changes yearly, making it difficult to maintain cohesiveness over time. This is compounded by the fact that refactoring is difficult, and so removing "unwanted" patterns from a large code-base a difficult (often multi-year) process. - - @sect{Javascript-the-platform} - @p - However, even though Javascript-the-language is pretty bad, Javascript-the-platform has some very nice properties that make it a good target for application developers: - - @ul - @li - Zero-install distribution: just go to a URL and have the application downloaded and ready to use. - @li - Hyperlinks: being able to link to a particular page or item within a web app is a feature other platforms lack, and makes it much easier to cross-reference between different systems - @li - Sandboxed security: web applications are secure by default. No matter how sketchy the websites you visit, you can be sure that once you close the page, they're gone - - @p - These features are all very nice to have, and together have made the web platform the success it is today. When you compare it to traditional applications, you can see the draw: - @ul - @li - Installing traditional desktop applications is usually a several-minute-long process. If something goes wrong in the installation, that often leaves a botched half-install on your computer which makes installing, uninstalling, or running the program impossible without manual surgery to excise the broken files. - @li - Desktop applications generally do not talk to each other at all. While on the web you can easily link a page to someone, trying to get someone to a particular screen in desktop software often involves a chain of screenshots with detailed instructions of which buttons to click at each stage. - @li - Desktop application security is non-existent. Install one rogue application and it can take over your computer, steal your credit card number, use your email for sending spam, and all sorts of other nasty things. Removing these for-good sometimes involves re-installing your entire operating system. Hence people are much more wary about only installing desktop software from people they "trust". - - @p - In many ways, mobile App platforms like Android and iOS have closed the gap between "native" and "web" applications. Installing a new App may take 30 seconds, you can often deep-link to certain pages within an App, and Apps have a much tighter security model than desktop software does. Nevertheless, 30 seconds is still much longer than the 0.5 seconds it takes to open a web page, deep-linking in apps is not very prevalent, and the security model still often leaves space for rogue Apps to misbehave and steal data. - - @hr - @p - Despite the problems with Javascript (and other tools like HTML an CSS, which have their own problems) the Web platform got a lot of things right, and the Desktop and Mobile platforms have a lot of catching up to do. If only we could improve upon the parts that aren't so great. This is where Scala.js comes in. - -@sect{About Scala.js} - - @p - With Scala.js, you can cross compile your Scala code to a Javascript executable that can run on all major web browsers. You get all the benefits of the web platform in terms of deployability, security, and hyperlinking, with none of the problems of writing your software in Javascript. Scala.js provides a @sect.ref("The Language", "better language") to do your work in, but also provides some other goodies that have in-so-far never been seen in mainstream web development: @sect.ref("Sharing Code", "shared-code") and @sect.ref("Client-Server Integration", "client-server integration"). - - @sect{The Language} - @p - At a first approximation, Scala.js provides you a sane language to do development in the web browser. This saves you from an endless stream of Javascript warts like this one: - - @hl.javascript - javascript> ["10", "10", "10", "10"].map(parseInt) - [10, NaN, 2, 3] // WTF - - @hl.scala - scala> List("10", "10", "10", "10").map(parseInt) - List(10, 10, 10, 10) // Yay! - - - @p - Not only do you have an expressive language with static types, you also have great tooling with IDEs like IntelliJ and Eclipse, a rich library of standard collections, and many other modern conveniences that we take for granted but are curiously missing when working in the wild west of web development: the browser! You get all of the upside of developing for the web platform. - - @p - 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: @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 - Shared code is one of the holy-grails of web development. Traditionally the client-side code and server-side code has been written in separate languages: PHP or Perl or Python or Ruby or Java on the server, with only Javascript on the client. This means that algorithms were often implemented twice, constants copied-&-pasted, or awkward Ajax calls are made in an attempt to centralize the logic in one place (the server). With the advent of Node.js in the last few years, you can finally re-use the same code on the server as you can on the client, but with the cost of having all the previously client-only @sect.ref("Javascript-the-language", "problems with Javascript") now inflicted upon your server code base. Node.js expanded your range-of-options for writing shared client/server logic from "Write everything twice" to "Write everything twice, or write everything in Javascript". More options is always good, but it's not clear which of the two choices is more painful! - - @p - Scala.js provides an alternative to this dilemma. With Scala.js, you can utilize the same libraries you use writing your Scala servers when writing your Scala web clients! On one end, you are sharing your templating language with @lnk("Scalatags", "https://github.com/lihaoyi/scalatags") or sharing your serialization logic with @lnk("uPickle", "https://github.com/lihaoyi/upickle"). At the other, you are sharing large, abstract libraries like @lnk("Scalaz", "https://github.com/japgolly/scalaz") or @lnk("Shapeless", "https://groups.google.com/forum/#!searchin/scala-js/shapeless/scala-js/5Sf2up0z3PU/9F9SYB0qHEcJ"). - - @p - Sharing code means several things: - - @ul - @li - Not having to find two libraries to do a particular common task - @li - Not having to re-learn two different ways of doing the exact same thing - @li - Not needing to implement the same algorithms twice, for the times you can't find a good library to do what you want - @li - Not having to debug problems caused by subtle differences in the two implementations - @li - 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 @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 even split of downloads from people using it on both platforms: - - @img(src:="images/Scalatags Downloads.png", width:="100%") - - @p - Shared code means that if you, as an application writer, want some logic to be available on both the client and server, you simply @sect.ref("A Client-Server Setup", "put it in a shared/ folder"), and that's the end of the discussion. No architectural patterns to follow, no clever techniques need to be involved. Shared logic, whether that means constants, functions, data structures, all the way to algorithms and entire libraries, can simply be placed in @code{shared/} and be instantly accessible from both your client-side web code and your server. - - @p - Shared code has long been the holy-grail of web development. Even now, people speak of shared code as if it were a myth. With Scala.js, shared code is the simple, boring reality. And all this while, just as importantly, you don't need to re-write your large enterprise back-end systems in a language that doesn't scale well beyond 100s of lines of code. - - @sect{Client-Server Integration} - @p - There is an endless supply of new platforms which have promised to change-the-way-we-do-web-development-forever. From old-timers like @lnk("Ur-Web", "http://www.impredicative.com/ur/"), to @lnk("GWT", "http://www.gwtproject.org/"), to Asana's @lnk("LunaScript", "https://asana.com/luna"), to more recently things like @lnk("Meteor.js", "https://www.meteor.com/"). - @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 @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. It becomes @sect.ref("What's Left?", "impossible to make a malformed Ajax call"). - @p - There's a lot to be said for automating things using a computer. The entire field of software engineering is basically about automating tasks that were previously done manually: accounting, banking, making travel arrangements, and all that. However, in the world of web-development, there has always been one set of tasks that has traditionally be done manually: the task of ensuring the web-clients are properly synchronized with the web-servers. Communication between the two has always been a manual, tedious, error-prone process, and mistakes often end un-noticed until something breaks in production. - @p - With Scala.js, like the other experimental platforms that have come before us, we attempt to provide a way forward from this manual-tedium. - - @hr - - @p - In many ways, Scala.js all-at-once provides many of the traditional holy-grails of web development: People have always dreamed about doing web development in a sane, best-of-breed language that compiles to both client and server. Of not having to worry too hard about whether code goes on the client or on the server, and being able to move or share it if necessary. Of having a compiler that will verify and check that your entire system is correct. - - @p - Scala.js provides all these things, and much more. If you're interested enough to want to make use of Scala.js, read on! diff --git a/book/src/main/scalatex/book/handson/CanvasApp.scalatex b/book/src/main/scalatex/book/handson/CanvasApp.scalatex deleted file mode 100644 index e40b211..0000000 --- a/book/src/main/scalatex/book/handson/CanvasApp.scalatex +++ /dev/null @@ -1,211 +0,0 @@ -@import BookData._ -@val canvasapp = wd/'examples/'demos/'src/'main/'scala/'canvasapp -@p - By this point, you've already cloned and got your hands dirty fiddling around with the toy @lnk("workbench-example-app", "https://github.com/lihaoyi/workbench-example-app"). You have your editor set up, SBT installed, and have published the example application in a way you can host online for other people to see. Maybe you've even made some changes to the application to see what happens. Hopefully you're curious, and want to learn more. - -@p - In this section of the book, we will walk through making a small canvas application. This will expose you to important concepts like: - -@ul - @li - Taking input with Javascript event handlers - @li - Writing your application logic in Scala - @li - Using a timer to drive periodic actions - -@p - In general, while the previous chapter was mostly set-up and exploring the Scala.js project, this chapter will walk you through actually writing a non-trivial, self-contained Scala.js application. Throughout this chapter, we will only be making modifications to @code{ScalaJSExample.scala}; the rest of the project will remain unchanged. - -@sect{Making a Sketchpad using Mouse Input} - - @p - To begin with, lets remove all the existing stuff in our @code{.scala} file and leave only the @hl.scala{object} and the @hl.scala{main} method. Let's start off with some necessary boilerplate: - - @hl.ref(canvasapp/"ScratchPad.scala", "/*setup*/", end = "/*code*/") - - @p - 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: - - @split - @more - @hl.ref(canvasapp/"ScratchPad.scala", "/*code*/") - - @less - @BookData.example(canvas, "canvasapp.ScratchPad().main") - - @p - 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 @lnk.dom.html.Canvas. 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%") - - @p - Apart from mouse events, keyboard events, scroll events, input events, etc. are all usable from Scala.js as you'd expect. If you have problems getting this to work, feel free to click on the link @i(cls:="fa fa-link ") icon below the code snippet to see what the full code for the example looks like - -@sect{Making a Clock using setInterval} - - @p - 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: - - @hl.ref(canvasapp/"Clock.scala", "/*setup*/", "/*code*/") - - @p - The only thing unusual here is that I'm going to create a @hl.scala{linearGradient} in order to make the stopwatch look pretty. This is by no means necessary, and you could simply make the @hl.scala{fillStyle} @hl.scala{"black"} if you want to keep things simple. - - @p - Once that's done, it's only a few lines of code to set up a nice, live clock: - - @split - @more - @hl.ref(canvasapp/"Clock.scala", "/*code*/") - - @less - @BookData.example(canvas, "canvasapp.Clock().main") - - @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 full name @hl.scala{scala.scalajs.js.Date}, here imported as @hl.scala{js.Date}. Again, click on the link @i(cls:="fa fa-link ") icon to view the full-code if you're having trouble here. - -@sect{Tying it together: Flappy Box} - - @p - You've just seen two examples of how to use Scala.js, together with the Javascript DOM APIs, to make simple applications. However, we've only used the "Scala" in Scala.js in the most rudimentary fashion: setting a few primitives here and there, defining some methods, mainly just gluing together a few Javascript APIs - - @p - In this example we will make a spiritual clone of the popular @lnk("Flappy Bird", "http://en.wikipedia.org/wiki/Flappy_Bird") video game. This game involves a few simple rules - - @ul - @li - Your character starts in the middle of the screen - @li - Gravity pulls your character down - @li - Clicking/tapping the screen makes your character jump up - @li - There are obstacles that approach your character from the right side of the screen, and you have to make sure you go through the hole in each obstacle to avoid hitting it - @li - Don't go out of bounds! - - @p - It's a relatively simple game, but there should be enough "business logic" in here that we won't be simply gluing together APIs. Let's start! - - @sect{Setting Up the Canvas} - @hl.ref(canvasapp/"FlappyLine.scala", "/*setup*/", end="/*variables*/") - - @p - This section of the code is peripherally necessary, but not core to the implementation or logic of Flappy Box. We see the same @hl.scala{canvas}/@hl.scala{renderer} logic we've seen in all our examples, along with some logic to make the canvas a reasonable size, and some configuration of how we will render text to the canvas. - - @p - In general, code like this will usually end up being necessary in a Scala.js program: the Javascript APIs that the browser provides to do things often ends up being somewhat roundabout and verbose. It's somewhat annoying to have to do for a small program such as this one, but in a larger application, the cost is both spread out over thousands of lines of code and also typically hidden away in helper functions, so the verbosity and non-idiomatic-scala-ness doesn't bother you much. - - @sect{Defining our State} - @hl.ref(canvasapp/"FlappyLine.scala", "/*variables*/", end="def runLive") - - @p - 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 @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(canvasapp/"FlappyLine.scala", "def runLive", "def runDead") - - @p - The @hl.scala{runLive} function is the meat of Flappy Box. In it, we - - @ul - @li - Clear the canvas - @li - Generate new obstacles - @li - Apply velocity and acceleration to the player - @li - Check for collisions or out-of-bounds, killing the player if it happens - @li - Rendering everything, including the player as the namesake box - - @p - This function basically contains all the game logic, from motion, to collision-detection, to rendering, so it's pretty large. Not that large though! And entirely understandable, even if it takes a few moments to read through. - - @hl.ref(canvasapp/"FlappyLine.scala", "def runDead", "def run()") - - @p - This is the function that handles what happens when you're dead. Essentially, we reset all the mutable variables to their initial state, and just count down the @hl.scala{dead} counter until it reaches zero and we're considered alive again. - - @sect{A Working Product} - @hl.ref(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 @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! - - @div - @BookData.example(canvas, "canvasapp.FlappyLine().main") - -@sect{Canvas Recap} - @p - We've now gone through the workings of building a handful of toy applications using Scala.js. What have we learnt in the process? - - @sect{Development Speed} - @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 existing Scala IDEs like @lnk.misc.IntelliJ or @lnk.misc.Eclipse can give very useful help when you're working with Scala.js. Autocomplete, error-highlighting, 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 - All of the examples so far have been very self-contained: they do not touch the HTML DOM, they do not make Ajax calls, or try to access web services. They don't push the limits of the browser's API. - - @p - Nevertheless, these examples have exercised a good amount of the Scala language. List comprehensions, collections, the math library, and more. In general, most of the Scala standard library works under Scala.js, as well as a large number of third-party libraries. Unlike many other compile-to-Javascript languages out there, this isn't a language-that-looks-like-Scala: it is Scala through and through, with a tiny number of semantic differences. - - @sect{Seamless Javascript Interop} - @p - Even if we take some time to read through the code we've written, it is not immediately obvious which bits of code are Scala and which bits are Javascript! It all kind of meshes together, for example if we take the Flappy Box source code: - - @ul - @li - @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 @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 - @hl.scala{playerY} and @hl.scala{playerV} are Scala @hl.scala{Double}s, implemented directly as Javascript @hl.javascript{Number}s - - @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 @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. - - @hr - - @p - You've now had some experience building small canvas applications in Scala.js. Why not try exercising your new-found skills? Here are some possibilities: - - @ul - @li - Make more video games! I have a set of @lnk("retro-games ported to Scala.js", "http://lihaoyi.github.io/scala-js-games/"). Maybe re-make one of them without looking at the source, or maybe port some other game you're familiar with and enjoy playing. Even just drawing things on canvas, games can get @lnk("pretty elaborate", "http://lihaoyi.github.io/roll/"). - - @li - Explore the APIs! We've touched on a small number of Javascript APIs here, mainly for dealign with the canvas, but modern browsers offer a whole @lnk("zoo of functionality", "https://developer.mozilla.org/en-US/docs/Web/API"). Try making an application that uses @lnk("localStorage", "https://developer.mozilla.org/en-US/docs/Web/Guide/API/DOM/Storage") to save state even when you close the tab, or an application that works with the HTML DOM. - - @li - Draw something pretty! We have a working canvas, a nice programming language, and a way to easily publish the results online. @lnk("Various", "http://www.scala-js-fiddle.com/gist/77a3840678d154257ca1/KochSnowflake.scala") @lnk("fractals", "http://www.scala-js-fiddle.com/gist/77a3840678d154257ca1/KochCurve.scala"), or @lnk("colorful visualizations", "http://www.scala-js-fiddle.com/gist/9443f8e0ecc68d1058ad/RayTracer.scala") are all possibilities. - - @p - By this point you've some experience building stand-alone, single-canvas Scala.js applications, which has hopefully given you a feel for how Scala.js works. The problem is that few web applications satisfy the criteria of being stand-alone single-page canvas applications! Most web applications need to deal with the DOM of the HTML page, need to fetch data from web services, and generally need to do a lot of other messy things. We'll go into that in the next chapter diff --git a/book/src/main/scalatex/book/handson/ClientServer.scalatex b/book/src/main/scalatex/book/handson/ClientServer.scalatex deleted file mode 100644 index 50f9850..0000000 --- a/book/src/main/scalatex/book/handson/ClientServer.scalatex +++ /dev/null @@ -1,271 +0,0 @@ -@import BookData._ - -@def lazyload(id: String) = script(raw(s""" - window.addEventListener("load", function(){ - document.getElementById("$id").src = 'https://hands-on-scala-js.herokuapp.com/' - }) -""")) - - -@p - Historically, sharing code across client & server has been a holy-grail for web development. There are many things which have made it hard in the past: - -@ul - @li - Javascript on the client v.s. PHP/Perl/Python/Ruby/Java on the server - @li - Most back-ends make heavy use of C extensions, and front-end code was tightly coupled to the DOM. Even if you manage to port the main language -@p - There have been some attempts in recent years with more traction: Node.js, for example, has been very successful at running Javascript on the server, the Clojure/Clojurescript community has their own version of cross-built code, and there are a number of smaller, more esoteric platforms. - -@p - Scala.js lets you share code between client and server relatively straightforwardly. As we saw in the previous chapter, where we made a shared module. Let's work to turn that shared module into a working client-server application! - -@val server = wd/'examples/'crossBuilds/'clientserver/'app/'jvm/'src/'main/'scala/'simple -@val client = wd/'examples/'crossBuilds/'clientserver/'app/'js/'src/'main/'scala/'simple - -@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 (@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. Just like our project in @sect.ref{Cross Publishing Libraries}, our project will look like this: - - @hl.bash - $ tree - . - ├── build.sbt - ├── project/build.sbt - └── app -    ├── shared/src/main/scala/simple/FileData.scala -    ├── js/src/main/scala/simple/Client.scala -    └── jvm/src/main/scala/simple/ - ├── Page.scala - └── Server.scala - - @p - First, let's do the wiring in @code{build.sbt}: - - @hl.ref(wd/'examples/'crossBuilds/'clientserver/"build.sbt") - - @p - Again, we are using @hl.scala{crossProject} to define our @code{js/} and @code{jvm/} sub-projects. Both projects share a number of settings: 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. Note also the @hl.scala{packageArchetype.java_application} setting, which isn't strictly necessary depending on what you want to do with the application, but this example needs it as part of the deployment to Heroku. - - @p - The @code{js/} sub-project is uneventful, with a dependency on the by-now-familiar @code{scalajs-dom} library. The @code{jvm/} project, on the other hand, is interesting: it contains the dependencies required for us to set up out Spray server, and one additional 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. - - @p - Next, let's kick off the Spray server in our Scala-JVM main method: - - - @hl.ref(server/"Server.scala") - - @p - This is a not-very-interesting @lnk("spray-routing", "http://spray.io/documentation/1.2.2/spray-routing/") application: we set up a server on @code{localhost:8080}, have the root URL serve the main page on GET, and have other GET URLs serve resources. This includes the @code{js-fastopt.js} file that is now in our resources because of our @code{build.sbt} config earlier! We also add a POST route to allow the client ask the server to list files various directories. - - @p - The HTML template @hl.scala{Page.skeleton} is not shown above; I put it in a separate file for neatness: - - @hl.ref(server/"Page.scala") - - @p - 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{"} sneaks through and your users' accounts & data is compromised. - - @p - There are more, but we won't go deep into the intricacies of these problems. Suffice to say it makes mistakes easy to make and hard to catch, and we have something better... - -@sect{Scalatags} - @p - @lnk("Scalatags", "https://github.com/lihaoyi/scalatags") is a cross-platform Scala.js/Scala-JVM library that is designed to generate HTML. To use Scalatags, you need to add it as a dependency to your Scala.js SBT project, in the @code{build.sbt} file: - - @hl.ref(wd/'examples/'demos/"build.sbt", "scalatags", "") - - @p - With that, the above snippet of code re-written using Scalatags looks as follows: - - @split - @more - @hl.ref(webpage/"HelloWorld1.scala") - - @less - @BookData.example(div, "webpage.HelloWorld1().main") - - @p - Scalatags has some nice advantages over plain HTML: it's type-safe, so typos like @hl.scala{dvi} get caught at compile-time. It's also secure, such that you don't need to worry about script-tags in strings or similar. The @lnk("Scalatags Readme", "https://github.com/lihaoyi/scalatags#scalatags") elaborates on these points and other advantages. As you can see, it takes just 1 import at the top of the file to bring it in scope, and then you can use all of Scalatags' functionality. - - @p - The Scalatags github page has @lnk("comprehensive documentation", "https://github.com/lihaoyi/scalatags#hello-world") on how to express all manner of HTML fragments using Scalatags, so anyone who's familiar with how HTML works can quickly get up to speed. Instead of a detailed listing, we'll walk through some interactive examples to show Scalatags in action! - - @sect{User Input} - @split - @more - @hl.ref(webpage/"Inputs.scala", "val box") - - @less - @BookData.example(div(height:="150px"), "webpage.Inputs().main") - - @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 @lnk.dom.Element. Different fragments render to different things: e.g. @hl.scala{input.render} gives you a @lnk.dom.html.Input, @hl.scala{span.render} gives you a @lnk.dom.html.Span. 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. - - @sect{Re-rendering} - @p - Let's look at a slightly longer example. While above we spliced small snippets of text into the DOM, here we are going to re-render entire sections of HTML! The goal of this little exercise is to make a filtering search-box: starting from a default list of items, narrow it down as the user enters text into the box. - - @p - To begin with, let's define our list of items: Fruits! - - @hl.ref(webpage/"Search0.scala", "val listings", "def") - - @p - Next, let's think about how we want to render these fruits. One natural way would be as a list, which in HTML is represented by a @hl.html{