Skip to content

Commit

Permalink
refactored the KoolApp JavaFX browser so its generic and reusable; us…
Browse files Browse the repository at this point in the history
…ing a <script> tag to bind a page to the application (so multi-page applications are supported) along with using the new kotlin.browser API to access the DOM. Hopefully soon the same kotlin browser applications will work both in JavaFX and compiled to JavaScript; the script tag just changes then based on if you are using JS or Kotlin/JVM
  • Loading branch information
jstrachan committed May 24, 2012
1 parent 7382216 commit cb6a65e
Show file tree
Hide file tree
Showing 8 changed files with 122 additions and 27 deletions.
26 changes: 22 additions & 4 deletions koolapp-javafx/ReadMe.md
Expand Up @@ -25,8 +25,26 @@ The demo should create a really simple Java application that boots up a browser,

Here's a breakdown of the source code used to implement this:

* [test.koolapp.myapp.MyApp.kt](https://github.com/koolapp/koolapp/blob/master/koolapp-javafx/src/test/kotlin/test/koolapp/myapp/MyApp.kt) the actual application
* [test.koolapp.javafx.Main.kt](https://github.com/koolapp/koolapp/blob/master/koolapp-javafx/src/test/kotlin/test/koolapp/javafx/Main.kt) : Java main() function launcher for the JavaFX web app
* [test.koolapp.javafx.SampleWebApp.kt](https://github.com/koolapp/koolapp/blob/master/koolapp-javafx/src/test/kotlin/test/koolapp/javafx/SampleWebApp.kt) : JavaFX web app (i.e. a kinda browser app which then loads our myApp function when the browser is ready and the single page HTML)
* [test.koolapp.myapp.MyApp.kt](https://github.com/koolapp/koolapp/blob/master/koolapp-javafx/src/test/kotlin/test/koolapp/myapp/MyApp.kt) the actual application which interacts with the DOM using the standard [kotlin.browser](http://jetbrains.github.com/kotlin/versions/snapshot/apidocs/kotlin/browser/package-summary.html) package.
* [org.koolapp.javafx.WebApplication](https://github.com/koolapp/koolapp/tree/master/koolapp-javafx/src/main/kotlin/org/koolapp/javafx/WebApplication.kt) : standard KoolApp browser Application
* [org.koolapp.javafx.namespace](https://github.com/koolapp/koolapp/tree/master/koolapp-javafx/src/main/kotlin/org/koolapp/javafx/) : Java main() function launcher for the JavaFX web app

The application code - the [myapp() function](https://github.com/koolapp/koolapp/blob/master/koolapp-javafx/src/test/kotlin/test/koolapp/myapp/MyApp.kt) should be usable when compiled to JavaScript directly. The code in the javafx package is only required if you want to run the application on a JVM with JavaFX
The application code - the [myapp() function](https://github.com/koolapp/koolapp/blob/master/koolapp-javafx/src/test/kotlin/test/koolapp/myapp/MyApp.kt) should be usable when compiled to JavaScript directly. The code in the org.koolapp.javafx package is only required if you want to run the application on a JVM with JavaFX.

### How a KoolApp JavaFX application works

When using your web application in the KoolApp JavaFX browser, you need to bind your Kotlin application code to the web page. This is done by adding a script tag (or using the WebApplication.ready() function to pass a block when the document is ready).

For example if you add this to a HTML file:

<script type="text/kotlin">
test.koolapp.myapp.namespace.myApp()
</script>

when opening in the HTML in the KoolApp JavaFX browser (running the org.koolapp.javafx.namespace class as a Java main() function)

java org.koolapp.javafx.namespace file://foo.html

The browser will startup, load the HTML and then when it sees the text/kotlin script it will invoke your function; once the document has loaded (and the kotlin.browser.document property is updated).

The same Kotlin code should then work if the code is compiled to JavaScript. The only change is required is that the script tag with text/kotlin needs to be replaced with the actual JavaScript file; this could be done dynamically on the server or as part of your build process.
4 changes: 3 additions & 1 deletion koolapp-javafx/pom.xml
Expand Up @@ -72,7 +72,9 @@
<phase>test</phase>
<configuration>
<target>
<java fork="true" classpathref="maven.test.classpath" classname="test.koolapp.javafx.namespace"/>
<java fork="true" classpathref="maven.test.classpath" classname="org.koolapp.javafx.namespace">
<arg value="file://src/test/resources/sample.html"/>
</java>
</target>
</configuration>
<goals>
Expand Down
12 changes: 12 additions & 0 deletions koolapp-javafx/src/main/kotlin/org/koolapp/javafx/Main.kt
@@ -0,0 +1,12 @@
package org.koolapp.javafx

import javafx.application.Application

/**
* A command line application which starts the JavaFX browser and boots up
* the application defined by the first URL parameter.
*/
fun main(args: Array<String>): Unit {
println("Starting KoolApp browser: http://koolapp.org/ - pleasae keep kool!")
Application.launch(javaClass<WebApplication>(), args.makeString(" "))
}
Expand Up @@ -4,6 +4,7 @@ import java.io.File
import javafx.application.Application
import javafx.beans.value.ChangeListener
import javafx.beans.value.ObservableValue
import javafx.concurrent.Worker.State
import javafx.event.ActionEvent
import javafx.event.EventHandler
import javafx.geometry.HPos
Expand All @@ -17,8 +18,9 @@ import javafx.scene.layout.Priority
import javafx.scene.web.WebEngine
import javafx.scene.web.WebView
import javafx.stage.Stage
import kotlin.browser.*
import kotlin.dom.*
import org.w3c.dom.Document
import javafx.concurrent.Worker.State

/**
* An [[Application]] which uses a [[WebView]] and [[WebEngine]]
Expand All @@ -27,10 +29,10 @@ public open class WebApplication(): Application() {
private var _engine: WebEngine? = null

var engine: WebEngine
get() = _engine!!
set(value) {
_engine = value
}
get() = _engine!!
set(value) {
_engine = value
}

var prefWidth = 800.0
var prefHeight = 600.0
Expand All @@ -42,9 +44,9 @@ public open class WebApplication(): Application() {
val changeListener = object : ChangeListener<State?> {
public override fun changed(observable: ObservableValue<out State?>?, oldValue: State?, newValue: State?) {
if (newValue != null && newValue == State.SUCCEEDED) {
val document = engine.getDocument()
if (document != null) {
block(document)
val doc = engine.getDocument()
if (doc != null) {
block(doc)
}
if (fireOnce) {
engine.getLoadWorker()?.stateProperty()?.removeListener(this)
Expand All @@ -63,6 +65,11 @@ public open class WebApplication(): Application() {
view.setPrefSize(prefWidth, prefHeight)
view.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);

ready {
// lets register the global DOM instance
document = it
loadKotlinScripts(it)
}
val initialLocation = loadInitial()

val locationField = TextField(initialLocation)
Expand Down Expand Up @@ -120,7 +127,17 @@ public open class WebApplication(): Application() {
* so it can be populated into the address bar
*/
protected open fun loadInitial(): String {
return load("http://koolapp.org/")
val parameters = getParameters()!!
val unnamed = parameters.getUnnamed()!!
val raw = parameters.getRaw()!!
val url = if (unnamed.notEmpty()) {
unnamed.first()!!
} else if (raw.notEmpty()) {
raw.first()!!
} else {
"http://koolapp.org/"
}
return load(url)
}

/**
Expand All @@ -139,5 +156,43 @@ public open class WebApplication(): Application() {
}
return url
}

/**
* Attempts to find any &lt;script&gt; tags for the type *text/kotlin* and invokes the methods
*/
protected fun loadKotlinScripts(document: Document): Unit {
val scriptTags = document["script"]
for (scriptTag in scriptTags) {
val t = scriptTag.attribute("type")
if (t == "text/kotlin") {
val text = scriptTag.text.trim()
val lines = text.split("\n")
for (line in lines) {
val initCall = line.trim().trimTrailing("()")
if (initCall.notEmpty()) {
val idx = initCall.lastIndexOf('.')
if (idx <= 0) {
println("Warning cannot invoke initCall which is not on a class $initCall")
} else {
try {
val className = initCall.substring(0, idx)
val methodName = initCall.substring(idx + 1)
val klass = loadClass<Any>(className)
val method = klass.getMethod(methodName)!!
method.invoke(null)
} catch (e: Exception) {
println("Failed to invoke startup method: " + initCall + ". Reason: " + e)
e.printStackTrace()
}
}
}
}
}
}
}

protected fun <T> loadClass(className: String): Class<T> {
return Class.forName(className) as Class<T>
}
}

8 changes: 0 additions & 8 deletions koolapp-javafx/src/test/kotlin/test/koolapp/javafx/Main.kt

This file was deleted.

Expand Up @@ -6,9 +6,13 @@ import test.koolapp.myapp.myApp
public class SampleWebApp(): WebApplication() {

override fun loadInitial(): String {
/*
This is another way of registering an application on a page instead of using the <script> tag for the kotlin
ready {
myApp(it)
myApp()
}
*/
return load("file://src/test/resources/sample.html")
}
}
Expand Down
9 changes: 6 additions & 3 deletions koolapp-javafx/src/test/kotlin/test/koolapp/myapp/MyApp.kt
Expand Up @@ -3,15 +3,18 @@ package test.koolapp.myapp
import org.koolapp.template.html.*
import org.w3c.dom.Document
import org.w3c.dom.Node
import kotlin.browser.*
import kotlin.dom.*

/**
* Entry point to my application which can be called
* from a JS browser when its ready, or from JavaFX
*/
fun myApp(document: Document) {
fun myApp() {
val newNode = sampleTemplate(document)
println("About to insert DOM node $newNode")
println("About to insert DOM node $newNode into document $document")
println("New XML: ${newNode.toXmlString()}")

val container = document.getElementById("view")
if (container != null) {
container.appendChild(newNode)
Expand All @@ -21,6 +24,6 @@ fun myApp(document: Document) {

fun sampleTemplate(document: Document): Node {
return document.div {
h1("Kool Template Generated Content!")
h2("Kool Template Generated Content!") {}
}
}
11 changes: 10 additions & 1 deletion koolapp-javafx/src/test/resources/sample.html
@@ -1,12 +1,21 @@
<!DOCTYPE html>
<html>
<head>
<title>Sample</title>
<title>Sample</title>

<!-- this is the bootstrap mechanism to initialise our Kotlin application -->
<script type="text/kotlin">
test.koolapp.myapp.namespace.myApp()
</script>
</head>
<body>
<h1>Sample</h1>

<p>This example HTML was loaded from a local file!</p>

<div id="view"/>

<p>Should have generated stuff above...</p>

</body>
</html>

0 comments on commit cb6a65e

Please sign in to comment.