From cf9b1e5dc81d4c3a30a26cb18e38f7db03460ef4 Mon Sep 17 00:00:00 2001 From: Li Haoyi Date: Fri, 5 Sep 2014 13:20:48 -0700 Subject: Workbench is now pure-scala --- build.sbt | 3 +- client/src/main/scala/workbench/Main.scala | 74 ------------------ .../src/main/scala/workbench/WorkbenchClient.scala | 87 ++++++++++++++++++++++ .../src/main/scala/example/ScalaJSExample.scala | 3 +- src/main/scala/workbench/Server.scala | 28 ++++--- workbench_template.js | 71 ------------------ 6 files changed, 107 insertions(+), 159 deletions(-) delete mode 100644 client/src/main/scala/workbench/Main.scala create mode 100644 client/src/main/scala/workbench/WorkbenchClient.scala delete mode 100644 workbench_template.js diff --git a/build.sbt b/build.sbt index 852e777..6bb8ec3 100644 --- a/build.sbt +++ b/build.sbt @@ -9,7 +9,7 @@ val defaultSettings = Seq( lazy val root = project.in(file(".")).settings(defaultSettings:_*).settings( name := "workbench", - version := "0.1.6-SNAPSHOT", + version := "0.1.6", organization := "com.lihaoyi", scalaVersion := "2.10.4", sbtPlugin := true, @@ -53,7 +53,6 @@ lazy val client = project.in(file("client")) .settings( libraryDependencies ++= Seq( "org.scala-lang.modules.scalajs" %%% "scalajs-dom" % "0.6", - "org.scala-lang.modules" %% "scala-async" % "0.9.2", "com.lihaoyi" %%% "autowire" % "0.2.3", "com.lihaoyi" %%% "upickle" % "0.2.3" ) diff --git a/client/src/main/scala/workbench/Main.scala b/client/src/main/scala/workbench/Main.scala deleted file mode 100644 index 7f63da6..0000000 --- a/client/src/main/scala/workbench/Main.scala +++ /dev/null @@ -1,74 +0,0 @@ -package com.lihaoyi.workbench -import upickle._ -import org.scalajs.dom -import org.scalajs.dom.extensions._ -import upickle.{Reader, Writer, Js} - -import scala.scalajs.js -import scala.scalajs.js.annotation.JSExport -import scalajs.concurrent.JSExecutionContext.Implicits.runNow -object WireServer extends autowire.Server[Js.Value, upickle.Reader, upickle.Writer]{ - - def write[Result: Writer](r: Result) = upickle.writeJs(r) - def read[Result: Reader](p: Js.Value) = upickle.readJs[Result](p) - def wire(parsed: Js.Arr): Unit = { - WireServer.route[Api](Main).apply( - autowire.Core.Request( - parsed(0).asInstanceOf[Js.Arr].value.collect{case Js.Str(s) => s}, - parsed(1).value.asInstanceOf[Map[String, Js.Value]] - ) - ) - } -} -@JSExport -object Main extends Api{ - def main(bootSnippet: String, host: String, port: Int): Unit = { - def rec(): Unit = { - val f = Ajax.get(s"http://$host:$port/notifications") - - f.onSuccess { case data => - val parsed = json.read(data.responseText).asInstanceOf[Js.Arr] - WireServer.wire(parsed) - rec() - } (runNow) - () - } - rec() - } - - - override def clear(): Unit = ??? - - override def reload(): Unit = { - dom.console.log("Reloading page...") - dom.location.reload() - } - - override def run(path: String, bootSnippet: Option[String]): Unit = { - val tag = dom.document.createElement("script") - var loaded = false - - tag.setAttribute("src", path) - - bootSnippet.foreach{ bootSnippet => - tag.onreadystatechange = (e: dom.Event) => { - dom.console.log("Post-run reboot") - if (!loaded) { - dom.console.log("Post-run reboot go!") - js.eval(bootSnippet) - } - loaded = true - } - } - dom.document.head.appendChild(tag) - } - - override def print(level: String, msg: String): Unit = { - level match { - case "error" => dom.console.error(msg) - case "warn" => dom.console.warn(msg) - case "info" => dom.console.info(msg) - case "log" => dom.console.log(msg) - } - } -} \ No newline at end of file diff --git a/client/src/main/scala/workbench/WorkbenchClient.scala b/client/src/main/scala/workbench/WorkbenchClient.scala new file mode 100644 index 0000000..c67d4c6 --- /dev/null +++ b/client/src/main/scala/workbench/WorkbenchClient.scala @@ -0,0 +1,87 @@ +package com.lihaoyi.workbench +import upickle._ +import org.scalajs.dom +import org.scalajs.dom.extensions._ +import upickle.{Reader, Writer, Js} +import scala.scalajs.js +import scala.scalajs.js.annotation.JSExport +import scalajs.concurrent.JSExecutionContext.Implicits.runNow +object WireServer extends autowire.Server[Js.Value, upickle.Reader, upickle.Writer]{ + + def write[Result: Writer](r: Result) = upickle.writeJs(r) + def read[Result: Reader](p: Js.Value) = upickle.readJs[Result](p) + def wire(parsed: Js.Arr): Unit = { + val Js.Arr(path, args: Js.Obj) = parsed + + val req = new Request( + upickle.readJs[Seq[String]](path), + args.value.toMap + ) + WireServer.route[Api](WorkbenchClient).apply(req) + } +} +@JSExport +object WorkbenchClient extends Api{ + val shadowBody = dom.document.body.cloneNode(deep = true) + var interval = 1000 + var success = false + @JSExport + def main(bootSnippet: String, host: String, port: Int): Unit = { + Ajax.post(s"http://$host:$port/notifications").onComplete{ + case util.Success(data) => + if (!success) println("Workbench connected") + success = true + interval = 1000 + json.read(data.responseText) + .asInstanceOf[Js.Arr] + .value + .foreach(v => WireServer.wire(v.asInstanceOf[Js.Arr])) + main(bootSnippet, host, port) + case util.Failure(e) => + if (!success) println("Workbench disconnected " + e) + success = false + interval = math.min(interval * 2, 30000) + dom.setTimeout(() => main(bootSnippet, host, port), interval) + } + } + + override def clear(): Unit = { + dom.document.asInstanceOf[js.Dynamic].body = shadowBody.cloneNode(true) + for(i <- 0 until 100000){ + dom.clearTimeout(i) + dom.clearInterval(i) + } + } + + override def reload(): Unit = { + dom.console.log("Reloading page...") + dom.location.reload() + } + + override def run(path: String, bootSnippet: Option[String]): Unit = { + val tag = dom.document.createElement("script") + var loaded = false + + tag.setAttribute("src", path) + bootSnippet.foreach{ bootSnippet => + tag.onreadystatechange = (e: dom.Event) => { + if (!loaded) { + dom.console.log("Post-run reboot") + js.eval(bootSnippet) + } + loaded = true + } + tag.asInstanceOf[js.Dynamic].onload = tag.onreadystatechange + } + dom.document.head.appendChild(tag) + } + + override def print(level: String, msg: String): Unit = { + level match { + case "error" => dom.console.error(msg) + case "warn" => dom.console.warn(msg) + case "info" => dom.console.info(msg) + case "log" => dom.console.log(msg) + } + } +} \ No newline at end of file diff --git a/example/src/main/scala/example/ScalaJSExample.scala b/example/src/main/scala/example/ScalaJSExample.scala index 7222143..bab193b 100644 --- a/example/src/main/scala/example/ScalaJSExample.scala +++ b/example/src/main/scala/example/ScalaJSExample.scala @@ -7,7 +7,8 @@ case class Point(x: Int, y: Int){ def +(p: Point) = Point(x + p.x, y + p.y) def /(d: Int) = Point(x / d, y / d) } - +// Seems like you need this for sbt ~fastOptJS to work +// mkdir ~/.sbt/0.13/plugins/target/scala-2.10/sbt-0.13/classes @JSExport object ScalaJSExample { val ctx = dom.document diff --git a/src/main/scala/workbench/Server.scala b/src/main/scala/workbench/Server.scala index 96ac736..bec88b6 100644 --- a/src/main/scala/workbench/Server.scala +++ b/src/main/scala/workbench/Server.scala @@ -14,13 +14,13 @@ import scala.concurrent.Future class Server(url: String, port: Int, bootSnippet: String) extends SimpleRoutingApp{ implicit val system = ActorSystem( - "SystemLol", + "Workbench-System", config = ConfigFactory.load(ActorSystem.getClass.getClassLoader), classLoader = ActorSystem.getClass.getClassLoader ) object Wire extends autowire.Client[Js.Value, upickle.Reader, upickle.Writer]{ def doCall(req: Request): Future[Js.Value] = { - pubSub ! Js.Arr(Js.Str(req.path.mkString(".")), Js.Obj(req.args.toSeq:_*)) + pubSub ! Js.Arr(upickle.writeJs(req.path), Js.Obj(req.args.toSeq:_*)) Future.successful(Js.Null) } def write[Result: Writer](r: Result) = upickle.writeJs(r) @@ -63,19 +63,25 @@ class Server(url: String, port: Int, bootSnippet: String) extends SimpleRoutingA get { path("workbench.js") { complete { - IO.readStream( - getClass.getClassLoader - .getResourceAsStream("client-opt.js") - ) + s"\nMain.main(${upickle.write(bootSnippet)}, ${upickle.write(url)}, ${upickle.write(port)})" + val body = IO.readStream( + getClass.getClassLoader.getResourceAsStream("client-opt.js") + ) + s""" + (function(){ + $body + + WorkbenchClient().main(${upickle.write(bootSnippet)}, ${upickle.write(url)}, ${upickle.write(port)}) + }).call(this) + """ } } ~ - getFromDirectory(".") + getFromDirectory(".") } ~ - post { - path("notifications") { ctx => - pubSub ! ctx.responder - } + post { + path("notifications") { ctx => + pubSub ! ctx.responder } + } } def kill() = { system.shutdown() diff --git a/workbench_template.js b/workbench_template.js deleted file mode 100644 index a16e638..0000000 --- a/workbench_template.js +++ /dev/null @@ -1,71 +0,0 @@ -(function(){ - var shadowBody = null - var bootSnippet = "" - window.onload = function(){ - shadowBody = document.body.cloneNode(true) - start() - } - window.addEventListener("keydown", function (event) { - if(event.keyCode==13 && event.ctrlKey && event.altKey && event.shiftKey) { - clear() - eval(bootSnippet) - } - }) - function clear(){ - document.body = shadowBody.cloneNode(true) - for(var i = 0; i < 99999; i++){ - clearTimeout(i) - clearInterval(i) - } - } - - var start = function(){ - var req = new XMLHttpRequest() - - req.open("POST", "http://:/notifications") - - req.onload = function(){ - if (req.status != 200){ - setTimeout(function(){start()}, 1000) - }else{ - var dataList = JSON.parse(req.responseText) - for(var i = 0; i < dataList.length; i++){ - - var data = dataList[i] - if (data[0] == "reload") { - console.log("Reloading page...") - location.reload() - } - if (data[0] == "clear"){ - clear() - } - if (data[0] == "run"){ - var tag = document.createElement("script") - var loaded = false - - tag.setAttribute("src", data[1]) - - var bootSnippet = data[2] - if (bootSnippet){ - tag.onreadystatechange = tag.onload = function() { - console.log("Post-run reboot") - if (!loaded) { - console.log("Post-run reboot go!") - eval(bootSnippet) - } - loaded = true - }; - } - document.head.appendChild(tag) - } - if (data[0] == "boot"){ - eval(bootSnippet) - } - if (data[0] == "print") console[data[1]](data[2]) - } - start() - } - } - req.send() - } -})() -- cgit v1.2.3