From f48a24f376bc97064a9679f3b80b266e24cc76ba Mon Sep 17 00:00:00 2001 From: lihaoyi Date: Sun, 17 Nov 2013 16:22:30 -0800 Subject: first real commit --- package.scala | 112 +++++++++++++++++++++ plugin/build.sbt | 6 -- .../scala/scala/scalajs/js/resource/Plugin.scala | 54 ---------- project/Build.scala | 21 +++- project/plugins.sbt | 1 - runtime/build.sbt | 1 - runtime/project/plugins.sbt | 1 - .../src/main/scala/scala/scalajs/js/Resource.scala | 30 ------ workbench_template.js | 25 +++++ 9 files changed, 154 insertions(+), 97 deletions(-) create mode 100644 package.scala delete mode 100644 plugin/build.sbt delete mode 100644 plugin/src/main/scala/scala/scalajs/js/resource/Plugin.scala delete mode 100644 project/plugins.sbt delete mode 100644 runtime/build.sbt delete mode 100644 runtime/project/plugins.sbt delete mode 100644 runtime/src/main/scala/scala/scalajs/js/Resource.scala create mode 100644 workbench_template.js diff --git a/package.scala b/package.scala new file mode 100644 index 0000000..cee84bc --- /dev/null +++ b/package.scala @@ -0,0 +1,112 @@ +package scala.js + +import akka.actor.{Props, ActorRef, Actor, ActorSystem} +import akka.io +import akka.util.ByteString +import play.api.libs.json.JsArray + +import play.api.libs.json.Json +import spray.can.Http +import spray.can.server.websockets.model.Frame +import spray.can.server.websockets.model.OpCode +import spray.can.server.websockets.Sockets +import sbt._ +import Keys._ + +import com.typesafe.config.ConfigFactory +import scala.collection.mutable +import akka.io.Tcp +import spray.http.{StatusCodes, HttpResponse, HttpRequest} +import spray.http.HttpHeaders.Connection +import spray.can.server.websockets.model.OpCode.Text + +package object workbench extends sbt.Plugin { + val refreshBrowsers = taskKey[Unit]("Sends a message to all connected web pages asking them to refresh the page") + val generateClient = taskKey[File]("generates a .js file that can be embedded in your web page") + val localUrl = settingKey[(String, Int)]("localUrl") + val server = settingKey[ActorRef]("local websocket server") + val fileName = settingKey[String]("name of the generated javascript file") + + implicit val system = ActorSystem( + "SystemLol", + config = ConfigFactory.load(ActorSystem.getClass.getClassLoader), + classLoader = ActorSystem.getClass.getClassLoader + ) + + implicit class pimpedActor(server: ActorRef){ + def send(x: JsArray) = { + server ! Frame( + opcode = OpCode.Text, + data = ByteString(x.toString()) + ) + } + } + + val buildSettingsX = Seq( + localUrl := ("localhost", 12345), + fileName := "workbench.js", + server := { + implicit val server = system.actorOf(Props(new SocketServer)) + val host = localUrl.value + io.IO(Sockets) ! Http.Bind(server, host._1, host._2) + server + }, + + extraLoggers := { + val clientLogger = FullLogger{ + new Logger { + def log(level: Level.Value, message: => String): Unit = + if(level >= Level.Info) server.value.send(Json.arr("print", level.toString(), message)) + def success(message: => String): Unit = server.value.send(Json.arr("print", message)) + def trace(t: => Throwable): Unit = server.value.send(Json.arr("print", t.toString)) + } + } + val currentFunction = extraLoggers.value + (key: ScopedKey[_]) => clientLogger +: currentFunction(key) + }, + refreshBrowsers := { + streams.value.log("Reloading Pages...") + server.value.send(Json.arr("reload")) + }, + generateClient := { + val clientTemplate = IO.readStream(getClass.getClassLoader.getResourceAsStream("workbench_template.js")) + val transformed = clientTemplate.replace("", localUrl.value._1).replace("", localUrl.value._2.toString) + val outputFile = (crossTarget in Compile).value / fileName.value + IO.write(outputFile, transformed) + outputFile + } + ) + + class SocketServer extends Actor{ + val sockets: mutable.Set[ActorRef] = mutable.Set.empty + def receive = { + case x: Tcp.Connected => sender ! Tcp.Register(self) // normal Http server init + + case req: HttpRequest => + // Upgrade the connection to websockets if you think the incoming + // request looks good + if (req.headers.contains(Connection("Upgrade"))){ + sender ! Sockets.UpgradeServer(Sockets.acceptAllFunction(req), self) + }else{ + sender ! HttpResponse( + StatusCodes.OK, + entity="i am a cow" + ) + } + + case Sockets.Upgraded => + sockets.add(sender) + println("Browser Open n=" + sockets.size) + + case f @ Frame(fin, rsv, Text, maskingKey, data) => + sockets.foreach(_ ! f.copy(maskingKey=None)) + + case _: Tcp.ConnectionClosed => + if (sockets.contains(sender)) println("Browser Closed n=" + sockets.size ) + sockets.remove(sender) + + + case x => + } + } +} diff --git a/plugin/build.sbt b/plugin/build.sbt deleted file mode 100644 index 554cd1b..0000000 --- a/plugin/build.sbt +++ /dev/null @@ -1,6 +0,0 @@ -sbtPlugin := true - - -addSbtPlugin("org.scala-lang.modules.scalajs" % "scalajs-sbt-plugin" % "0.1-SNAPSHOT") - -libraryDependencies += "commons-codec" % "commons-codec" % "1.8" diff --git a/plugin/src/main/scala/scala/scalajs/js/resource/Plugin.scala b/plugin/src/main/scala/scala/scalajs/js/resource/Plugin.scala deleted file mode 100644 index c8960c8..0000000 --- a/plugin/src/main/scala/scala/scalajs/js/resource/Plugin.scala +++ /dev/null @@ -1,54 +0,0 @@ -package scala.scalajs.js.resource - -import sbt._ -import Keys._ -import scala.scalajs.sbtplugin.ScalaJSPlugin.ScalaJSKeys._ -import org.apache.commons.codec.binary.Base64 - - -object Plugin extends sbt.Plugin { - - val resourceSettings = Seq( - watchSources := { - watchSources.value ++ (resources in Compile).value ++ Path.allSubpaths((sourceDirectory in Compile).value / "js").map(_._1).toSeq - }, - packageJS := { - val bundledJSResults: Set[sbt.File] = FileFunction.cached( - cacheDirectory.value, - FilesInfo.lastModified, - FilesInfo.exists - ){(inFiles: Set[File]) => - val pathMap = Path.relativeTo((resources in Compile).value) - val bundle = crossTarget.value / "resources.js" - val fileLines = for(file <- inFiles) yield { - val b64 = Base64.encodeBase64String(IO.readBytes(file)) - " \"" + pathMap(file).get + "\": \"" + b64 + "\"" - } - IO.write(bundle, "\nScalaJS.resources = {\n" + fileLines.mkString(",\n") + "\n}" ) - Set(bundle) - }( - for{ - (resourceRoot: File) <- (resources in Compile).value.toSet - (file, path) <- Path.allSubpaths(resourceRoot) - } yield file - ) - - val copiedJSFiles = FileFunction.cached( - cacheDirectory.value, - FilesInfo.lastModified, - FilesInfo.exists - ){(inFiles: Set[File]) => - val pathMap = Path.relativeTo((sourceDirectory in Compile).value / "js") - IO.copy(inFiles.map{f => f -> crossTarget.value / pathMap(f).get}) - }( - for{ - (file, path) <- Path.allSubpaths((sourceDirectory in Compile).value / "js").toSet - } yield file - - ) - val normalResults: Seq[sbt.File] = (packageJS in Compile).value - - normalResults ++ bundledJSResults ++ copiedJSFiles - } - ) -} diff --git a/project/Build.scala b/project/Build.scala index 50dda40..726d8fd 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -1,10 +1,23 @@ import sbt._ +import Keys._ + object Build extends sbt.Build { import sbt._ - lazy val runtime = Project("runtime", file("runtime")) - lazy val plugin = Project("plugin", file("plugin")) - - + override lazy val projects = Seq(root) + lazy val root = + Project("scala-js-workbench", file(".")) + .dependsOn(uri("../WebSockets")) + .settings( + sbtPlugin := true, + (resources in Compile) := {(resources in Compile).value ++ (baseDirectory.value * "*.js").get}, + resolvers += "spray repo" at "http://repo.spray.io", + resolvers += "typesafe" at "http://repo.typesafe.com/typesafe/releases/", + libraryDependencies ++= Seq( + "io.spray" % "spray-can" % "1.2-RC3", + "com.typesafe.akka" %% "akka-actor" % "2.2.3", + "com.typesafe.play" %% "play-json" % "2.2.0-RC1" + ) + ) } \ No newline at end of file diff --git a/project/plugins.sbt b/project/plugins.sbt deleted file mode 100644 index 142a3c7..0000000 --- a/project/plugins.sbt +++ /dev/null @@ -1 +0,0 @@ -addSbtPlugin("org.scala-lang.modules.scalajs" % "scalajs-sbt-plugin" % "0.1-SNAPSHOT") \ No newline at end of file diff --git a/runtime/build.sbt b/runtime/build.sbt deleted file mode 100644 index fa2803f..0000000 --- a/runtime/build.sbt +++ /dev/null @@ -1 +0,0 @@ -scalaJSSettings \ No newline at end of file diff --git a/runtime/project/plugins.sbt b/runtime/project/plugins.sbt deleted file mode 100644 index 142a3c7..0000000 --- a/runtime/project/plugins.sbt +++ /dev/null @@ -1 +0,0 @@ -addSbtPlugin("org.scala-lang.modules.scalajs" % "scalajs-sbt-plugin" % "0.1-SNAPSHOT") \ No newline at end of file diff --git a/runtime/src/main/scala/scala/scalajs/js/Resource.scala b/runtime/src/main/scala/scala/scalajs/js/Resource.scala deleted file mode 100644 index 632ebb9..0000000 --- a/runtime/src/main/scala/scala/scalajs/js/Resource.scala +++ /dev/null @@ -1,30 +0,0 @@ -package scala.scalajs -package js - - -object Resource { - println("Resource") - val fileDict = { - val fileDict = js.Dynamic.global.ScalaJS.resources.asInstanceOf[js.Object] - for(key <- js.Object.keys(fileDict)){ - val data = fileDict.asInstanceOf[js.Dictionary](key).asInstanceOf[String] - fileDict.asInstanceOf[js.Dictionary](key) = new Resource(data).asInstanceOf[js.Any] - } - fileDict - } - - println("Resource Initialized") - def apply(path: String) = { - js.Dynamic.global - .ScalaJS - .resources - .asInstanceOf[js.Dictionary] - .apply(path) - .asInstanceOf[Resource] - } - def create(value: String) = new Resource(value) -} - -class Resource(base64: String){ - lazy val string = js.Dynamic.global.atob(base64).asInstanceOf[js.String] -} \ No newline at end of file diff --git a/workbench_template.js b/workbench_template.js new file mode 100644 index 0000000..e5ae48d --- /dev/null +++ b/workbench_template.js @@ -0,0 +1,25 @@ +var socket = (function(){ + var open = false + var start = function(){ + socket = new WebSocket("ws://:/") + socket.onopen = function(event){ + open = true + console.log("Host connection Opened") + } + socket.onmessage = function(event){ + var data = JSON.parse(event.data) + if (data[0] == "reload") { + console.log("Reloading page...") + location.reload(true) + } + if (data[0] == "print") console[data[1]](data[2]) + } + socket.onclose = function(event){ + if (open) console.log("Host connection Closed") + open = false + setTimeout(function(){start()}, 1000) + } + } + start() + return socket +})() -- cgit v1.2.3