Skip to content
This repository was archived by the owner on Dec 4, 2022. It is now read-only.

Add Javascript support #47

Draft
wants to merge 10 commits into
base: development
Choose a base branch
from
2 changes: 2 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ dependencies {
api("com.squareup.okhttp3:okhttp:4.10.0")
implementation("com.google.guava:guava:31.1-jre")

// rhino
implementation("org.mozilla:rhino:1.7.14")

// ### Testing Libraries
testImplementation(kotlin("stdlib"))
Expand Down
2 changes: 1 addition & 1 deletion src/main/kotlin/app/shosetsu/lib/Novel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ class Novel {
var genres: Array<String> = arrayOf(),
var authors: Array<String> = arrayOf(),
var artists: Array<String> = arrayOf(),
var chapters: List<Chapter> = arrayListOf()
var chapters: Array<Chapter> = arrayOf()
)

/** Represents the data type of a chapter */
Expand Down
55 changes: 55 additions & 0 deletions src/main/kotlin/app/shosetsu/lib/js/Ext.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package app.shosetsu.lib.js

import org.mozilla.javascript.Context
import org.mozilla.javascript.Function
import org.mozilla.javascript.NativeArray
import org.mozilla.javascript.Scriptable

/*
* 04 / 08 / 2022
*/

@Throws(NullPointerException::class, IllegalArgumentException::class)
inline fun <reified O> Scriptable.getOrThrow(key: String, scope: Scriptable): O {
val obj = this.get(key, scope)
if (obj == Scriptable.NOT_FOUND) {
throw NullPointerException("`$key` not found in script")
}
return Context.jsToJava(obj, O::class.java) as O
}

@Throws(NullPointerException::class, IllegalArgumentException::class)
inline fun <reified O> Scriptable.getArrayOrThrow(key: String, scope: Scriptable): Array<O> {
val obj = this.get(key, scope)
if (obj == Scriptable.NOT_FOUND) {
throw NullPointerException("`$key` not found in script")
}
if (obj is NativeArray) {
return obj.map { Context.jsToJava(it, O::class.java) as O }.toTypedArray()
}
throw ClassCastException("Return is not an array, is `${obj::class.simpleName}`")
}

@Throws(NullPointerException::class)
fun Scriptable.callOrThrow(
context: Context,
key: String,
scope: Scriptable,
vararg arguments: Any?
) {
val func = this.getOrThrow(key, scope) as Function
func.call(context, scope, scope, arguments)
}

@Throws(NullPointerException::class)
inline fun <reified O> Scriptable.callOrThrowReturn(
context: Context,
key: String,
scope: Scriptable,
vararg arguments: Any?
): O? {
val func = this.getOrThrow(key, scope) as Function
val result = func.call(context, scope, scope, arguments)

return Context.jsToJava(result, O::class.java) as O
}
30 changes: 30 additions & 0 deletions src/main/kotlin/app/shosetsu/lib/js/Globals.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package app.shosetsu.lib.js

import org.mozilla.javascript.Context
import org.mozilla.javascript.NativeArray
import org.mozilla.javascript.Scriptable

/**
* 03 / 08 / 2022
*/
fun shosetsuJSGlobals(context: Context): Scriptable {
val scriptable = context.initSafeStandardObjects()
scriptable.put(
"lib",
scriptable,
Context.toObject(ShosetsuJSLib(context, scriptable), scriptable)
)
scriptable.put("console", scriptable, Context.toObject(JSConsole(), scriptable))
return scriptable
}

class JSConsole {

fun log(array: NativeArray) {
log(array.toArray().contentToString())
}

fun log(any: Any) {
println(any)
}
}
114 changes: 114 additions & 0 deletions src/main/kotlin/app/shosetsu/lib/js/JSExtension.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
package app.shosetsu.lib.js

import app.shosetsu.lib.Filter
import app.shosetsu.lib.IExtension
import app.shosetsu.lib.Novel
import app.shosetsu.lib.lua.*
import org.mozilla.javascript.Context
import java.io.InputStream

/**
* 03 / 08 / 2022
*
* Javascript extension, utilizing rhino
*/
class JSExtension : IExtension {
private val jsContext by lazy { Context.enter() }
private val scope by lazy { shosetsuJSGlobals(jsContext) }


constructor(
content: String, name: String
) {
jsContext.evaluateString(scope, content, name, 0, null)
}

constructor(
inputStream: InputStream, name: String
) {
jsContext.evaluateReader(scope, inputStream.reader(), name, 0, null)
}

override val exMetaData: IExtension.ExMetaData
get() = TODO("Not yet implemented")

override val name: String by lazy {
scope.getOrThrow(KEY_NAME, scope)
}
override val baseURL: String by lazy {
scope.getOrThrow(KEY_BASE_URL, scope)
}
override val imageURL: String by lazy {
scope.getOrThrow(KEY_IMAGE_URL, scope)
}
override val formatterID: Int by lazy {
scope.getOrThrow(KEY_ID, scope)
}
override val hasSearch: Boolean by lazy {
scope.getOrThrow(KEY_HAS_SEARCH, scope)
}
override val isSearchIncrementing: Boolean by lazy {
scope.getOrThrow(KEY_IS_SEARCH_INC, scope)
}
override val hasCloudFlare: Boolean by lazy {
scope.getOrThrow(KEY_HAS_CLOUD_FLARE, scope)
}
override val listings: Array<IExtension.Listing> by lazy {
scope.getOrThrow(KEY_LISTINGS, scope)
}
override val settingsModel: Array<Filter<*>> by lazy {
scope.getArrayOrThrow(KEY_SETTINGS, scope)
}
override val searchFiltersModel: Array<Filter<*>> by lazy {
scope.getArrayOrThrow(KEY_SEARCH_FILTERS, scope)
}
override val chapterType: Novel.ChapterType by lazy {
scope.getOrThrow(KEY_CHAPTER_TYPE, scope) as Novel.ChapterType
}
override val startIndex: Int by lazy {
scope.getOrThrow<Double>(KEY_START_INDEX, scope).toInt()
}

override fun updateSetting(id: Int, value: Any?) {
scope.callOrThrow(jsContext, KEY_UPDATE_SETTING, scope, id, value)
}

override fun search(data: Map<Int, *>): Array<Novel.Listing> {
return scope.callOrThrowReturn(jsContext, KEY_SEARCH, scope, data) ?: emptyArray()
}

override fun getPassage(chapterURL: String): ByteArray {
return scope.callOrThrowReturn(jsContext, KEY_GET_PASSAGE, scope, chapterURL)
?: ByteArray(0)
}

override fun parseNovel(novelURL: String, loadChapters: Boolean): Novel.Info {
return scope.callOrThrowReturn(
jsContext,
KEY_PARSE_NOVEL,
scope,
novelURL,
loadChapters
)!!
}

override fun expandURL(smallURL: String, type: Int): String {
return scope.callOrThrowReturn(
jsContext,
KEY_EXPAND_URL,
scope,
smallURL,
type
)!!
}

override fun shrinkURL(longURL: String, type: Int): String {
return scope.callOrThrowReturn(
jsContext,
KEY_SHRINK_URL,
scope,
longURL,
type
)!!
}
}
Loading