opal/inesita/live_reload.rb
require 'inesita'
module Inesita
WebSocket = JS.global.JS[:WebSocket]
Document = JS.global.JS[:document]
Head = Document.JS[:head]
Window = JS.global
module Component
alias_method :old_mount_to, :mount_to
def mount_to(element)
Window.JS.addEventListener('inesita:refresh', -> { render! }, false)
old_mount_to(element)
end
end
class LiveReload
def initialize
connect
end
def connect
ws = `new WebSocket('ws://0.0.0.0:23654')`
ws.JS[:onmessage] = ->(e) { on_file_change(e.JS[:data]) }
ws.JS[:onclose] = -> { reconnect }
end
def reconnect
JS.setTimeout(-> { connect }, 1000)
end
def on_file_change(filename)
prefix, mod, ext = filename.split('|')
filename = "#{prefix}/#{mod}.self.#{ext}"
case ext
when 'js'
replace_js(filename, mod)
when 'css'
replace_css(filename)
else
raise Error, "Don't know how to reload #{ext} file!"
end
end
def replace_js(filename, mod)
s = create_element(:script, type: 'text/javascript', src: filename, onload: lambda do
Opal.load(mod)
Window.JS.dispatchEvent(`new Event('inesita:refresh')`)
end)
replace_or_append(s, 'script') { |t| t.JS[:src].match(filename) }
end
def replace_css(filename)
s = create_element(:link, rel: 'stylesheet', type: 'text/css', href: filename)
replace_or_append(s, 'link') { |t| t.JS[:href].match(filename) }
end
def create_element(name, attrs = {})
s = Document.JS.createElement(name)
s.JS[:onload] = attrs.delete(:onload)
attrs.each do |k, v|
s.JS.setAttribute(k, v)
end
s
end
def replace_or_append(tag, tags, &block)
tags = Document.JS.getElementsByTagName(tags)
tags.JS[:length].times do |i|
next unless block.call(tags.JS.item(i))
Head.JS.replaceChild(tag, tags.JS.item(i))
return
end
Head.JS.appendChild(tag)
end
end
end
Inesita::LiveReload.new