Commit 842cfd86 authored by Mathieu leclaire's avatar Mathieu leclaire

Initiate scalatra server

parent 06b795de
import java.io.File
import org.scalatra.sbt.ScalatraPlugin
import org.scalajs.sbtplugin.ScalaJSPlugin.autoImport._
val Organization = "fr.iscpif"
val Name = "ScalaWUI"
val Organization = "openmole"
val Name = "gate"
val Version = "0.1.0-SNAPSHOT"
val ScalaVersion = "2.12.3"
val scalatraVersion = "2.5.1"
val jettyVersion = "9.4.6.v20170531"
val ScalaVersion = "2.12.4"
val scalatraVersion = "2.6.2"
val jettyVersion = "9.4.8.v20171121"
val json4sVersion = "3.5.2"
val scalatagsVersion = "0.6.5"
val autowireVersion = "0.2.6"
......@@ -24,14 +23,11 @@ lazy val shared = project.in(file("./shared")).settings(
scalaVersion := ScalaVersion
)
val jqueryPath = s"META-INF/resources/webjars/jquery/$jqueryVersion/jquery.js"
lazy val client = project.in(file("client")) settings(
version := Version,
scalaVersion := ScalaVersion,
resolvers in ThisBuild ++= Resolvers,
skip in packageJSDependencies := false,
jsDependencies += "org.webjars" % "d3js" % "4.2.1" / "d3.min.js",
libraryDependencies ++= Seq(
"com.lihaoyi" %%% "autowire" % autowireVersion,
"com.lihaoyi" %%% "upickle" % upickleVersion,
......
package client
import org.scalajs.dom
import scala.concurrent.Future
import rx._
import scala.scalajs.js.annotation.JSExport
import scala.scalajs.concurrent.JSExecutionContext.Implicits.runNow
@JSExport("Client")
object Client {
val helloValue = Var(0)
val caseClassValue = Var("empty")
@JSExport
def run() {
val nodes = Seq(
Graph.task("one", 400, 600),
Graph.task("two", 1000, 600),
Graph.task("three", 400, 100),
Graph.task("four", 1000, 100),
Graph.task("five", 105, 60)
)
val edges = Seq(
Graph.edge(nodes(0), nodes(1)),
Graph.edge(nodes(0), nodes(2)),
Graph.edge(nodes(3), nodes(1)),
Graph.edge(nodes(3), nodes(2)))
val window = new Window(nodes, edges)
}
}
object Post extends autowire.Client[String, upickle.default.Reader, upickle.default.Writer] {
override def doCall(req: Request): Future[String] = {
val url = req.path.mkString("/")
dom.ext.Ajax.post(
url = "http://localhost:8080/" + url,
data = upickle.default.write(req.args)
).map {
_.responseText
}
}
def read[Result: upickle.default.Reader](p: String) = upickle.default.read[Result](p)
def write[Result: upickle.default.Writer](r: Result) = upickle.default.write(r)
}
package client
/*
* Copyright (C) 22/09/14 // mathieu.leclaire@openmole.org
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import java.util.UUID
import org.scalajs.dom
import scala.scalajs.js
import rx._
import scalatags.JsDom.all._
import scalatags.JsDom.svgAttrs
import scalatags.JsDom.svgTags
import scaladget.stylesheet.all._
import scaladget.api.svg._
import scaladget.tools.JsRxTags._
import org.scalajs.dom.raw._
trait Selectable {
val selected: Var[Boolean] = Var(false)
}
// DEFINE SOME CASE CLASS TO STORE TASK AND EDGE STRUCTURES
object Graph {
case class Task(title: Var[String] = Var(""),
location: Var[(Double, Double)] = Var((0.0, 0.0))) extends Selectable
class Edge(val source: Var[Task],
val target: Var[Task]) extends Selectable
def task(title: String, x: Double, y: Double) = Task(Var(title), Var((x, y)))
def edge(source: Task, target: Task) = new Edge(Var(source), Var(target))
}
import Graph._
class Window(nodes: Seq[Task] = Seq(), edges: Seq[Edge] = Seq()) {
val svgNode = {
val child = svgTags.svg(
width := 2500,
height := 2500
).render
dom.document.body.appendChild(child.render)
child
}
new GraphCreator(svgNode,
nodes,
edges
)
}
class GraphCreator(svg: SVGElement, _tasks: Seq[Task], _edges: Seq[Edge]) {
implicit val ctx: Ctx.Owner = Ctx.Owner.safe()
val SELECTED: String = "selected"
val CIRCLE: String = "conceptG"
val LINK: String = "link"
val LINK_DRAGLINE: String = "link dragline"
val HIDDEN: String = "hidden"
val DELETE_KEY = 46
val NODE_RADIUS = 50
val END_ARROW: String = "end-arrow"
val URL_END_ARROW: String = s"url(#${END_ARROW})"
val MARK_END_ARROW: String = "mark-end-arrow"
val URL_MARK_END_ARROW: String = s"url(#${MARK_END_ARROW})"
// DEFINE A SVG ELEMENT TO DISPLAY A PHANTOM LINK WHILE DRAGGING A LINK FROM ONE TASK TO ANOTHER
class DragLine {
private val m: Var[(Int, Int)] = Var((0, 0))
private val l: Var[(Int, Int)] = Var((0, 0))
val dragging = Var(false)
def move(x: Int, y: Int) = {
dragging() = true
m() = (x, y)
this
}
def line(x: Int, y: Int) = {
dragging() = true
l() = (x, y)
this
}
val render: SVGElement = Rx {
path(ms = ms(s"$LINK_DRAGLINE ${
if (dragging()) "" else HIDDEN
}")).m(m()._1, m()._2).l(l()._1, l()._2)(svgAttrs.markerEnd := URL_MARK_END_ARROW).render
}
}
implicit def dynamicToString(d: js.Dynamic): String = d.asInstanceOf[String]
implicit def dynamicToBoolean(d: js.Dynamic): Boolean = d.asInstanceOf[Boolean]
// SVG DEFINITIONS
val svgG = svgTags.g.render
val defs = svgTags.defs.render
val dragLine = new DragLine
svgG.appendChild(dragLine.render)
svg.appendChild(svgG)
svg.appendChild(defs)
val mouseDownTask: Var[Option[Task]] = Var(None)
val dragging: Var[Option[DragLine]] = Var(None)
// EVENT DEFINITIONS FOR MOUSEUP AND MOUSEDOWN ON THE SCENE
svg.onmousemove = (me: MouseEvent) => mousemove(me)
svg.onmouseup = (me: MouseEvent) => mouseup(me)
def mousemove(me: MouseEvent) = {
Seq(mouseDownTask.now).flatten.map { t
val x = me.clientX.toInt
val y = me.clientY.toInt
if (me.shiftKey) {
dragLine.move(t.location.now._1.toInt, t.location.now._2.toInt).line(x, y)
}
else {
t.location() = (x, y)
}
}
}
def mouseup(me: MouseEvent) = {
// Hide the drag line
if (me.shiftKey && !dragLine.dragging.now) {
val (x, y) = (me.clientX, me.clientY)
addTask(task(UUID.randomUUID().toString, x, y))
}
mouseDownTask() = None
dragLine.dragging() = false
}
// ARROW MARKERS FOR GRAPH LINKS
def arrow = svgTags.marker(
svgAttrs.viewBox := "0 -5 10 10",
svgAttrs.markerWidth := "3.5",
svgAttrs.markerHeight := "3.5",
svgAttrs.orient := "auto",
svgAttrs.refX := 32
)
def endArrowMarker = arrow(
id := END_ARROW,
svgAttrs.refX := 32,
path.start(0, -5).l(10, 0).l(0, 5).render
).render
def markEndArrow = arrow(
id := MARK_END_ARROW,
svgAttrs.refX := 7,
path.start(0, -5).l(10, 0).l(0, 5).render
).render
defs.appendChild(endArrowMarker)
defs.appendChild(markEndArrow)
lazy val tasks: Var[Seq[Var[Task]]] = Var(Seq())
_tasks.map {
addTask
}
// RETURN A SVG CIRCLE, WHICH CAN BE SELECTED (ON CLICK), MOVED OR DELETED (DEL KEY)
def circle(task: Task) = {
val element: SVGElement = Rx {
svgTags.g(
ms(CIRCLE + {
if (task.selected()) s" $SELECTED" else ""
})
)(
svgAttrs.transform := s"translate(${
val location = task.location()
s"${location._1}, ${location._2}"
})")(svgTags.circle(svgAttrs.r := NODE_RADIUS).render)
}
val gCircle = svgTags.g(element).render
gCircle.onmousedown = (me: MouseEvent) => {
mouseDownTask() = Some(task)
me.stopPropagation
unselectTasks
unselectEdges
task.selected() = !task.selected.now
}
gCircle.onmouseup = (me: MouseEvent) => {
Seq(mouseDownTask.now).flatten.map { mdt
if (task != mdt) {
addEdge(edge(mdt, task))
}
}
}
gCircle
}
lazy val edges: Var[Seq[Var[Edge]]] = Var(Seq())
_edges.map { e =>
addEdge(edge(e.source.now, e.target.now))
}
// DEFINE A LINK, WHICH CAN BE SELECTED AND REMOVED (DEL KEY)
def link(edge: Edge) = {
val sVGElement: SVGElement = Rx {
val p = path(ms = (if (edge.selected()) ms(SELECTED) else emptyMod) +++
ms(LINK)).m(
edge.source().location()._1.toInt,
edge.source().location()._2.toInt
).l(
edge.target().location()._1.toInt,
edge.target().location()._2.toInt
).render(svgAttrs.markerEnd := URL_END_ARROW).render
p.onmousedown = (me: MouseEvent) => {
unselectTasks
unselectEdges
edge.selected() = !edge.selected.now
}
p
}
svgTags.g(sVGElement).render
}
// ADD ALL LINKS AND CIRCLES ON THE SCENE. THE RX SEQUENCE IS AUTOMATICALLY RUN IN CASE OF tasks or edges ALTERATION
def addToScene[T](s: Var[Seq[Var[T]]], draw: T => SVGElement) = {
val element: SVGElement = Rx {
svgTags.g(
for {
t <- s()
} yield {
draw(t.now)
}
)
}
svgG.appendChild(svgTags.g(element).render).render
}
addToScene(edges, link)
addToScene(tasks, circle)
// DEAL WITH DEL KEY ACTION
dom.document.onkeydown = (e: KeyboardEvent) => {
e.keyCode match {
case DELETE_KEY
tasks.now.filter(t t.now.selected.now).map { t
removeTask(t)
}
edges.now.filter(e e.now.selected.now).map { e
removeEdge(e)
}
case _
}
}
// ADD, SELECT AND REMOVE ITEMS
def unselectTasks = tasks.now.foreach { t t.now.selected() = false }
def unselectEdges = edges.now.foreach { e e.now.selected() = false }
def removeTask(t: Var[Task]) = {
tasks() = tasks.now diff Seq(t)
edges() = edges.now.filterNot(e e.now.source.now == t.now || e.now.target.now == t.now)
}
def removeEdge(e: Var[Edge]) = {
edges() = edges.now diff Seq(e)
}
def addTask(task: Task): Unit = {
tasks() = tasks.now :+ Var(task)
}
def addEdge(edge: Edge): Unit = {
edges() = edges.now :+ Var(edge)
}
}
\ No newline at end of file
sbt.version=0.13.13
sbt.version=1.0.4
\ No newline at end of file
resolvers += "Typesafe repository" at "http://repo.typesafe.com/typesafe/releases/"
addSbtPlugin("com.github.mpeltonen" % "sbt-idea" % "1.5.1")
addSbtPlugin("org.scalatra.sbt" % "sbt-scalatra" % "1.0.1")
addSbtPlugin("org.scalatra.sbt" % "scalatra-sbt" % "0.5.1")
addSbtPlugin("org.scala-js" % "sbt-scalajs" % "0.6.18")
addSbtPlugin("org.scala-js" % "sbt-scalajs" % "0.6.21")
import fr.iscpif.app._
import openmole.gate.server._
import org.scalatra._
import javax.servlet.ServletContext
......
package fr.iscpif.app
import org.scalatra._
import scala.concurrent.ExecutionContext.Implicits.global
import upickle.default
import autowire._
import shared._
import upickle._
import scala.concurrent.duration._
import scala.concurrent.Await
import scalatags.Text.all._
import scalatags.Text.{all => tags}
object AutowireServer extends autowire.Server[String, upickle.default.Reader, upickle.default.Writer] {
def read[Result: upickle.default.Reader](p: String) = upickle.default.read[Result](p)
def write[Result: upickle.default.Writer](r: Result) = upickle.default.write(r)
}
object ApiImpl extends shared.Api {
}
class Servlet extends ScalatraServlet {
val basePath = "shared"
get("/") {
contentType = "text/html"
tags.html(
tags.head(
tags.meta(tags.httpEquiv := "Content-Type", tags.content := "text/html; charset=UTF-8"),
tags.link(tags.rel := "stylesheet", tags.`type` := "text/css", href := "css/styleWUI.css"),
tags.script(tags.`type` := "text/javascript", tags.src := "js/client-opt.js"),
tags.script(tags.`type` := "text/javascript", tags.src := "js/client-jsdeps.min.js")
),
tags.body(tags.onload := "Client().run();")
)
}
post(s"/$basePath/*") {
Await.result(AutowireServer.route[shared.Api](ApiImpl)(
autowire.Core.Request(Seq(basePath) ++ multiParams("splat").head.split("/"),
upickle.default.read[Map[String, String]](request.body))
), Duration.Inf)
}
}
package shared
package openmole.gate.shared
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment