require "js" class Array def sample return nil if length == 0 self[rand(length)] end end class GlampingElement def initialize(node) @node = node end def text @node[:textContent].to_s end def text=(value) @node[:textContent] = value end def value @node[:value].to_s end def value=(new_value) @node[:value] = new_value end def attribute(name, value = nil) return @node.getAttribute(name.to_s).to_s if value.nil? @node.setAttribute(name.to_s, value.to_s) end def show @node[:hidden] = false end def hide @node[:hidden] = true end def enable @node[:disabled] = false end def disable @node[:disabled] = true end def add_class(name) @node[:classList].add(name.to_s) end def remove_class(name) @node[:classList].remove(name.to_s) end def toggle_class(name) @node[:classList].toggle(name.to_s) end def focus @node.focus end def play @node.play end def on(event, synchronous = false, &block) @node.addEventListener(event.to_s, sync: synchronous, &block) end end def element(name) node = JS.document.getElementById(name.to_s) raise "No element named #{name}" if node.nil? GlampingElement.new(node) end def text(name, value = nil) target = element(name) return target.text if value.nil? target.text = value end def value(name, new_value = nil) target = element(name) return target.value if new_value.nil? target.value = new_value end def attribute(name, attribute_name, new_value = nil) element(name).attribute(attribute_name, new_value) end def on(name, event, &block) synchronous = event.to_s == "click" element(name).on(event, synchronous, &block) end def on_click(name, &block) on(name, :click, &block) end def on_change(name, &block) on(name, :change, &block) end def click(name, &block) on_click(name, &block) end def change(name, &block) on_change(name, &block) end def show(name) element(name).show end def hide(name) element(name).hide end def enable(name) element(name).enable end def disable(name) element(name).disable end def add_class(name, class_name) element(name).add_class(class_name) end def remove_class(name, class_name) element(name).remove_class(class_name) end def toggle_class(name, class_name) element(name).toggle_class(class_name) end def focus(name) element(name).focus end def play(name) element(name).play end def go(path) JS.global[:location][:href] = path end def asset(path) normalized = path.to_s normalized[0] == "/" ? normalized : "/#{normalized}" end def every(seconds, &block) raise ArgumentError, "interval must be greater than zero" unless seconds > 0 Task.new do while true sleep seconds block.call end end end def after(seconds, &block) raise ArgumentError, "delay must not be negative" unless seconds >= 0 Task.new do sleep seconds block.call end end module GlampingData def self.read(path) node = JS.document.getElementById(data_id(path)) raise "No embedded file named #{path}" if node.nil? JS.global[:JSON].parse(node[:textContent].to_s).to_s end def self.parse(source) ruby_value(JS.global[:JSON].parse(source.to_s)) end def self.data_id(path) normalized = path.to_s normalized = normalized[1, normalized.length - 1] if normalized[0] == "/" "glamping-data-#{normalized}" end def self.ruby_value(value) if value.is_a?(JS::Array) result = [] value.each { |item| result << ruby_value(item) } result elsif value.is_a?(JS::Object) result = {} JS.global[:Object].keys(value).each do |key| result[key.to_s] = ruby_value(value[key.to_s.to_sym]) end result else value end end end class File def self.read(path) GlampingData.read(path) end end module JSON def self.parse(source) GlampingData.parse(source) end def self.load_file(path) parse(File.read(path)) end end def file(path) File.read(path) end def json(path) JSON.load_file(path) end