Commit f8b0d4b5 authored by Mathieu leclaire's avatar Mathieu leclaire

Merge branch 'form'

Conflicts:
	client/src/main/scala/fr/iscpif/client/FlowChart.scala
parents 15e1244c aae3aec2
package client package client
import java.util.UUID
import org.scalajs.dom import org.scalajs.dom
import org.scalajs.dom.HTMLElement
import scala.concurrent.Future import scala.concurrent.Future
import scalatags.JsDom._ import scalatags.JsDom._
import all._ import all._
...@@ -11,13 +8,9 @@ import tags2.section ...@@ -11,13 +8,9 @@ import tags2.section
import rx._ import rx._
import scala.scalajs.js.annotation.JSExport import scala.scalajs.js.annotation.JSExport
import scala.scalajs.concurrent.JSExecutionContext.Implicits.runNow import scala.scalajs.concurrent.JSExecutionContext.Implicits.runNow
import org.scalajs.dom.extensions.Ajax
import scala.Some
import shared._ import shared._
import upickle._ import upickle._
import autowire._ import autowire._
import JsRxTags._
@JSExport @JSExport
object Client { object Client {
...@@ -47,7 +40,7 @@ object Post extends autowire.Client[String, upickle.Reader, upickle.Writer] { ...@@ -47,7 +40,7 @@ object Post extends autowire.Client[String, upickle.Reader, upickle.Writer] {
override def doCall(req: Request): Future[String] = { override def doCall(req: Request): Future[String] = {
val url = req.path.mkString("/") val url = req.path.mkString("/")
dom.extensions.Ajax.post( dom.ext.Ajax.post(
url = "http://localhost:8080/" + url, url = "http://localhost:8080/" + url,
data = upickle.write(req.args) data = upickle.write(req.args)
).map { ).map {
......
package client package client
/* /*
* Copyright (C) 21/08/14 // mathieu.leclaire@openmole.org * Copyright (C) 22/09/14 // mathieu.leclaire@openmole.org
* *
* This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU Affero General Public License as published by
...@@ -16,17 +16,16 @@ package client ...@@ -16,17 +16,16 @@ package client
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
import fr.iscpif.scaladget.mapping._
import fr.iscpif.scaladget.d3mapping._
import org.scalajs.dom
import shared.Api import shared.Api
import org.scalajs.dom
import scala.scalajs.js import scala.scalajs.js
import js.Dynamic.{literal => lit, newInstance => jsnew} import js.Dynamic.{ literal lit }
import rx._ import rx._
import fr.iscpif.scaladget.d3._ import fr.iscpif.scaladget.d3._
import scala.scalajs.concurrent.JSExecutionContext.Implicits.runNow import scala.scalajs.concurrent.JSExecutionContext.Implicits.runNow
import autowire._ import autowire._
import js.JSConverters._
trait GraphElement <: EventStates { trait GraphElement <: EventStates {
def literal: js.Dynamic def literal: js.Dynamic
...@@ -57,35 +56,34 @@ class Window(nodes: Array[Task] = Array(), edges: Array[Edge] = Array()) { ...@@ -57,35 +56,34 @@ class Window(nodes: Array[Task] = Array(), edges: Array[Edge] = Array()) {
val svg = d3.select("body") val svg = d3.select("body")
.append("svg") .append("svg")
.attr("id", "workflow")
.attr("width", "2500px") .attr("width", "2500px")
.attr("height", "2500px") .attr("height", "2500px")
val graph = new GraphCreator(svg, val graph = new GraphCreator(svg,
nodes, nodes,
edges edges
) )
} }
case class Consts(selectedClass: js.String = "selected", case class Consts(selectedClass: String = "selected",
circleGClass: String = "conceptG", circleGClass: String = "conceptG",
graphClass: js.String = "graph", graphClass: String = "graph",
activeEditId: js.String = "active-editing", activeEditId: String = "active-editing",
DELETE_KEY: js.Number = 46, DELETE_KEY: Double = 46,
nodeRadius: js.Number = 50 nodeRadius: Double = 50)
)
class GraphCreator(svgSelection: Selection, _tasks: Array[Task], _edges: Array[Edge]) { class GraphCreator(svgSelection: Selection, _tasks: Array[Task], _edges: Array[Edge]) {
implicit def dynamicToString(d: js.Dynamic): String = d.asInstanceOf[js.String] implicit def dynamicToString(d: js.Dynamic): String = d.asInstanceOf[String]
implicit def dynamicToBoolean(d: js.Dynamic): Boolean = d.asInstanceOf[js.Boolean] implicit def dynamicToBoolean(d: js.Dynamic): Boolean = d.asInstanceOf[Boolean]
// SVG DEFINITIONS // // SVG DEFINITIONS //
val consts = new Consts val consts = new Consts
val svgG = svgSelection.append("g").classed(consts.graphClass, true) val svgG = svgSelection.append("g").classed(consts.graphClass, true)
val dragLine = svgG.append("svg:path") val dragLine = svgG.append("svg:path")
.attr("class", "link dragline hidden") .attr("class", "link dragline hidden")
.attr("d", "M0,0L0,0") .attr("d", "M0,0L0,0")
...@@ -98,8 +96,8 @@ class GraphCreator(svgSelection: Selection, _tasks: Array[Task], _edges: Array[E ...@@ -98,8 +96,8 @@ class GraphCreator(svgSelection: Selection, _tasks: Array[Task], _edges: Array[E
val dragging: Var[Boolean] = Var(false) val dragging: Var[Boolean] = Var(false)
svgSelection svgSelection
.on("mousemove", (_: js.Any, _: js.Number) => mousemove) .on("mousemove", (_: js.Any, _: Double) mousemove)
.on("mouseup.scene", (_: js.Any, _: js.Number) => mouseup) .on("mouseup.scene", (_: js.Any, _: Double) mouseup)
// define arrow markers for graph links // define arrow markers for graph links
defs.append("svg:marker") defs.append("svg:marker")
...@@ -123,7 +121,6 @@ class GraphCreator(svgSelection: Selection, _tasks: Array[Task], _edges: Array[E ...@@ -123,7 +121,6 @@ class GraphCreator(svgSelection: Selection, _tasks: Array[Task], _edges: Array[E
.append("svg:path") .append("svg:path")
.attr("d", "M0,-5L10,0L0,5") .attr("d", "M0,-5L10,0L0,5")
val tasks: Var[Array[Var[Task]]] = Var(Array()) val tasks: Var[Array[Var[Task]]] = Var(Array())
_tasks.map { _tasks.map {
addTask addTask
...@@ -134,25 +131,30 @@ class GraphCreator(svgSelection: Selection, _tasks: Array[Task], _edges: Array[E ...@@ -134,25 +131,30 @@ class GraphCreator(svgSelection: Selection, _tasks: Array[Task], _edges: Array[E
addEdge addEdge
} }
val svgElement = js.Dynamic.global.document.getElementById("workflow")
// GLOBAL EVENTS // // GLOBAL EVENTS //
d3.select(dom.window) d3.select(dom.window)
.on("keydown", (_: js.Any, _: js.Number) => { .on("keydown", (_: js.Any, _: Double) {
d3.event.keyCode match { d3.event.keyCode match {
case consts.DELETE_KEY => case consts.DELETE_KEY
tasks().filter(t => t().selected()).map { t => tasks().filter(t t().selected()).map { t
removeTask(t) removeTask(t)
} }
edges().filter(e => e().selected()).map { e => edges().filter(e e().selected()).map { e
removeEdge(e) removeEdge(e)
} }
case _ => case _
} }
}) })
def mouseXY = d3.mouse(svgElement)
def mousemove = { def mousemove = {
Seq(mouseDownTask()).flatten.map { t => Seq(mouseDownTask()).flatten.map { t
val x = d3.event.clientX val xy = mouseXY
val y = d3.event.clientY val x = xy(0)
val y = xy(1)
if (d3.event.shiftKey) { if (d3.event.shiftKey) {
dragging() = true dragging() = true
dragLine.attr("d", "M" + t.location()._1 + "," + t.location()._2 + "L" + x + "," + y) dragLine.attr("d", "M" + t.location()._1 + "," + t.location()._2 + "L" + x + "," + y)
...@@ -165,8 +167,9 @@ class GraphCreator(svgSelection: Selection, _tasks: Array[Task], _edges: Array[E ...@@ -165,8 +167,9 @@ class GraphCreator(svgSelection: Selection, _tasks: Array[Task], _edges: Array[E
def mouseup = { def mouseup = {
// Hide the drag line // Hide the drag line
val xy = mouseXY
if (d3.event.shiftKey && !dragging()) { if (d3.event.shiftKey && !dragging()) {
val (x,y) = (d3.event.clientX, d3.event.clientY) val (x, y) = (xy(0), xy(1))
Post[Api].uuid.call().foreach{ i=> Post[Api].uuid.call().foreach{ i=>
addTask(i, i, x, y) addTask(i, i, x, y)
...@@ -179,15 +182,14 @@ class GraphCreator(svgSelection: Selection, _tasks: Array[Task], _edges: Array[E ...@@ -179,15 +182,14 @@ class GraphCreator(svgSelection: Selection, _tasks: Array[Task], _edges: Array[E
.style("marker-end", " ") .style("marker-end", " ")
} }
// ADD, SELECT AND REMOVE ITEMS // // ADD, SELECT AND REMOVE ITEMS //
def unselectTasks = tasks().foreach { t => t().selected() = false} def unselectTasks = tasks().foreach { t t().selected() = false }
def unselectEdges = edges().foreach { e => e().selected() = false} def unselectEdges = edges().foreach { e e().selected() = false }
def removeTask(t: Var[Task]) = { def removeTask(t: Var[Task]) = {
tasks() = tasks() diff Array(t) tasks() = tasks() diff Array(t)
edges() = edges().filterNot(e => e().source() == t() || e().target() == t()) edges() = edges().filterNot(e e().source() == t() || e().target() == t())
} }
def removeEdge(e: Var[Edge]) = { def removeEdge(e: Var[Edge]) = {
...@@ -200,7 +202,7 @@ class GraphCreator(svgSelection: Selection, _tasks: Array[Task], _edges: Array[E ...@@ -200,7 +202,7 @@ class GraphCreator(svgSelection: Selection, _tasks: Array[Task], _edges: Array[E
tasks() = tasks() :+ Var(task) tasks() = tasks() :+ Var(task)
Obs(tasks) { Obs(tasks) {
val mysel = circleRoot.selectAll("g").data(tasks(), (task: Var[Task], n: js.Number) => { val mysel = circleRoot.selectAll("g").data(tasks().toJSArray, (task: Var[Task], n: Double) {
task().id.toString task().id.toString
}) })
...@@ -209,17 +211,17 @@ class GraphCreator(svgSelection: Selection, _tasks: Array[Task], _edges: Array[E ...@@ -209,17 +211,17 @@ class GraphCreator(svgSelection: Selection, _tasks: Array[Task], _edges: Array[E
Rx { Rx {
newG.classed(consts.circleGClass, true) newG.classed(consts.circleGClass, true)
.attr("transform", (task: Var[Task]) => { .attr("transform", (task: Var[Task]) {
val loc = task().location() val loc = task().location()
"translate(" + loc._1 + "," + loc._2 + ")" "translate(" + loc._1 + "," + loc._2 + ")"
}) })
newG.classed(consts.selectedClass, (task: Var[Task]) => { newG.classed(consts.selectedClass, (task: Var[Task]) {
task().selected() task().selected()
}) })
} }
newG.on("mousedown", (t: Var[Task], n: js.Number) => { newG.on("mousedown", (t: Var[Task], n: Double) {
mouseDownTask() = Some(t()) mouseDownTask() = Some(t())
d3.event.stopPropagation d3.event.stopPropagation
...@@ -228,7 +230,6 @@ class GraphCreator(svgSelection: Selection, _tasks: Array[Task], _edges: Array[E ...@@ -228,7 +230,6 @@ class GraphCreator(svgSelection: Selection, _tasks: Array[Task], _edges: Array[E
unselectEdges unselectEdges
t().selected() = !t().selected() t().selected() = !t().selected()
if (d3.event.shiftKey) { if (d3.event.shiftKey) {
val x = t().location()._1 val x = t().location()._1
val y = t().location()._2 val y = t().location()._2
...@@ -238,8 +239,8 @@ class GraphCreator(svgSelection: Selection, _tasks: Array[Task], _edges: Array[E ...@@ -238,8 +239,8 @@ class GraphCreator(svgSelection: Selection, _tasks: Array[Task], _edges: Array[E
.attr("d", "M" + x + "," + y + "L" + x + "," + y) .attr("d", "M" + x + "," + y + "L" + x + "," + y)
} }
}) })
.on("mouseup.task", (t: Var[Task], n: js.Number) => { .on("mouseup.task", (t: Var[Task], n: Double) {
Seq(mouseDownTask()).flatten.map { mdt => Seq(mouseDownTask()).flatten.map { mdt
if (t() != mdt) { if (t() != mdt) {
addEdge(mdt, t()) addEdge(mdt, t())
} }
...@@ -251,14 +252,13 @@ class GraphCreator(svgSelection: Selection, _tasks: Array[Task], _edges: Array[E ...@@ -251,14 +252,13 @@ class GraphCreator(svgSelection: Selection, _tasks: Array[Task], _edges: Array[E
} }
} }
def addEdge(source: Task, target: Task): Unit = addEdge(new Edge(Var(source), Var(target))) def addEdge(source: Task, target: Task): Unit = addEdge(new Edge(Var(source), Var(target)))
def addEdge(edge: Edge): Unit = { def addEdge(edge: Edge): Unit = {
edges() = edges() :+ Var(edge) edges() = edges() :+ Var(edge)
Obs(edges) { Obs(edges) {
val mysel = pathRoot.selectAll("path").data(edges(), (edge: Var[Edge], n: js.Number) => { val mysel = pathRoot.selectAll("path").data(edges().toJSArray, (edge: Var[Edge], n: Double) {
edge().source().id + "+" + edge().target().id edge().source().id + "+" + edge().target().id
}) })
...@@ -267,7 +267,7 @@ class GraphCreator(svgSelection: Selection, _tasks: Array[Task], _edges: Array[E ...@@ -267,7 +267,7 @@ class GraphCreator(svgSelection: Selection, _tasks: Array[Task], _edges: Array[E
Rx { Rx {
newPath.style("marker-end", "url(#end-arrow)") newPath.style("marker-end", "url(#end-arrow)")
.classed("link", true) .classed("link", true)
.attr("d", (edge: Var[Edge], n: js.Number) => { .attr("d", (edge: Var[Edge], n: Double) {
val source = edge().source().location() val source = edge().source().location()
val target = edge().target().location() val target = edge().target().location()
"M" + source._1 + "," + source._2 + "L" + target._1 + "," + target._2 "M" + source._1 + "," + source._2 + "L" + target._1 + "," + target._2
...@@ -275,12 +275,12 @@ class GraphCreator(svgSelection: Selection, _tasks: Array[Task], _edges: Array[E ...@@ -275,12 +275,12 @@ class GraphCreator(svgSelection: Selection, _tasks: Array[Task], _edges: Array[E
// update existing paths // update existing paths
newPath.style("marker-end", "url(#end-arrow)") newPath.style("marker-end", "url(#end-arrow)")
.classed(consts.selectedClass, (edge: Var[Edge], n: Number) => { .classed(consts.selectedClass, (edge: Var[Edge], n: Number) {
edge().selected() edge().selected()
} }
) )
newPath.on("mousedown", (edge: Var[Edge], n: js.Number) => { newPath.on("mousedown", (edge: Var[Edge], n: Double) {
unselectTasks unselectTasks
unselectEdges unselectEdges
edge().selected() = !edge().selected() edge().selected() = !edge().selected()
......
...@@ -47,7 +47,7 @@ object JsRxTags { ...@@ -47,7 +47,7 @@ object JsRxTags {
* the Obs onto the element itself so we have a reference to kill it when * the Obs onto the element itself so we have a reference to kill it when
* the element leaves the DOM (e.g. it gets deleted). * the element leaves the DOM (e.g. it gets deleted).
*/ */
implicit def rxMod[T <: dom.HTMLElement](r: Rx[HtmlTag]): Modifier = { implicit def rxMod[T <: dom.raw.HTMLElement](r: Rx[HtmlTag]): Modifier = {
def rSafe = r.toTry match { def rSafe = r.toTry match {
case Success(v) => v.render case Success(v) => v.render
case Failure(e) => span(e.toString, backgroundColor := "red").render case Failure(e) => span(e.toString, backgroundColor := "red").render
......
import sbt._ import sbt._
import Keys._ import Keys._
import org.scalatra.sbt._ import org.scalatra.sbt._
import org.scalatra.sbt.PluginKeys._ import org.scalatra.sbt.PluginKeys._
import fr.iscpif.jsmanager.JSManagerPlugin._ import org.scalajs.sbtplugin.ScalaJSPlugin
import org.scalajs.sbtplugin.ScalaJSPlugin.autoImport._
object ScalaTraJSTagsWireRxBuild extends Build { object ScalaTraJSTagsWireRxBuild extends Build {
val Organization = "fr.iscpif" val Organization = "fr.iscpif"
val Name = "ScalaTraJSTagsWireRx" val Name = "ScalaTraJSTagsWireRx"
val Version = "0.1.0-SNAPSHOT" val Version = "0.1.0-SNAPSHOT"
val ScalaVersion = "2.11.2" val ScalaVersion = "2.11.6"
val ScalatraVersion = "2.3.0" val ScalatraVersion = "2.3.0"
val Resolvers = Seq(Resolver.sonatypeRepo("snapshots"), val Resolvers = Seq(Resolver.sonatypeRepo("snapshots"),
"Typesafe repository" at "http://repo.typesafe.com/typesafe/releases/", "Typesafe repository" at "http://repo.typesafe.com/typesafe/releases/"
"bintray/non" at "http://dl.bintray.com/non/maven", )
Resolver.url("scala-js-releases",
url("http://dl.bintray.com/content/scala-js/scala-js-releases"))(
Resolver.ivyStylePatterns))
lazy val shared = project.in(file("./shared")).settings( lazy val shared = project.in(file("./shared")).settings(
scalaVersion := ScalaVersion scalaVersion := ScalaVersion
).settings(jsManagerSettings: _*) )
lazy val client = Project( lazy val client = Project(
"client", "client",
file("./client"), file("./client"),
settings = Defaults.defaultSettings ++ jsManagerSettings ++ Seq( settings = Seq(
version := Version, version := Version,
scalaVersion := ScalaVersion, scalaVersion := ScalaVersion,
resolvers ++= Resolvers, resolvers ++= Resolvers,
libraryDependencies ++= Seq( libraryDependencies ++= Seq(
"com.lihaoyi" %%% "autowire" % "0.2.3", "com.lihaoyi" %%% "autowire" % "0.2.5",
"com.lihaoyi" %%% "upickle" % "0.2.5", "com.lihaoyi" %%% "upickle" % "0.2.7",
"com.scalatags" %%% "scalatags" % "0.4.2", "com.lihaoyi" %%% "scalatags" % "0.4.6",
"com.scalarx" %%% "scalarx" % "0.2.6", "com.lihaoyi" %%% "scalarx" % "0.2.8",
"fr.iscpif" %%% "scaladget" % "0.1.0", "fr.iscpif" %%% "scaladget" % "0.5.0-SNAPSHOT",
"org.scala-lang.modules.scalajs" %%% "scalajs-dom" % "0.6" "org.scala-js" %%% "scalajs-dom" % "0.8.0"
),
//jsCall := "Client().run();",
outputPath := "server/src/main/webapp/"
) )
).dependsOn(shared) )
).dependsOn(shared) enablePlugins (ScalaJSPlugin)
lazy val server = Project( lazy val server = Project(
"server", "server",
file("./server"), file("./server"),
settings = Defaults.defaultSettings ++ ScalatraPlugin.scalatraWithJRebel ++ Seq( settings = ScalatraPlugin.scalatraWithJRebel ++ Seq(
organization := Organization, organization := Organization,
name := Name, name := Name,
version := Version, version := Version,
scalaVersion := ScalaVersion, scalaVersion := ScalaVersion,
resolvers ++= Resolvers, resolvers ++= Resolvers,
libraryDependencies ++= Seq( libraryDependencies ++= Seq(
"com.lihaoyi" %% "autowire" % "0.2.3", "com.lihaoyi" %% "autowire" % "0.2.5",
"com.lihaoyi" %% "upickle" % "0.2.5", "com.lihaoyi" %% "upickle" % "0.2.7",
"com.scalatags" %% "scalatags" % "0.4.2", "com.lihaoyi" %% "scalatags" % "0.4.6",
"org.scalatra" %% "scalatra" % ScalatraVersion, "org.scalatra" %% "scalatra" % ScalatraVersion,
"org.scalatra" %% "scalatra-specs2" % ScalatraVersion % "test", "org.scalatra" %% "scalatra-specs2" % ScalatraVersion % "test",
"ch.qos.logback" % "logback-classic" % "1.0.12" % "runtime", "ch.qos.logback" % "logback-classic" % "1.0.12" % "runtime",
"org.eclipse.jetty" % "jetty-webapp" % "8.1.8.v20121106" % "container", "org.eclipse.jetty" % "jetty-webapp" % "8.1.17.v20150415" % "container",
"org.eclipse.jetty.orbit" % "javax.servlet" % "3.0.0.v201112011016" % "container;provided;test" /*artifacts (Artifact("javax.servlet", "jar", "jar"))*/ "org.eclipse.jetty.orbit" % "javax.servlet" % "3.0.0.v201112011016" % "container;provided;test" /*artifacts (Artifact("javax.servlet", "jar", "jar"))*/
) )
) )
......
sbt.version=0.13.5 sbt.version=0.13.7
resolvers ++= Seq(Resolver.url("scala-js-releases",
url("http://dl.bintray.com/content/scala-js/scala-js-releases"))(
Resolver.ivyStylePatterns))
resolvers += "Typesafe repository" at resolvers += "Typesafe repository" at
"http://repo.typesafe.com/typesafe/releases/" "http://repo.typesafe.com/typesafe/releases/"
addSbtPlugin("fr.iscpif" %% "jsmanager" % "0.6.0") addSbtPlugin("org.scala-js" % "sbt-scalajs" % "0.6.3")
addSbtPlugin("org.scalatra.sbt" % "scalatra-sbt" % "0.3.5") addSbtPlugin("org.scalatra.sbt" % "scalatra-sbt" % "0.3.5")
package fr.iscpif.app package fr.iscpif.app
import java.util.UUID
import org.scalatra._ import org.scalatra._
import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.ExecutionContext.Implicits.global
import upickle._ import upickle._
...@@ -19,8 +17,6 @@ object AutowireServer extends autowire.Server[String, upickle.Reader, upickle.Wr ...@@ -19,8 +17,6 @@ object AutowireServer extends autowire.Server[String, upickle.Reader, upickle.Wr
} }
object ApiImpl extends Api { object ApiImpl extends Api {
def hello(a: Int) = a * 3
def caseClass = MyCaseClass("Hello !")
} }
class Servlet extends ScalatraServlet { class Servlet extends ScalatraServlet {
......
package shared package shared
import scala.scalajs.js.annotation.JSExport
@JSExport
case class MyCaseClass(hello: String)
trait Api { trait Api {
def hello(a: Int): Int
def caseClass(): MyCaseClass
def uuid(): String = java.util.UUID.randomUUID.toString def uuid(): String = java.util.UUID.randomUUID.toString
} }
\ No newline at end of file
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