Simon Danisch / Apr 17 2019
WebIO JSCall
This is a prototype to call any JS library easily in Julia with WebIO and makes it much easier to manage state that exists on the JS side.
pkg"up; add WebIO WebSockets"
using WebSockets, WebIO using WebIO, JSExpr, Observables
mutable struct JSObject name::Symbol scope::Scope typ::Symbol end function JSObject(jso::JSObject, typ::Symbol) return JSObject(getfield(jso, :name), getfield(jso, :scope), typ) end
JSObject
0.9s
Julia
Install
#TODO: use WebIO.tojs function to_js(jso::JSObject) return "window.object_pool[\"$(objectid(jso))\"]" end to_js(obj::Union{String, Number}) = repr(obj) to_js(obj::Symbol) = "'$obj'" function to_js(obj) "JSON.parse($(repr(JSON.json(obj))))" end function to_js(dict::AbstractDict) return sprint() do io print(io, "{") for (k, v) in dict print(io, to_js(k), ':', to_js(v)) end print(io, "}") end end function to_js(vec::AbstractVector) return sprint() do io print(io, "[") for elem in vec print(io, to_js(elem), ',') end print(io, "]") end end
to_js (generic function with 6 methods)
0.5s
Julia
Install
function WebIO.evaljs(jso::JSObject, js::String) task = evaljs(getfield(jso, :scope), JSString(js)) end function Base.getproperty(jso::JSObject, name::Symbol) scope, typ = getfield.((jso,), (:scope, :typ)) jname = getfield(jso, :name) typ = if name === :new jsonew = JSObject(jso, :new) evaljs(jso, """ $(to_js(jsonew)) = $(to_js(jso)) """) return jsonew else result = JSObject(name, scope, typ) js = """ var object = $(to_js(jso)) var result = object.$(name) if(result.bind != undefined){ result = result.bind(object) } window.object_pool[\"$(objectid(result))\"] = result """ evaljs(jso, js) return result end end function Base.setproperty!(jso::JSObject, name::Symbol, val) evaljs(jso, """ $(to_js(jso)).$(name) = $(to_js(val)) """) return val end modifier(jso::JSObject) = getfield(jso, :typ) === :new ? "new " : "" function get_args(args, kw_args) if isempty(kw_args) return join(to_js.(args), ", ") elseif isempty(args) return to_js(Dict(kw_args)) else # TODO: I'm not actually sure about this :D error("Javascript only supports keyword arguments OR arguments. Found posititional arguments and keyword arguments") end end function (jso::JSObject)(args...; kw_args...) scope = getfield(jso, :scope); name = getfield(jso, :name) result = JSObject(:result, scope, :call) input_args = get_args(args, kw_args) js = """ var func = $(to_js(jso)) var result = $(modifier(jso))func( $input_args ) window.object_pool[\"$(objectid(result))\"] = result """ evaljs(jso, js) return result end
function Module(name::Symbol, url::String) scope = Scope(imports=[url]) mod = JSObject(name, scope, :module) document = JSObject(:document, scope, :module) onimport(scope, function (mod) window.object_pool = Dict() window.THREE = mod window.object_pool[$(string(objectid(mod)))] = mod window.object_pool[$(string(objectid(document)))] = document end) return mod, document end
Module (generic function with 1 method)
THREE, document = Module( :THREE, "https://cdnjs.cloudflare.com/ajax/libs/three.js/103/three.js", ) (getfield(THREE, :scope))(dom"div#container"())
evaljs(THREE, "alert('hi')")
Task (queued) @0x00007f3366aa1390
scene = THREE.new.Scene() width, height = 500, 500 # Create a basic perspective camera camera = THREE.new.PerspectiveCamera(75, width / height, 0.1, 1000) camera.position.z = 4 renderer = THREE.new.WebGLRenderer(Dict(:antialias => true)) renderer.setClearColor("#ffffff") geometry = THREE.new.BoxGeometry(1, 1, 1) material = THREE.new.MeshBasicMaterial(color = "#433F81") cube = THREE.new.Mesh(geometry, material); scene.add(cube) container = document.querySelector("#container") container.appendChild(renderer.domElement)
for r in LinRange(0.0, 2pi, 200) cube.rotation.x = r cube.rotation.y = r renderer.render(scene, camera) sleep(0.01) end