aboutsummaryrefslogtreecommitdiff
path: root/app/src/main/kotlin/com/pitchedapps/frost/injectors/JsInjector.kt
blob: c7b4eaf829d1f3239da7f0aba43656fec996914f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
package com.pitchedapps.frost.injectors

import android.webkit.WebView

/**
 * Created by Allan Wang on 2017-05-31.
 */
enum class JsActions(val function: String) {
    HIDE("style.display='none'"),
    REMOVE("remove()")
}

class VariableGenerator {

    var count = 0

    val next: String
        get() {
            var key = count
            count++
            if (key == 0) return "a"
            val name = StringBuilder()
            while (key > 0) {
                name.append(((key % 26) + 'a'.toInt()).toChar())
                key /= 26
            }
            return name.toString()
        }

    fun reset() {
        count = 0
    }
}

class JsBuilder {

    private val map: MutableMap<String, MutableSet<JsActions>> = mutableMapOf()
    private val v = VariableGenerator()
    private val css: StringBuilder by lazy { StringBuilder() }

    fun append(action: JsActions, vararg ids: String): JsBuilder {
        ids.forEach { id -> map[id]?.add(action) ?: map.put(id, mutableSetOf(action)) }
        return this
    }

    fun css(css: String): JsBuilder {
        this.css.append(css.trim())
        return this
    }

    fun build() = JsInjector(toString())

    override fun toString(): String {
        v.reset()
        val builder = StringBuilder().append("!function(){")
        map.forEach { id, actions ->
            if (actions.size == 1) {
                builder.append("document.getElementById('$id').${actions.first().function};")
            } else {
                val name = v.next
                builder.append("var $name=document.getElementById('$id');")
                actions.forEach { a -> builder.append("$name.${a.function};") }
            }
        }
        if (css.isNotBlank()) {
            val name = v.next
            builder.append("var $name=document.createElement('style');$name.innerHTML='$css';document.head.appendChild($name);")
        }
        return builder.append("}()").toString()
    }
}

class JsInjector(val function: String) {
    fun inject(webView: WebView, callback: ((String) -> Unit)? = null) {
        webView.evaluateJavascript(function, { value -> callback?.invoke(value) })
    }
}