mirror of
https://github.com/mistergibson/gxg-web-client.git
synced 2026-10-19 07:39:44 -07:00
Initial Commit
This commit is contained in:
parent
356ad3315b
commit
81daf6f387
283 changed files with 130672 additions and 1 deletions
301
gxg/web/gxg.rb
Normal file
301
gxg/web/gxg.rb
Normal file
|
|
@ -0,0 +1,301 @@
|
|||
# GxG for Opal
|
||||
# Place Holders:
|
||||
module GxG
|
||||
LOG = nil
|
||||
DISPATCHER = nil
|
||||
OBJECT_SPACE = {:registry => {}}
|
||||
end
|
||||
$object_space = {:registry => {}}
|
||||
# Alterations to basic Object
|
||||
class Object
|
||||
private
|
||||
#
|
||||
def bytes(*args)
|
||||
GxG::ByteArray::try_convert(args)
|
||||
end
|
||||
#
|
||||
def new_message(*args)
|
||||
# if args[0].is_a?(Hash)
|
||||
# ::GxG::Events::Message.new({:sender => self, :subject => args[1], :body => nil}.merge(args[0]))
|
||||
# else
|
||||
::GxG::Events::Message.new({:sender => self, :subject => args[1], :body => args[0]})
|
||||
# end
|
||||
end
|
||||
# logging hooks
|
||||
def log_unknown(message = nil, progname = nil, &block)
|
||||
# a.k.a 'unknown'
|
||||
if ::GxG::LOG
|
||||
::GxG::LOG.unknown(message, progname, self, &block)
|
||||
end
|
||||
end
|
||||
alias :log_trace :log_unknown
|
||||
def log_fatal(message = nil, progname = nil, &block)
|
||||
if ::GxG::LOG
|
||||
::GxG::LOG.fatal(message, progname, self, &block)
|
||||
end
|
||||
end
|
||||
def log_error(message = nil, progname = nil, &block)
|
||||
if ::GxG::LOG
|
||||
::GxG::LOG.error(message, progname, self, &block)
|
||||
end
|
||||
end
|
||||
def log_warn(message = nil, progname = nil, &block)
|
||||
if ::GxG::LOG
|
||||
::GxG::LOG.warn(message, progname, self, &block)
|
||||
end
|
||||
end
|
||||
alias :log_warning :log_warn
|
||||
def log_info(message = nil, progname = nil, &block)
|
||||
if ::GxG::LOG
|
||||
::GxG::LOG.info(message, progname, self, &block)
|
||||
end
|
||||
end
|
||||
def log_debug(message = nil, progname = nil, &block)
|
||||
if ::GxG::LOG
|
||||
::GxG::LOG.debug(message, progname, self, &block)
|
||||
end
|
||||
end
|
||||
#
|
||||
def post_event(queue_name=:root,dispatcher=nil,&block)
|
||||
result = false
|
||||
if dispatcher.is_a?(::GxG::Events::EventDispatcher)
|
||||
result = dispatcher.post_event(queue_name,&block)
|
||||
else
|
||||
if ::GxG::DISPATCHER
|
||||
result = ::GxG::DISPATCHER.post_event(queue_name,&block)
|
||||
else
|
||||
log_error("No Event Dispatcher Defined")
|
||||
end
|
||||
end
|
||||
result
|
||||
end
|
||||
#
|
||||
def post_event_at(at_time=Time.now,dispatcher=nil,&block)
|
||||
result = nil
|
||||
if dispatcher.is_a?(::GxG::Events::EventDispatcher)
|
||||
result = dispatcher.add_timer({:when => at_time},&block)
|
||||
else
|
||||
if ::GxG::DISPATCHER
|
||||
result = ::GxG::DISPATCHER.add_timer({:when => at_time},&block)
|
||||
else
|
||||
log_error("No Event Dispatcher Defined")
|
||||
end
|
||||
end
|
||||
result
|
||||
end
|
||||
#
|
||||
def post_event_periodic(interval=0.333,dispatcher=nil,&block)
|
||||
result = nil
|
||||
if dispatcher.is_a?(::GxG::Events::EventDispatcher)
|
||||
result = dispatcher.add_periodic_timer({:interval => interval},&block)
|
||||
else
|
||||
if ::GxG::DISPATCHER
|
||||
result = ::GxG::DISPATCHER.add_periodic_timer({:interval => interval},&block)
|
||||
else
|
||||
log_error("No Event Dispatcher Defined")
|
||||
end
|
||||
end
|
||||
result
|
||||
end
|
||||
#
|
||||
def cancel_periodic(reference=nil,dispatcher=nil)
|
||||
result = false
|
||||
if reference.is_a?(Hash)
|
||||
if dispatcher.is_a?(::GxG::Events::EventDispatcher)
|
||||
result = dispatcher.cancel_timer(reference)
|
||||
else
|
||||
if ::GxG::DISPATCHER
|
||||
result = ::GxG::DISPATCHER.cancel_timer(reference)
|
||||
else
|
||||
log_error("No Event Dispatcher Defined")
|
||||
end
|
||||
end
|
||||
end
|
||||
result
|
||||
end
|
||||
#
|
||||
public
|
||||
def is_any?(*args)
|
||||
result = false
|
||||
args.flatten.to_enum.each do |thing|
|
||||
if thing.class == Class
|
||||
if self.is_a?(thing)
|
||||
result = true
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
result
|
||||
end
|
||||
end
|
||||
#
|
||||
module GxG
|
||||
# Generic toolbox of methods
|
||||
def self.uuid_generate()
|
||||
SecureRandom.uuid()
|
||||
end
|
||||
#
|
||||
def self.passes_needed(size_used=0, container_limit=0)
|
||||
if size_used > 0 and container_limit > 0
|
||||
needed_raw = size_used.to_f / container_limit.to_f
|
||||
overhang = needed_raw - needed_raw.to_i.to_f
|
||||
needed_raw = needed_raw.to_i.to_f
|
||||
if overhang > 0.0
|
||||
needed_raw += 1.0
|
||||
end
|
||||
needed_raw.to_i
|
||||
else
|
||||
0
|
||||
end
|
||||
end
|
||||
#
|
||||
def self.apportioned_ranges(how_much_data=0, container_limit=0, original_offset=0)
|
||||
result = []
|
||||
the_count = ::GxG::passes_needed(how_much_data, container_limit)
|
||||
if the_count > 0
|
||||
offset = original_offset
|
||||
the_count.times do
|
||||
if (offset + (container_limit - 1)) <= (how_much_data - 1)
|
||||
end_point = (offset + (container_limit - 1))
|
||||
else
|
||||
end_point = (how_much_data - 1)
|
||||
end
|
||||
result << ((offset)..(end_point))
|
||||
offset = (end_point + 1)
|
||||
end
|
||||
end
|
||||
result
|
||||
end
|
||||
#
|
||||
def self.valid_uuid?(uuid=nil,strict=true)
|
||||
if uuid.is_any?(::String, ::Symbol)
|
||||
if strict == true
|
||||
pattern = /[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]-[0-9a-f][0-9a-f][0-9a-f][0-9a-f]-[4][0-9a-f][0-9a-f][0-9a-f]-[0-9a-f][0-9a-f][0-9a-f][0-9a-f]-[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]/
|
||||
else
|
||||
pattern = /[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]-[0-9a-f][0-9a-f][0-9a-f][0-9a-f]-[0-9a-f][0-9a-f][0-9a-f][0-9a-f]-[0-9a-f][0-9a-f][0-9a-f][0-9a-f]-[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]/
|
||||
end
|
||||
if uuid.to_s.match(pattern)
|
||||
if uuid.to_s.size == 36
|
||||
true
|
||||
else
|
||||
false
|
||||
end
|
||||
else
|
||||
false
|
||||
end
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
#
|
||||
def self.replace_registry(new_registry={})
|
||||
$object_space[:registry] = new_registry
|
||||
true
|
||||
end
|
||||
#
|
||||
def self.register(something=nil, parent=nil)
|
||||
unless $object_space[:registry][(something.uuid.to_s.to_sym)]
|
||||
if parent
|
||||
ordinal = GxG::children_of(parent).size
|
||||
else
|
||||
ordinal = 0
|
||||
end
|
||||
$object_space[:registry][(something.uuid.to_s.to_sym)] = {:object => something, :parent => parent, :ordinal => ordinal}
|
||||
end
|
||||
true
|
||||
end
|
||||
#
|
||||
def self.unregister(the_uuid=nil)
|
||||
if $object_space[:registry][(the_uuid.to_s.to_sym)] && GxG::DISPLAY_DETAILS[:object].uuid.to_s.to_sym != the_uuid.to_s.to_sym
|
||||
$object_space[:registry].delete(the_uuid.to_s.to_sym)
|
||||
end
|
||||
true
|
||||
end
|
||||
#
|
||||
def self.register_uuid_list()
|
||||
$object_space[:registry].keys
|
||||
end
|
||||
#
|
||||
def self.record_by_uuid(the_uuid=nil)
|
||||
result = nil
|
||||
if $object_space[:registry][(the_uuid.to_s.to_sym)]
|
||||
result = $object_space[:registry][(the_uuid.to_s.to_sym)]
|
||||
end
|
||||
result
|
||||
end
|
||||
#
|
||||
def self.object_by_uuid(the_uuid=nil)
|
||||
(GxG::record_by_uuid(the_uuid) || {})[:object]
|
||||
end
|
||||
#
|
||||
def self.children_of(something=nil)
|
||||
result = []
|
||||
buffer = []
|
||||
$object_space[:registry].values.each do |entry|
|
||||
if entry[:parent].object_id == something.object_id
|
||||
buffer << entry
|
||||
end
|
||||
end
|
||||
buffer = buffer.sort { |record_a,record_b| record_a[:ordinal] <=> record_b[:ordinal] }
|
||||
buffer.each do |the_record|
|
||||
result << the_record[:object]
|
||||
end
|
||||
result
|
||||
end
|
||||
#
|
||||
def self.set_ordinal(the_selector=nil,the_ordinal=0)
|
||||
result = false
|
||||
if the_selector.is_any?(String, Symbol)
|
||||
the_object = GxG::object_by_uuid(the_selector.to_sym)
|
||||
else
|
||||
the_object = the_selector
|
||||
end
|
||||
parent = GxG::parent_of(the_object)
|
||||
if parent
|
||||
list = GxG::children_of(parent)
|
||||
current = list.find_index(the_object)
|
||||
if current
|
||||
if the_ordinal >= (list.size - 1)
|
||||
list << list.delete_at(current)
|
||||
else
|
||||
if the_ordinal < 0
|
||||
list = list.unshift(list.delete_at(current))
|
||||
else
|
||||
list = list.insert(the_ordinal,list.delete_at(current))
|
||||
end
|
||||
end
|
||||
#
|
||||
list.each_with_index do |an_object, indexer|
|
||||
record = GxG::record_by_uuid(an_object.uuid.to_sym)
|
||||
record[:ordinal] = indexer
|
||||
end
|
||||
result = true
|
||||
end
|
||||
end
|
||||
result
|
||||
end
|
||||
#
|
||||
def self.parent_of(something=nil)
|
||||
result = nil
|
||||
$object_space[:registry].values.each do |entry|
|
||||
if entry[:object].object_id == something.object_id
|
||||
result = entry[:parent]
|
||||
break
|
||||
end
|
||||
end
|
||||
result
|
||||
end
|
||||
#
|
||||
def self.lineage_of(something=nil)
|
||||
result = []
|
||||
the_parent = GxG::parent_of(something)
|
||||
until the_parent == nil do
|
||||
result.unshift(the_parent)
|
||||
the_parent = GxG::parent_of(the_parent)
|
||||
end
|
||||
# first = base progenitor.
|
||||
result
|
||||
end
|
||||
#
|
||||
end
|
||||
#
|
||||
519
gxg/web/gxg_applications.rb
Normal file
519
gxg/web/gxg_applications.rb
Normal file
|
|
@ -0,0 +1,519 @@
|
|||
module GxG
|
||||
# ----------------------------------------------------------------------------------------------------
|
||||
class Application
|
||||
def initialize(settings={})
|
||||
# @process = server-side process uuid
|
||||
@process = settings[:application]
|
||||
# receives a :source PersistedHash defining the application internals.
|
||||
@definition = settings[:source]
|
||||
@title = "Untitled"
|
||||
# @state is persisted as state-data on the server when altered.
|
||||
@state = (settings[:state] || {})
|
||||
# Gui component if any:
|
||||
@viewports = {}
|
||||
if settings[:viewport].is_a?(::GxG::Gui::ApplicationViewport)
|
||||
@viewports[(settings[:viewport].uuid.to_s.to_sym)] = settings[:viewport]
|
||||
end
|
||||
if settings[:viewports].is_a?(::Array)
|
||||
settings[:viewports].each do |the_viewport|
|
||||
if the_viewport.is_a?(::GxG::Gui::ApplicationViewport)
|
||||
@viewports[(the_viewport.uuid.to_s.to_sym)] = the_viewport
|
||||
end
|
||||
end
|
||||
end
|
||||
@windows = {}
|
||||
if settings[:window].is_a?(::GxG::Gui::Window)
|
||||
@windows[(settings[:window].uuid.to_s.to_sym)] = settings[:window]
|
||||
end
|
||||
if settings[:windows].is_a?(::Array)
|
||||
settings[:windows].each do |the_window|
|
||||
if the_window.is_a?(::GxG::Gui::Window)
|
||||
@windows[(the_window.uuid.to_s.to_sym)] = the_window
|
||||
end
|
||||
end
|
||||
end
|
||||
@libraries = {}
|
||||
# Event Supports:
|
||||
@event_handlers = {}
|
||||
@event_listeners = {}
|
||||
# Runtime flags:
|
||||
@credentialed = (settings[:credentialed] || false)
|
||||
@unique = (settings[:unique] || true)
|
||||
# Application location:
|
||||
@location = settings[:location]
|
||||
# Internal Heap:
|
||||
@heap = {}
|
||||
# ### Compile Source
|
||||
if @definition.is_a?(::String)
|
||||
if @definition.base64?
|
||||
@definition = @definition.decode64
|
||||
end
|
||||
if @definition.json?
|
||||
begin
|
||||
@definition = ::JSON::parse(@definition,{:symbolize_names => true})
|
||||
@definition = ::GxG::Database::process_import(@definition)
|
||||
rescue Exception => the_error
|
||||
log_error({:error => the_error, :parameters => {:definition => @definition}})
|
||||
end
|
||||
else
|
||||
# error - malformed source.
|
||||
end
|
||||
end
|
||||
if @definition.is_a?(::GxG::Database::DetachedHash)
|
||||
@title = @definition.title
|
||||
# Question: should I overwrite the potentially passed settings options?
|
||||
the_opts = @definition[:options].unpersist
|
||||
if the_opts[:credentialed] == true
|
||||
@credentialed = true
|
||||
else
|
||||
@credentialed = false
|
||||
end
|
||||
if the_opts[:unique] == true
|
||||
@unique = true
|
||||
else
|
||||
@unique = false
|
||||
end
|
||||
end
|
||||
# Load Libraries
|
||||
# You can use ruby or javascript for the :native code portion which will be added to the page body.
|
||||
# you can then also set a wrapper library script on the library for smooth integration.
|
||||
# use get_library(uuid/title) to access the library in order to call methods.
|
||||
if @definition.is_any?(::GxG::Database::DetachedHash, ::GxG::Database::DetachedHash)
|
||||
# libarary requirement format: {:library => "", :type => "", :minimum => 0.0, :maximum => nil}
|
||||
load_list = []
|
||||
if @definition[:requirements].is_any?(::GxG::Database::DetachedArray, ::GxG::Database::PersistedArray)
|
||||
@definition[:requirements].each do |the_requirement|
|
||||
unless GxG::DISPLAY_DETAILS[:object].library_loaded?(the_requirement)
|
||||
load_list << the_requirement
|
||||
end
|
||||
end
|
||||
end
|
||||
GxG::DISPLAY_DETAILS[:object].load_libraries(load_list) do |all_loaded|
|
||||
if all_loaded == true
|
||||
# Link to libraries required:
|
||||
if @definition[:requirements].is_a?(::GxG::Database::PersistedArray)
|
||||
@definition[:requirements].each do |the_requirement|
|
||||
the_library = GxG::DISPLAY_DETAILS[:object].get_library(the_requirement)
|
||||
if the_library
|
||||
@libraries[(the_library.uuid)] = the_library
|
||||
end
|
||||
end
|
||||
end
|
||||
# Set Script:
|
||||
if @definition[:script].size > 0
|
||||
if @definition[:script].base64?
|
||||
instance_eval(@definition[:script].to_s.decode64)
|
||||
else
|
||||
instance_eval(@definition[:script].to_s)
|
||||
end
|
||||
end
|
||||
else
|
||||
log_warn("Library load error - not processing main application script for #{@definition.title}.")
|
||||
end
|
||||
end
|
||||
end
|
||||
#
|
||||
self
|
||||
end
|
||||
#
|
||||
def get_resource(the_reference=nil)
|
||||
result = nil
|
||||
if @definition
|
||||
@definition[:content].each do |the_object|
|
||||
if (the_object.title == the_reference || the_object.uuid == the_refrence.to_s.to_sym)
|
||||
result = the_object
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
result
|
||||
end
|
||||
#
|
||||
def require_resource(the_reference=nil)
|
||||
# call self.require_resource("script-name") to load content.script-name
|
||||
result = false
|
||||
if @definition
|
||||
# content source record format: {:component => "script", :type => "text/ruby", :script => ""}
|
||||
the_script = self.get_resource(the_reference)
|
||||
if the_script
|
||||
if the_script[:component].to_s == "script" && the_script[:type].to_s == "text/ruby"
|
||||
if the_script[:script].size > 0
|
||||
if the_script[:script].base64?
|
||||
instance_eval(the_script[:script].to_s.decode64)
|
||||
else
|
||||
instance_eval(the_script[:script].to_s)
|
||||
end
|
||||
result = true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
result
|
||||
end
|
||||
#
|
||||
def title()
|
||||
@title
|
||||
end
|
||||
#
|
||||
def credentialed()
|
||||
@credentialed
|
||||
end
|
||||
#
|
||||
def unique()
|
||||
@unique
|
||||
end
|
||||
#
|
||||
def location()
|
||||
@location
|
||||
end
|
||||
# Libraries:
|
||||
def libraries()
|
||||
@libraries
|
||||
end
|
||||
#
|
||||
def state_data()
|
||||
@state
|
||||
end
|
||||
#
|
||||
def state_pull()
|
||||
GxG::CONNECTION.app_state_pull({:application => @process.to_s}) do |response|
|
||||
if response.is_a?(::Hash)
|
||||
if response[:result].is_a?(::Hash)
|
||||
@state = response[:result]
|
||||
true
|
||||
else
|
||||
false
|
||||
end
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
end
|
||||
#
|
||||
def state_push()
|
||||
GxG::CONNECTION.app_state_push({:application => @process.to_s, :data => @state}) do |response|
|
||||
if response.is_a?(::Hash)
|
||||
if response[:result] == true
|
||||
true
|
||||
else
|
||||
false
|
||||
end
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
end
|
||||
#
|
||||
def get_library(the_reference=nil)
|
||||
result = nil
|
||||
if the_reference.is_any?(::String, ::Symbol)
|
||||
result = @libraries[(the_reference.to_s.to_sym)]
|
||||
unless result
|
||||
@libraries.values.each do |the_library|
|
||||
if the_library.title == the_reference
|
||||
result = the_library
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
result
|
||||
end
|
||||
#
|
||||
# Viewport Supports:
|
||||
def link_viewport(the_viewport=nil)
|
||||
if the_viewport.is_a?(::GxG::Gui::ApplicationViewport)
|
||||
@viewports[(the_viewport.uuid.to_s.to_sym)] = the_viewport
|
||||
true
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
#
|
||||
def unlink_viewport(the_reference=nil)
|
||||
if the_reference.is_any?(::String, ::Symbol)
|
||||
if @viewports[(the_reference.to_s.to_sym)]
|
||||
@viewports.delete((the_reference.to_s.to_sym))
|
||||
else
|
||||
@viewports.values.each do |the_viewport|
|
||||
if the_viewport.title == the_reference
|
||||
@viewports.delete(the_viewport.uuid)
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
true
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
#
|
||||
def viewports()
|
||||
@viewports
|
||||
end
|
||||
#
|
||||
def get_viewport(the_reference=nil)
|
||||
result = nil
|
||||
if the_reference.is_any?(::String, ::Symbol)
|
||||
result = @viewports[(the_reference.to_s.to_sym)]
|
||||
unless result
|
||||
@viewports.values.each do |the_viewport|
|
||||
if the_viewport.title == the_reference
|
||||
result = the_viewport
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
result
|
||||
end
|
||||
#
|
||||
# Window Supports:
|
||||
def link_window(the_window=nil)
|
||||
if the_window.is_a?(::GxG::Gui::Window)
|
||||
@windows[(the_window.uuid.to_s.to_sym)] = the_window
|
||||
# Review: add link to window object to application ??
|
||||
true
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
#
|
||||
def unlink_window(the_reference=nil)
|
||||
if the_reference.is_any?(::String, ::Symbol)
|
||||
if @windows[(the_reference.to_s.to_sym)]
|
||||
@windows.delete((the_reference.to_s.to_sym))
|
||||
else
|
||||
@windows.values.each do |the_window|
|
||||
if the_window.title == the_reference
|
||||
@windows.delete(the_window.uuid)
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
true
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
#
|
||||
def windows()
|
||||
@windows
|
||||
end
|
||||
#
|
||||
def get_window(the_reference=nil)
|
||||
result = nil
|
||||
if the_reference.is_any?(::String, ::Symbol)
|
||||
result = @windows[(the_reference.to_s.to_sym)]
|
||||
unless result
|
||||
@windows.values.each do |the_window|
|
||||
if the_window.title == the_reference
|
||||
result = the_window
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
result
|
||||
end
|
||||
#
|
||||
def process_uuid()
|
||||
@process
|
||||
end
|
||||
#
|
||||
def source_uuid()
|
||||
if @definition.is_a?(::GxG::Database::DetachedHash)
|
||||
@definition.uuid()
|
||||
else
|
||||
nil
|
||||
end
|
||||
end
|
||||
#
|
||||
def search_resources(the_reference=nil)
|
||||
result = nil
|
||||
if the_reference.is_any?(::String, ::Symbol)
|
||||
if @definition
|
||||
@definition[:content].search do |item,selector,container|
|
||||
if item.is_a?(::GxG::Database::DetachedHash)
|
||||
if the_reference.to_s.to_sym == item.uuid().to_s.to_sym || the_reference.to_s == item.title().to_s
|
||||
result = item
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
result
|
||||
end
|
||||
# Event Supports:
|
||||
## Internal method on
|
||||
def on(the_event_type=nil, captured=false, &block)
|
||||
if the_event_type.is_any?(::String, ::Symbol)
|
||||
if block.respond_to?(:call)
|
||||
@event_handlers[(the_event_type.to_s.downcase.to_sym)] = block
|
||||
@event_listeners[(the_event_type.to_s.downcase.to_sym)] = `#{self.method(:event_handler).to_proc}`
|
||||
`#{self.element}.addEventListener(#{the_event_type.to_s.downcase}, #{@event_listeners[(the_event_type.to_s.downcase.to_sym)]}, #{captured})`
|
||||
end
|
||||
end
|
||||
end
|
||||
#
|
||||
def remove_listener(the_event_type=nil)
|
||||
if @event_handlers[(the_event_type.to_s.downcase.to_sym)] && @event_listeners[(the_event_type.to_s.downcase.to_sym)]
|
||||
`#{self.element}.removeEventListener(#{the_event_type.to_s.downcase},#{@event_listeners[(the_event_type.to_s.downcase.to_sym)]});`
|
||||
@event_handlers.delete(the_event_type.to_s.downcase.to_sym)
|
||||
@event_listeners.delete(the_event_type.to_s.downcase.to_sym)
|
||||
end
|
||||
true
|
||||
end
|
||||
#
|
||||
def event_handler(the_event)
|
||||
# format the event record for travel in the system.
|
||||
# See: https://www.w3schools.com/jsref/dom_obj_event.asp
|
||||
# Setup event type groupings to glean appropriate details from the event.
|
||||
the_type = `#{the_event}.type`.to_s.downcase.to_sym
|
||||
event_record = {}
|
||||
if @event_handlers[(the_type)]
|
||||
genre = (GxG::Gui::EventHandlers::EVENT_GENRES[(the_type)] || :event)
|
||||
if genre
|
||||
fields = GxG::Gui::EventHandlers::EVENT_FIELDS[(genre)]
|
||||
if fields
|
||||
#
|
||||
%x{
|
||||
serializer = function thisOne (e) {
|
||||
if (e) {
|
||||
var o = {
|
||||
eventName: e.toString(),
|
||||
altKey: e.altKey,
|
||||
bubbles: e.bubbles,
|
||||
button: e.button,
|
||||
buttons: e.buttons,
|
||||
cancelBubble: e.cancelBubble,
|
||||
cancelable: e.cancelable,
|
||||
clientX: e.clientX,
|
||||
clientY: e.clientY,
|
||||
composed: e.composed,
|
||||
ctrlKey: e.ctrlKey,
|
||||
currentTarget: e.currentTarget ? e.currentTarget.outerHTML : null,
|
||||
defaultPrevented: e.defaultPrevented,
|
||||
detail: e.detail,
|
||||
eventPhase: e.eventPhase,
|
||||
fromElement: e.fromElement ? e.fromElement.outerHTML : null,
|
||||
isTrusted: e.isTrusted,
|
||||
layerX: e.layerX,
|
||||
layerY: e.layerY,
|
||||
metaKey: e.metaKey,
|
||||
movementX: e.movementX,
|
||||
movementY: e.movementY,
|
||||
offsetX: e.offsetX,
|
||||
offsetY: e.offsetY,
|
||||
pageX: e.pageX,
|
||||
pageY: e.pageY,
|
||||
relatedTarget: e.relatedTarget ? e.relatedTarget.outerHTML : null,
|
||||
returnValue: e.returnValue,
|
||||
screenX: e.screenX,
|
||||
screenY: e.screenY,
|
||||
shiftKey: e.shiftKey,
|
||||
sourceCapabilities: e.sourceCapabilities ? e.sourceCapabilities.toString() : null,
|
||||
target: e.target ? e.target.outerHTML : null,
|
||||
timeStamp: e.timeStamp,
|
||||
toElement: e.toElement ? e.toElement.outerHTML : null,
|
||||
type: e.type,
|
||||
view: e.view ? e.view.toString() : null,
|
||||
which: e.which,
|
||||
x: e.x,
|
||||
y: e.y
|
||||
};
|
||||
return o;
|
||||
};
|
||||
};
|
||||
event_record = JSON.stringify(serializer(#{the_event}));
|
||||
}
|
||||
event_record = ::JSON::parse(event_record.to_s, {:symbolize_names => true})
|
||||
# puts "Got: #{event_record.inspect}"
|
||||
end
|
||||
end
|
||||
#
|
||||
@event_handlers[(the_type)].call(event_record)
|
||||
#
|
||||
end
|
||||
end
|
||||
#
|
||||
def run(data={})
|
||||
# override this in your object script.
|
||||
if data.is_a?(::Hash)
|
||||
if data[:restore] == true
|
||||
self.state_pull
|
||||
end
|
||||
end
|
||||
end
|
||||
#
|
||||
def exitready?(data={})
|
||||
# override this in your object script.
|
||||
:ready
|
||||
end
|
||||
#
|
||||
def before_exit(data={})
|
||||
# override this in your object script.
|
||||
true
|
||||
end
|
||||
def exit(data={})
|
||||
# override this in your object script.
|
||||
# GxG::APPLICATIONS[:processes]
|
||||
GxG::CONNECTION.application_close({:application => @process}) do |response|
|
||||
if response.is_a?(::Hash)
|
||||
if response[:result] == true
|
||||
GxG::APPLICATIONS[:processes].delete(@process)
|
||||
else
|
||||
log_warn("Could not close application #{@process.inspect} : #{response.inspect}")
|
||||
end
|
||||
else
|
||||
log_warn("Malformed response: #{response.inspect}")
|
||||
end
|
||||
end
|
||||
true
|
||||
end
|
||||
#
|
||||
def window_close(details={})
|
||||
# override this in your object script.
|
||||
if details.is_a?(::Hash)
|
||||
if GxG::valid_uuid?(details[:window])
|
||||
the_window = GxG::DISPLAY_DETAILS[:object].get_window(details[:window])
|
||||
if the_window
|
||||
# TODO: re-think documents and such.
|
||||
# Document data needs saving first ??
|
||||
@viewports.keys.each do |the_reference|
|
||||
the_viewport = the_window.find_child(the_reference)
|
||||
if the_viewport
|
||||
the_viewport.set_application(nil)
|
||||
self.unlink_viewport(the_reference)
|
||||
end
|
||||
end
|
||||
self.unlink_window(the_window.uuid)
|
||||
GxG::DISPLAY_DETAILS[:object].window_close(the_window.uuid)
|
||||
unless @windows.size > 0
|
||||
self.exit()
|
||||
end
|
||||
end
|
||||
else
|
||||
log_warn("Invalid Argument passed: #{details.inspect}")
|
||||
end
|
||||
end
|
||||
end
|
||||
#
|
||||
# Viewport Management:
|
||||
def viewport_clear(the_reference=nil)
|
||||
viewport = self.get_viewport(the_reference)
|
||||
if viewport
|
||||
viewport.gxg_each_child do |the_item|
|
||||
unless the_item == viewport
|
||||
the_item.destroy
|
||||
end
|
||||
end
|
||||
true
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
#
|
||||
end
|
||||
end
|
||||
1270
gxg/web/gxg_augments.rb
Normal file
1270
gxg/web/gxg_augments.rb
Normal file
File diff suppressed because it is too large
Load diff
1081
gxg/web/gxg_comm.rb
Normal file
1081
gxg/web/gxg_comm.rb
Normal file
File diff suppressed because it is too large
Load diff
2319
gxg/web/gxg_database.rb
Normal file
2319
gxg/web/gxg_database.rb
Normal file
File diff suppressed because it is too large
Load diff
3193
gxg/web/gxg_datetime.rb
Normal file
3193
gxg/web/gxg_datetime.rb
Normal file
File diff suppressed because it is too large
Load diff
1887
gxg/web/gxg_elements.rb
Normal file
1887
gxg/web/gxg_elements.rb
Normal file
File diff suppressed because it is too large
Load diff
567
gxg/web/gxg_events.rb
Normal file
567
gxg/web/gxg_events.rb
Normal file
|
|
@ -0,0 +1,567 @@
|
|||
# Events Section:
|
||||
module GxG
|
||||
module Events
|
||||
#
|
||||
class Message
|
||||
#
|
||||
def initialize(*args)
|
||||
# MUST provide a hash with at least :sender message field set
|
||||
# {:sender => <some-object>, :body => <some-object>, :post_process => <some-proc>, :error_handler => <some-object>/<some-proc> }
|
||||
unless args[0].is_a?(::Hash)
|
||||
raise ArgumentError, "you must pass a Hash to create the message"
|
||||
end
|
||||
@data = {:sender => nil, :id => (::GxG.uuid_generate().to_s.to_sym), :subject => args[1], :body => nil, :on_success => nil, :on_fail => nil}.merge(args[0])
|
||||
unless @data[:sender]
|
||||
raise ArgumentError, "you must set the :sender key in the argument Hash"
|
||||
end
|
||||
self
|
||||
end
|
||||
#
|
||||
def inspect()
|
||||
@data.inspect
|
||||
end
|
||||
#
|
||||
def id()
|
||||
@data[:id]
|
||||
end
|
||||
#
|
||||
def sender()
|
||||
@data[:sender]
|
||||
end
|
||||
#
|
||||
def subject()
|
||||
@data[:subject]
|
||||
end
|
||||
#
|
||||
def body()
|
||||
@data[:body]
|
||||
end
|
||||
#
|
||||
def succeed(input=nil)
|
||||
if @data[:on_success].respond_to?(:call)
|
||||
@data[:on_success].call(input)
|
||||
end
|
||||
end
|
||||
#
|
||||
def fail(input=nil)
|
||||
if @data[:on_fail].respond_to?(:call)
|
||||
@data[:on_fail].call(input)
|
||||
end
|
||||
end
|
||||
#
|
||||
def on(event_type=nil,&block)
|
||||
if [:success, :fail].include?(event_type)
|
||||
if block.respond_to?(:call)
|
||||
case event_type
|
||||
when :success
|
||||
@data[:on_success] = block
|
||||
when :fail
|
||||
@data[:on_fail] = block
|
||||
end
|
||||
else
|
||||
raise ArgumentError, "You MUST provide a callable block."
|
||||
end
|
||||
else
|
||||
raise ArgumentError, "Unknown event type: #{event_type}"
|
||||
end
|
||||
end
|
||||
# Hash-like support methods:
|
||||
def keys()
|
||||
@data.keys()
|
||||
end
|
||||
def [](the_key)
|
||||
@data[(the_key)]
|
||||
end
|
||||
def []=(the_key, the_value)
|
||||
@data[(the_key)] = the_value
|
||||
end
|
||||
def process(&block)
|
||||
@data.process(&block)
|
||||
end
|
||||
def process!(&block)
|
||||
@data.process!(&block)
|
||||
end
|
||||
def search(&block)
|
||||
@data.search(&block)
|
||||
end
|
||||
def paths_to(*args)
|
||||
@data.paths_to(*args)
|
||||
end
|
||||
def get_at_path(*args)
|
||||
@data.get_at_path(*args)
|
||||
end
|
||||
def set_at_path(*args)
|
||||
@data.set_at_path(*args)
|
||||
end
|
||||
end
|
||||
#
|
||||
class LoggerDB
|
||||
#
|
||||
end
|
||||
#
|
||||
class LogRing
|
||||
#
|
||||
def initialize()
|
||||
@uuid = ::GxG.uuid_generate().to_sym
|
||||
@busy = false
|
||||
@outlets = {}
|
||||
@messages = []
|
||||
end
|
||||
#
|
||||
def uuid()
|
||||
@uuid
|
||||
end
|
||||
#
|
||||
def keys()
|
||||
@outlets.keys()
|
||||
end
|
||||
#
|
||||
def [](the_key)
|
||||
@outlets[(the_key)]
|
||||
end
|
||||
#
|
||||
def []=(the_key,the_logger)
|
||||
if the_logger.is_any?(::Logger, ::GxG::Events::LoggerDB)
|
||||
@outlets[(the_key)] = the_logger
|
||||
the_logger
|
||||
else
|
||||
raise ArgumentError, "You must supply a Logger or GxG::Events::LoggerDB instance, you provided a #{the_logger.class}"
|
||||
end
|
||||
end
|
||||
#
|
||||
def process_messages()
|
||||
unless @busy
|
||||
@busy = true
|
||||
#
|
||||
the_message = @messages.shift
|
||||
if the_message.is_a?(::GxG::Events::Message)
|
||||
begin
|
||||
if the_message[:subject] == :notification
|
||||
if the_message[:body].is_a?(::Hash)
|
||||
outlets = self.keys()
|
||||
outlets.to_enum(:each).each do |the_outlet_key|
|
||||
the_outlet = self[(the_outlet_key)]
|
||||
if (the_message[:body][:severity] || ::Logger::UNKNOWN) >= the_outlet.level()
|
||||
# output
|
||||
if the_outlet.is_a?(::GxG::Events::LoggerDB)
|
||||
# TODO: GxG::Events::LogRing : build out object-based event logging:
|
||||
else
|
||||
# conventional string based:
|
||||
if (the_message[:body][:message].is_a?(::String) || the_message[:body][:message].is_a?(::Exception))
|
||||
the_outlet.add(the_message[:body][:severity], the_message[:body][:message], the_message[:body][:progname])
|
||||
else
|
||||
if the_message[:body][:message].is_a?(::Hash)
|
||||
#
|
||||
if the_message[:body][:message][:error].is_a?(::Exception)
|
||||
if the_message[:body][:message][:error].backtrace.is_a?(::Array)
|
||||
if the_message[:body][:message][:parameters]
|
||||
the_outlet.add(the_message[:body][:severity], ("\n #{the_message[:body][:message][:error].exception.class.to_s}: " + the_message[:body][:message][:error].to_s + "\n Parameters: " + the_message[:body][:message][:parameters].inspect + "\n Trace: " + the_message[:body][:message][:error].backtrace.join("\n") + "\n"), the_message[:body][:progname])
|
||||
else
|
||||
the_outlet.add(the_message[:body][:severity], ("\n #{the_message[:body][:message][:error].exception.class.to_s}: " + the_message[:body][:message][:error].to_s + "\n Trace: " + the_message[:body][:message][:error].backtrace.join("\n") + "\n"), the_message[:body][:progname])
|
||||
end
|
||||
else
|
||||
if the_message[:body][:message][:parameters]
|
||||
the_outlet.add(the_message[:body][:severity], ("\n #{the_message[:body][:message][:error].exception.class.to_s}: " + the_message[:body][:message][:error].to_s + "\n Parameters: " + the_message[:body][:message][:parameters].inspect + "\n"), the_message[:body][:progname])
|
||||
else
|
||||
the_outlet.add(the_message[:body][:severity], ("\n #{the_message[:body][:message][:error].exception.class.to_s}: " + the_message[:body][:message][:error].to_s + "\n"), the_message[:body][:progname])
|
||||
end
|
||||
end
|
||||
else
|
||||
the_outlet.add(the_message[:body][:severity], the_message[:body][:message].inspect, the_message[:body][:progname])
|
||||
end
|
||||
else
|
||||
the_outlet.add(the_message[:body][:severity], the_message[:body][:message].inspect, the_message[:body][:progname])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
#
|
||||
end
|
||||
end
|
||||
end
|
||||
rescue Exception => the_error
|
||||
@busy = false
|
||||
# log_error({:error => the_error, :parameters => {:message => the_message}})
|
||||
end
|
||||
end
|
||||
#
|
||||
@busy = false
|
||||
end
|
||||
true
|
||||
end
|
||||
#
|
||||
def add(severity = nil, message = nil, progname = nil, origin = nil, &block)
|
||||
#
|
||||
unless [::Logger::DEBUG, ::Logger::INFO, ::Logger::WARN, ::Logger::ERROR, ::Logger::FATAL, ::Logger::UNKNOWN].include?(severity)
|
||||
severity = ::Logger::UNKNOWN
|
||||
end
|
||||
if progname
|
||||
unless message
|
||||
message = progname
|
||||
progname = nil
|
||||
end
|
||||
end
|
||||
begin
|
||||
unless message
|
||||
if block.respond_to?(:call)
|
||||
message = block.call()
|
||||
end
|
||||
end
|
||||
# self.mailbox << ::GxG::Events::Message.new({:sender => (origin || self), :severity => severity, :message => message, :progname => progname})
|
||||
the_message = new_message({:severity => severity, :message => message, :progname => progname}, :notification)
|
||||
if origin
|
||||
the_message[:sender] = origin
|
||||
end
|
||||
while @busy == true do
|
||||
sleep 0.333
|
||||
end
|
||||
@busy = true
|
||||
@messages << the_message
|
||||
@busy = false
|
||||
true
|
||||
rescue Exception => the_error
|
||||
@busy = false
|
||||
# log_error({:error => the_error, :parameters => {:severity => severity, :message => message, :progname => progname, :block => block}})
|
||||
false
|
||||
end
|
||||
end
|
||||
#
|
||||
def unknown?()
|
||||
true
|
||||
end
|
||||
alias :trace? unknown?
|
||||
def unknown(message = nil, progname = nil, origin = nil, &block)
|
||||
self.add(::Logger::UNKNOWN, message, progname, (origin || self), &block)
|
||||
end
|
||||
alias :trace :unknown
|
||||
def fatal?()
|
||||
true
|
||||
end
|
||||
def fatal(message = nil, progname = nil, origin = nil, &block)
|
||||
self.add(::Logger::FATAL, message, progname, (origin || self), &block)
|
||||
end
|
||||
def error?()
|
||||
true
|
||||
end
|
||||
def error(message = nil, progname = nil, origin = nil, &block)
|
||||
self.add(::Logger::ERROR, message, progname, (origin || self), &block)
|
||||
end
|
||||
def warn?()
|
||||
true
|
||||
end
|
||||
alias :warning? :warn?
|
||||
def warn(message = nil, progname = nil, origin = nil, &block)
|
||||
self.add(::Logger::WARN, message, progname, (origin || self), &block)
|
||||
end
|
||||
alias :warning :warn
|
||||
def info?()
|
||||
true
|
||||
end
|
||||
def info(message = nil, progname = nil, origin = nil, &block)
|
||||
self.add(::Logger::INFO, message, progname, (origin || self), &block)
|
||||
end
|
||||
def debug?()
|
||||
true
|
||||
end
|
||||
def debug(message = nil, progname = nil, origin = nil, &block)
|
||||
self.add(::Logger::DEBUG, message, progname, (origin || self), &block)
|
||||
end
|
||||
end
|
||||
#
|
||||
class EventDispatcher
|
||||
def initialize(interval=0.333)
|
||||
super()
|
||||
@uuid = ::GxG.uuid_generate().to_sym
|
||||
@event_queues = {:root => {:events => [], :settings => {:active => true, :portion => 100.0}}}
|
||||
@running = false
|
||||
@ticking = false
|
||||
# Timer format: {:when => Time_Object.to_f, :what => event_frame, :interval => float, :id => uuid.sym}
|
||||
@timers = []
|
||||
# JavaScript main pushing timer:
|
||||
@js_interval = (interval.to_f * 1000.0).to_i
|
||||
@host_timer = nil
|
||||
# Register with global dispatcher pool:
|
||||
self
|
||||
end
|
||||
#
|
||||
def startup()
|
||||
@running = true
|
||||
unless @host_timer
|
||||
@host_timer = `setInterval(#{method(:tick).to_proc}, #{@js_interval})`
|
||||
end
|
||||
if @host_timer
|
||||
true
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
#
|
||||
def shutdown()
|
||||
@running = false
|
||||
if @host_timer
|
||||
if @host_timer.is_a?(Numeric)
|
||||
`clearInterval(#{@host_timer})`
|
||||
else
|
||||
`clearInterval(#{@host_timer}._id)`
|
||||
end
|
||||
end
|
||||
self.cancel_all_timers
|
||||
@host_timer = nil
|
||||
true
|
||||
end
|
||||
#
|
||||
def tick()
|
||||
if @running
|
||||
unless @ticking
|
||||
@ticking = true
|
||||
events = []
|
||||
#
|
||||
if @timers.size > 0
|
||||
triggered = []
|
||||
current = ::Time.now.to_f
|
||||
@timers.each_with_index do |timer,indexer|
|
||||
if current >= timer[:when]
|
||||
events << timer[:what]
|
||||
if timer[:interval]
|
||||
@timers[(indexer)][:when] = (current + timer[:interval].to_f)
|
||||
else
|
||||
triggered.unshift(indexer)
|
||||
end
|
||||
end
|
||||
end
|
||||
triggered.each do |indexer|
|
||||
@timers.delete_at(indexer)
|
||||
end
|
||||
end
|
||||
#
|
||||
total_slots = 100
|
||||
load_size = 0
|
||||
queues = {}
|
||||
@event_queues.keys.each do |the_queue|
|
||||
if (@event_queues[(the_queue)][:settings][:active] && @event_queues[(the_queue)][:events].size > 0)
|
||||
queues[(the_queue)] = {:portion => ((@event_queues[(the_queue)][:settings][:portion].to_f / 100.0) * total_slots.to_f).to_i}
|
||||
if queues[(the_queue)][:portion] == 0
|
||||
# Fudging a little here in case of real heavy loads and/or too many queues.
|
||||
queues[(the_queue)][:portion] = 1
|
||||
end
|
||||
queues[(the_queue)][:size] = @event_queues[(the_queue)][:events].size
|
||||
load_size += queues[(the_queue)][:size]
|
||||
end
|
||||
end
|
||||
queues.keys.each do |the_queue|
|
||||
# TODO: devise apportioned event dispatch
|
||||
queues[(the_queue)][:portion].times do
|
||||
op = @event_queues[(the_queue)][:events].shift
|
||||
if op.respond_to?(:call)
|
||||
events << op
|
||||
else
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
#
|
||||
events.each do |the_event|
|
||||
if the_event
|
||||
begin
|
||||
the_event.call()
|
||||
rescue Exception => the_error
|
||||
#
|
||||
end
|
||||
end
|
||||
end
|
||||
#
|
||||
@ticking = false
|
||||
end
|
||||
end
|
||||
end
|
||||
#
|
||||
def inspect_timers()
|
||||
@timers
|
||||
end
|
||||
def inspect_queue(queue=:root)
|
||||
@event_queues[(queue)]
|
||||
end
|
||||
#
|
||||
def adjust_event_queue(queue=:root,settings={})
|
||||
# RESEARCH: GxG::Events::EventDispatcher.adjust_event_queue : refactor :portion of each queue algo needed.
|
||||
unless queue.to_s.to_sym == :root
|
||||
if @event_queues[(queue.to_sym)]
|
||||
# LATER: Eventually add :portion as valid setting to accept.
|
||||
[:active].each do |the_setting|
|
||||
if settings[(the_setting.to_sym)]
|
||||
new_setting = {}
|
||||
# portion adjustment algo here
|
||||
new_setting[(the_setting.to_sym)] = settings[(the_setting.to_sym)]
|
||||
#
|
||||
@event_queues[(queue.to_sym)][:settings].merge!(new_setting)
|
||||
end
|
||||
end
|
||||
else
|
||||
puts "Warning: queue #{queue.inspect} does not exist"
|
||||
# For Now: just ignore references to non-existent queues. (reconsider later)
|
||||
# raise ArgumentError, "Event Queue :#{queue} does not exist to alter"
|
||||
end
|
||||
end
|
||||
end
|
||||
#
|
||||
def create_event_queue(settings={})
|
||||
unless settings.is_a?(Hash)
|
||||
raise ArgumentError, "you must provide a Hash as a parameter set"
|
||||
end
|
||||
queue_name = settings.delete(:name).to_s.to_sym
|
||||
if queue_name.to_s.size > 0
|
||||
if @event_queues[(queue_name)]
|
||||
raise ArgumentError, "Event Queue #{queue_name.inspect} already exists"
|
||||
else
|
||||
@event_queues[(queue_name)] = {:events => [], :settings => {:active => false, :portion => 0.0}}
|
||||
# For Now: simply evenly divide the portions equally, only :root can be 100%.
|
||||
even_portion = (100.0 / @event_queues.keys.size.to_f)
|
||||
@event_queues.keys.each do |the_queue|
|
||||
@event_queues[(the_queue)][:settings][:portion] = (even_portion)
|
||||
end
|
||||
self.adjust_event_queue(queue_name, settings.merge({:active => true}))
|
||||
true
|
||||
end
|
||||
else
|
||||
raise ArgumentError, ":name must be provided for Event Queue"
|
||||
end
|
||||
end
|
||||
#
|
||||
def delete_event_queue(queue=nil)
|
||||
if queue.to_s.to_sym == :root
|
||||
false
|
||||
else
|
||||
@event_queues.delete(queue)
|
||||
# For Now: simply evenly divide the portions equally, only :root can be 100%.
|
||||
even_portion = (@event_queues.keys.size.to_f / 100.0)
|
||||
@event_queues.keys.each do |the_queue|
|
||||
@event_queues[(the_queue)][:settings][:portion] = (even_portion)
|
||||
end
|
||||
true
|
||||
end
|
||||
end
|
||||
#
|
||||
def pause_event_queue(queue=nil)
|
||||
unless queue.to_s.to_sym == :root
|
||||
self.adjust_event_queue(queue,{:active => true})
|
||||
end
|
||||
end
|
||||
#
|
||||
def unpause_event_queue(queue=nil)
|
||||
unless queue.to_s.to_sym == :root
|
||||
self.adjust_event_queue(queue,{:active => false})
|
||||
end
|
||||
end
|
||||
#
|
||||
def post_to_event_queue(queue=:root,the_event=nil)
|
||||
if the_event.respond_to?(:call)
|
||||
if @event_queues[(queue)]
|
||||
@event_queues[(queue)][:events] << the_event
|
||||
else
|
||||
# issue a warning, and post to :root queue anyways (for now). (link to logger, and output)
|
||||
@event_queues[:root][:events] << the_event
|
||||
end
|
||||
end
|
||||
end
|
||||
#
|
||||
def post_event(queue_name=:root,&block)
|
||||
unless @event_queues[(queue_name.to_sym)]
|
||||
self.create_event_queue({:name => queue_name.to_sym})
|
||||
end
|
||||
self.post_to_event_queue(queue_name.to_sym,block)
|
||||
true
|
||||
end
|
||||
#
|
||||
# Adding Timers
|
||||
def add_timer(options={},&block)
|
||||
what_time = options[:when]
|
||||
what = (options[:do] || block)
|
||||
#
|
||||
if what_time.is_a?(::Numeric)
|
||||
# ms offset
|
||||
what_time = (::Time::now.to_f + what_time.to_f)
|
||||
else
|
||||
if what_time.is_a?(::Time)
|
||||
what_time = what_time.to_f
|
||||
else
|
||||
raise ArgumentError, "you must specify a :when => millisecond (float) offset, or a Time for the timer"
|
||||
end
|
||||
end
|
||||
#
|
||||
unless what.respond_to?(:call)
|
||||
raise ArgumentError, "you must provide a :do => callable object (Proc) for the timer, or a block"
|
||||
end
|
||||
the_id = ::GxG.uuid_generate().to_sym
|
||||
@timers << {:when => what_time, :what => what, :id => the_id}
|
||||
{:timer => the_id}
|
||||
end
|
||||
#
|
||||
def add_periodic_timer(options={},&block)
|
||||
interval = options[:interval]
|
||||
what = (options[:do] || block)
|
||||
#
|
||||
what_time = ::Time::now.to_f
|
||||
if interval.is_a?(::Numeric)
|
||||
# offset
|
||||
what_time += interval.to_f
|
||||
else
|
||||
raise ArgumentError, "you must specify an :interval => Float (as you would with sleep) offset interval for the periodic timer"
|
||||
end
|
||||
#
|
||||
unless what.respond_to?(:call)
|
||||
raise ArgumentError, "you must provide a :do => callable object (Proc) for the timer, or a block"
|
||||
end
|
||||
the_id = ::GxG.uuid_generate().to_sym
|
||||
@timers << {:when => what_time, :what => what, :interval => interval.to_f, :id => the_id}
|
||||
{:timer => the_id}
|
||||
end
|
||||
#
|
||||
def cancel_timer(params={})
|
||||
the_id = params[:timer]
|
||||
result = false
|
||||
if the_id
|
||||
@timers.each_index do |indexer|
|
||||
if @timers[(indexer)][:id] == the_id
|
||||
@timers.delete_at(indexer)
|
||||
result = true
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
result
|
||||
end
|
||||
#
|
||||
def cancel_all_timers()
|
||||
while @ticking == true do
|
||||
sleep 0.333
|
||||
end
|
||||
@timers = []
|
||||
true
|
||||
end
|
||||
#
|
||||
end
|
||||
end
|
||||
end
|
||||
# Establish Console Logger for Applications:
|
||||
module GxG
|
||||
# setup LOG.
|
||||
if const_defined?(:LOG)
|
||||
remove_const(:LOG)
|
||||
end
|
||||
LOG = ::GxG::Events::LogRing.new()
|
||||
end
|
||||
::GxG::LOG[:default] = ::Logger.new(::Logger::WARN)
|
||||
# Establish Dispatcher Service for Applications:
|
||||
module GxG
|
||||
# setup DISPATCHER & LOG timer.
|
||||
if const_defined?(:DISPATCHER)
|
||||
remove_const(:DISPATCHER)
|
||||
end
|
||||
DISPATCHER = ::GxG::Events::EventDispatcher.new(0.1)
|
||||
DISPATCHER.startup
|
||||
LOG_TIMER = DISPATCHER.add_periodic_timer({:interval => 1.0}) { ::GxG::LOG.process_messages }
|
||||
def self.shutdown()
|
||||
DISPATCHER.shutdown
|
||||
end
|
||||
end
|
||||
#
|
||||
8418
gxg/web/gxg_ferro.rb
Normal file
8418
gxg/web/gxg_ferro.rb
Normal file
File diff suppressed because it is too large
Load diff
2130
gxg/web/gxg_foundation.rb
Normal file
2130
gxg/web/gxg_foundation.rb
Normal file
File diff suppressed because it is too large
Load diff
1281
gxg/web/gxg_gui.rb
Normal file
1281
gxg/web/gxg_gui.rb
Normal file
File diff suppressed because it is too large
Load diff
2349
gxg/web/gxg_gui_old.rb
Normal file
2349
gxg/web/gxg_gui_old.rb
Normal file
File diff suppressed because it is too large
Load diff
483
gxg/web/gxg_iac.rb
Normal file
483
gxg/web/gxg_iac.rb
Normal file
|
|
@ -0,0 +1,483 @@
|
|||
#
|
||||
module GxG
|
||||
module Networking
|
||||
class Channel
|
||||
#
|
||||
def initialize(the_connector=nil, settings={})
|
||||
unless the_connector.is_a?(GxG::Networking::Connector)
|
||||
raise ArgumentError, "You MUST provide a GxG::Networking::Connector to associate with."
|
||||
end
|
||||
@connector = the_connector
|
||||
@uuid = (settings[:uuid] || GxG::uuid_generate().to_sym)
|
||||
@remote_uuid = settings[:remote_uuid]
|
||||
@remote_interface = []
|
||||
@mounted_object = nil
|
||||
@pending = {}
|
||||
# messages from server
|
||||
@buffer = []
|
||||
# messages from objects
|
||||
@inbox = []
|
||||
# deferred messages to send (to object or server) ???
|
||||
@outbox = []
|
||||
#
|
||||
@timer = post_event_periodic(0.333) do
|
||||
self.process_messages
|
||||
end
|
||||
#
|
||||
post_event(:communications) do
|
||||
gxg_message = new_message
|
||||
gxg_message[:sender] = @uuid
|
||||
gxg_message[:to] = @remote_uuid
|
||||
gxg_message[:body] = {:operation => "fetch_interface"}
|
||||
@connector.transmit(gxg_message)
|
||||
end
|
||||
#
|
||||
self
|
||||
end
|
||||
#
|
||||
def closed?()
|
||||
if @timer.is_a?(Hash)
|
||||
false
|
||||
else
|
||||
true
|
||||
end
|
||||
end
|
||||
#
|
||||
def close()
|
||||
result = false
|
||||
if @timer.is_a?(Hash)
|
||||
cancel_periodic(@timer)
|
||||
@timer = nil
|
||||
end
|
||||
# send out close_channel operation to partner on server, set result = true
|
||||
result
|
||||
end
|
||||
#
|
||||
def uuid()
|
||||
@uuid
|
||||
end
|
||||
#
|
||||
def remote_uuid()
|
||||
@remote_uuid
|
||||
end
|
||||
#
|
||||
def mounted()
|
||||
@mounted_object
|
||||
end
|
||||
#
|
||||
def mount(something=nil)
|
||||
@mounted_object = something
|
||||
end
|
||||
#
|
||||
def process_messages()
|
||||
# process @buffer items, add to @outbox
|
||||
server_message = @buffer.shift
|
||||
if server_message[:body].is_a?(Hash)
|
||||
# puts "Channel Processing: #{server_message.inspect}"
|
||||
# puts "Got channel message: #{server_message.inspect}"
|
||||
if server_message[:body][:operation]
|
||||
case server_message[:body][:operation]
|
||||
when "define_interface"
|
||||
if server_message[:body][:interface].is_a?(Array)
|
||||
@remote_interface = []
|
||||
server_message[:body][:interface].each do |entry|
|
||||
@remote_interface << entry.to_s.to_sym
|
||||
end
|
||||
end
|
||||
if @mounted_object
|
||||
@mounted_object.set_remote_uuid(server_message[:body][:remote_uuid].to_s.to_sym)
|
||||
end
|
||||
when "fetch_interface"
|
||||
if @mounted_object
|
||||
method_list = []
|
||||
@mounted_object.public_methods.each do |entry|
|
||||
method_list << entry.to_s
|
||||
end
|
||||
gxg_message = new_message
|
||||
gxg_message[:sender] = @uuid
|
||||
gxg_message[:to] = server_message[:sender]
|
||||
gxg_message[:body] = {:operation => "define_interface", :remote_uuid => @mounted_object.uuid.to_s, :interface => method_list}
|
||||
@connector.transmit(gxg_message)
|
||||
end
|
||||
when "mount_object"
|
||||
# a) by registry uuid, b) create with params, c) ?
|
||||
# GxG::OBJECT_SPACE[:registry][(the_uuid)]
|
||||
# GxG::object_by_uuid(the_uuid)
|
||||
# puts "Attempting to mount object"
|
||||
if server_message[:body][:uuid]
|
||||
@mounted_object = GxG::object_by_uuid(server_message[:body][:uuid].to_s.to_sym)
|
||||
if @mounted_object
|
||||
if server_message[:body][:remote_uuid]
|
||||
@mounted_object.set_remote_uuid(server_message[:body][:remote_uuid].to_s.to_sym)
|
||||
end
|
||||
end
|
||||
end
|
||||
# ???
|
||||
if server_message[:body][:type]
|
||||
case server_message[:body][:type].to_s
|
||||
when "GxG::Gui::Display"
|
||||
@mounted_object = nil
|
||||
# puts "Creating Display ..."
|
||||
if server_message[:body][:parameters]
|
||||
if server_message[:body][:parameters].is_a?(Array)
|
||||
@mounted_object = ::GxG::Gui::Display.new(*server_message[:body][:parameters])
|
||||
else
|
||||
@mounted_object = ::GxG::Gui::Display.new(server_message[:body][:parameters])
|
||||
end
|
||||
else
|
||||
@mounted_object = ::GxG::Gui::Display.new()
|
||||
end
|
||||
# BUG - FIX - Never gets to this line of code:
|
||||
# puts "Mounted: #{@mounted_object.inspect} with Parameters: #{server_message[:body][:parameters].inspect}"
|
||||
else
|
||||
log_error({:error => (Exception.new("Unrecognized object type")), :parameters => {:message => server_message}})
|
||||
end
|
||||
#
|
||||
end
|
||||
#
|
||||
if @mounted_object != nil
|
||||
#
|
||||
# puts "Object MOUNTED."
|
||||
@mounted_object.set_channel(self)
|
||||
method_list = []
|
||||
@mounted_object.public_methods.each do |entry|
|
||||
method_list << entry.to_s
|
||||
end
|
||||
gxg_message = new_message
|
||||
gxg_message[:sender] = @uuid
|
||||
gxg_message[:to] = server_message[:sender]
|
||||
gxg_message[:body] = {:operation => "define_interface", :remote_uuid => @mounted_object.uuid.to_s, :interface => method_list}
|
||||
@connector.transmit(gxg_message)
|
||||
else
|
||||
log_error({:error => (Exception.new("Failed to mount object")), :parameters => {:mesaage => server_message}})
|
||||
end
|
||||
#
|
||||
end
|
||||
else
|
||||
# RPC message
|
||||
# Layer 3
|
||||
rpc_message = new_message
|
||||
server_message[:body].keys.each do |rpc_key|
|
||||
rpc_message[(rpc_key)] = server_message[:body][(rpc_key)]
|
||||
end
|
||||
if rpc_message[:body][:response]
|
||||
case rpc_message[:body][:response]
|
||||
when "success"
|
||||
responder = @pending.delete(rpc_message.id().to_s.to_sym)
|
||||
if responder
|
||||
if responder[:success].respond_to?(:call)
|
||||
responder[:success].call(rpc_message)
|
||||
end
|
||||
end
|
||||
when "fail"
|
||||
responder = @pending.delete(rpc_message.id().to_s.to_sym)
|
||||
if responder
|
||||
if responder[:fail].respond_to?(:call)
|
||||
responder[:fail].call(rpc_message)
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
if @mounted_object
|
||||
# {:method => :nil, :parameters => []/{}/etc}
|
||||
if rpc_message[:body][:method]
|
||||
if @mounted_object.public_methods.include?(rpc_message[:body][:method].to_s.to_sym)
|
||||
begin
|
||||
if rpc_message[:body][:parameters]
|
||||
if rpc_message[:body][:parameters].is_a?(Array)
|
||||
result = @mounted_object.send(rpc_message[:body][:method].to_s.to_sym, *rpc_message[:body][:parameters])
|
||||
else
|
||||
result = @mounted_object.send(rpc_message[:body][:method].to_s.to_sym, rpc_message[:body][:parameters])
|
||||
end
|
||||
else
|
||||
result = @mounted_object.send(rpc_message[:body][:method].to_s.to_sym)
|
||||
end
|
||||
# result success back.
|
||||
gxg_message = new_message
|
||||
gxg_message[:sender] = @uuid
|
||||
gxg_message[:to] = server_message[:sender]
|
||||
gxg_message[:body] = {:sender => rpc_message[:to], :to => rpc_message[:sender], :id => rpc_message[:id], :subject => rpc_message[:subject], :body => {:response => "success", :result => result}}
|
||||
@connector.transmit(gxg_message)
|
||||
#
|
||||
rescue Exception => the_error
|
||||
gxg_message = new_message
|
||||
gxg_message[:sender] = @uuid
|
||||
gxg_message[:to] = server_message[:sender]
|
||||
gxg_message[:body] = {:sender => rpc_message[:to], :to => rpc_message[:sender], :id => rpc_message[:id], :subject => rpc_message[:subject], :body => {:response => "fail", :error => the_error}}
|
||||
@connector.transmit(gxg_message)
|
||||
#
|
||||
end
|
||||
else
|
||||
gxg_message = new_message
|
||||
gxg_message[:sender] = @uuid
|
||||
gxg_message[:to] = server_message[:sender]
|
||||
gxg_message[:body] = {:sender => rpc_message[:to], :to => rpc_message[:sender], :id => rpc_message[:id], :subject => rpc_message[:subject], :body => {:response => "fail", :error => "remote method missing"}}
|
||||
@connector.transmit(gxg_message)
|
||||
#
|
||||
end
|
||||
end
|
||||
else
|
||||
gxg_message = new_message
|
||||
gxg_message[:sender] = @uuid
|
||||
gxg_message[:to] = server_message[:sender]
|
||||
gxg_message[:body] = {:sender => rpc_message[:to], :to => rpc_message[:sender], :id => rpc_message[:id], :subject => rpc_message[:subject], :body => {:response => "fail", :error => "no mount available"}}
|
||||
@connector.transmit(gxg_message)
|
||||
#
|
||||
end
|
||||
end
|
||||
#
|
||||
end
|
||||
end
|
||||
# process next message in @inbox
|
||||
end
|
||||
#
|
||||
def in_buffer(the_message)
|
||||
@buffer << the_message
|
||||
end
|
||||
#
|
||||
def transmit(the_message)
|
||||
if the_message.is_a?(GxG::Events::Message)
|
||||
@connector.transmit(the_message)
|
||||
end
|
||||
#
|
||||
end
|
||||
#
|
||||
def remote_call(the_message)
|
||||
# Layer 3
|
||||
if @remote_interface.size > 0
|
||||
if (the_message[:success].respond_to?(:call)) || (the_message[:fail].respond_to?(:call))
|
||||
@pending[(the_message.id().to_s.to_sym)] = the_message
|
||||
end
|
||||
rpc_message = {}
|
||||
the_message.keys.each do |the_key|
|
||||
unless [:success, :fail].include?(the_key.to_sym)
|
||||
rpc_message[(the_key)] = the_message[(the_key)]
|
||||
end
|
||||
end
|
||||
gxg_message = new_message
|
||||
gxg_message[:sender] = @uuid
|
||||
gxg_message[:to] = @remote_uuid
|
||||
gxg_message[:body] = rpc_message
|
||||
@connector.transmit(gxg_message)
|
||||
else
|
||||
error_message = "No Remote Interface Loaded."
|
||||
@pending.delete(the_message.id().to_s.to_sym)
|
||||
if (the_message[:fail].respond_to?(:call))
|
||||
the_message[:fail].call({:response => "fail", :error => error_message})
|
||||
else
|
||||
raise Exception, error_message
|
||||
end
|
||||
end
|
||||
end
|
||||
#
|
||||
def remote_interface()
|
||||
@remote_interface
|
||||
end
|
||||
#
|
||||
end
|
||||
#
|
||||
class Connector
|
||||
#
|
||||
def transmit(the_message)
|
||||
# Fully qualified GxG::Events::Message with all the data needed to send it out.
|
||||
# Packet Frame Format: {:id => <uuid>, :routing => <array>, :index => <int>, :total => <int>, :subject => <string>, :payload => <Base64-JSON>}
|
||||
if @active == true
|
||||
begin
|
||||
while @out_busy == true do
|
||||
sleep 0.333
|
||||
end
|
||||
@out_busy = true
|
||||
if the_message.is_a?(Hash)
|
||||
if the_message[:operation]
|
||||
@socket << the_message.to_json.encode64
|
||||
end
|
||||
else
|
||||
unless the_message.is_a?(GxG::Events::Message)
|
||||
raise ArgumentError, "You MUST provide a valid GxG::Events::Message message object."
|
||||
end
|
||||
if the_message[:body].is_a?(String)
|
||||
if the_message[:body].base64?
|
||||
payload = the_message[:body]
|
||||
else
|
||||
payload = the_message[:body].encode64
|
||||
end
|
||||
else
|
||||
if the_message[:body].is_any?(Hash,Array)
|
||||
payload = the_message[:body].to_json.encode64
|
||||
else
|
||||
raise ArgumentError, "You MUST supply a message body that is a Hash, an Array, or a Base64 String."
|
||||
end
|
||||
end
|
||||
if the_message[:routing].is_a?(Array)
|
||||
packet = {:id => the_message[:id], :routing => the_message[:routing], :index => 0, :total => 10000, :subject => the_message[:subject], :payload => ""}
|
||||
else
|
||||
packet = {:id => the_message[:id], :routing => [(the_message[:sender]), (the_message[:to])], :index => 0, :total => 10000, :subject => the_message[:subject], :payload => ""}
|
||||
end
|
||||
overhead = packet.to_json.size
|
||||
ranges = GxG.apportioned_ranges(payload.size,(65536 - overhead))
|
||||
packet[:total] = ranges.size
|
||||
ranges.each_with_index do |the_range,indexer|
|
||||
packet[:index] = indexer
|
||||
packet[:payload] = payload[(the_range)]
|
||||
@socket << packet.to_json.encode64
|
||||
end
|
||||
end
|
||||
@out_busy = false
|
||||
true
|
||||
rescue Exception => the_error
|
||||
log_error({:error => the_error, :parameters => {:message => the_message}})
|
||||
@out_busy = false
|
||||
false
|
||||
end
|
||||
end
|
||||
end
|
||||
#
|
||||
def receive(data)
|
||||
# Data will ALWAYS be: Base64 JSON
|
||||
# Packet Frame Format: {:id => <uuid>, :routing => <array>, :index => <int>, :total => <int>, :subject => <string>, :payload => <Base64-JSON>}
|
||||
# On Mulit-Packet transfers: :id, :routing, :total, :subject are the same on each packet - only :index and :payload differ.
|
||||
# Routing: first=sender, last=recipient; Connectors are invisible and don't have an entry in the routing table. Channels operate as Pairs.
|
||||
# :index (which packet of :total set)? 0-offset (Marshalling)
|
||||
# recipient Channel is found and sent the completed data set when all packets are in. Further processing will happen in the Channel object.
|
||||
begin
|
||||
while @in_busy == true do
|
||||
sleep 0.333
|
||||
end
|
||||
@in_busy = true
|
||||
packet = JSON.parse(data.decode64,{:symbolize_names => true})
|
||||
unless packet.is_a?(Hash)
|
||||
raise Exception, "Malformed Packet received."
|
||||
end
|
||||
# puts "Message Received: #{packet.inspect}"
|
||||
if packet[:operation]
|
||||
# Connector Operation
|
||||
case packet[:operation]
|
||||
when "introduction"
|
||||
@remote_uuid = packet[:uuid]
|
||||
when "heartbeat_query"
|
||||
self.transmit({:operation => "heartbeat_acknowledge"})
|
||||
when "create_channel"
|
||||
unless @channels[(packet[:uuid])]
|
||||
@channels[(packet[:uuid])] = GxG::Networking::Channel.new(self,{:uuid => packet[:uuid], :remote_uuid => packet[:remote_uuid]})
|
||||
end
|
||||
end
|
||||
else
|
||||
# Data packet
|
||||
unless (packet[:id] && packet[:routing] && packet[:index] && packet[:total] && packet[:payload])
|
||||
raise Exception, "Malformed Packet received."
|
||||
end
|
||||
# puts "Routing: #{packet[:routing].inspect}"
|
||||
sender = packet[:routing].first.to_sym
|
||||
recipient = packet[:routing].last.to_sym
|
||||
if packet[:total] > 1
|
||||
# Multi-part Packet Set
|
||||
unless @buffers[(recipient)].is_a?(Array)
|
||||
@buffers[(recipient)] = []
|
||||
packet[:total].times do
|
||||
@buffers[(recipient)] << nil
|
||||
end
|
||||
end
|
||||
@buffers[(recipient)][(packet[:index])] = packet
|
||||
complete = true
|
||||
@buffers[(recipient)].each_index do |indexer|
|
||||
unless @buffers[(recipient)][(indexer)]
|
||||
complete = false
|
||||
break
|
||||
end
|
||||
end
|
||||
if complete == true
|
||||
buffer = ""
|
||||
@buffers[(recipient)].each_index do |indexer|
|
||||
buffer = (buffer + @buffers[(recipient)][(indexer)][:payload])
|
||||
end
|
||||
if buffer.base64?
|
||||
begin
|
||||
buffer = ::JSON.parse(buffer.decode64,{:symbolize_names => true})
|
||||
rescue Exception => the_error
|
||||
log_error({:error => the_error, :parameters => {:payload => buffer}})
|
||||
end
|
||||
end
|
||||
the_message = new_message
|
||||
the_message[:sender] = sender
|
||||
the_message[:to] = recipient
|
||||
the_message[:id] = packet[:id]
|
||||
the_message[:subject] = packet[:subject]
|
||||
the_message[:body] = buffer
|
||||
unless @channels[(recipient)]
|
||||
@channels[(recipient)] = GxG::Networking::Channel.new(self,{:uuid => recipient, :remote_uuid => sender})
|
||||
end
|
||||
@channels[(recipient)].in_buffer(the_message)
|
||||
@buffers.delete(recipient)
|
||||
end
|
||||
else
|
||||
# Single Packet
|
||||
if packet[:payload].is_a?(String)
|
||||
if packet[:payload].base64?
|
||||
begin
|
||||
packet[:payload] = ::JSON.parse(packet[:payload].decode64,{:symbolize_names => true})
|
||||
rescue Exception => the_error
|
||||
log_error({:error => the_error, :parameters => {:payload => packet[:payload]}})
|
||||
end
|
||||
end
|
||||
end
|
||||
the_message = new_message
|
||||
the_message[:sender] = sender
|
||||
the_message[:to] = recipient
|
||||
the_message[:id] = packet[:id]
|
||||
the_message[:subject] = packet[:subject]
|
||||
the_message[:body] = packet[:payload]
|
||||
unless @channels[(recipient)]
|
||||
@channels[(recipient)] = GxG::Networking::Channel.new(self,{:uuid => recipient, :remote_uuid => sender})
|
||||
end
|
||||
@channels[(recipient)].in_buffer(the_message)
|
||||
end
|
||||
end
|
||||
rescue Exception => the_error
|
||||
log_error({:error => the_error, :parameters => {:data => data}})
|
||||
end
|
||||
@in_busy = false
|
||||
true
|
||||
end
|
||||
#
|
||||
def close()
|
||||
self.transmit({:operation => "disconnect"})
|
||||
@socket.close(1000)
|
||||
@active = false
|
||||
end
|
||||
# Channel supports:
|
||||
def channels()
|
||||
@channels
|
||||
end
|
||||
#
|
||||
def initialize(the_url=nil)
|
||||
unless the_url
|
||||
raise ArgumentError, "You MUST provide a URL String to contact a host."
|
||||
end
|
||||
@uuid = GxG.uuid_generate().to_sym
|
||||
@remote_uuid = nil
|
||||
@active = false
|
||||
@in_busy = false
|
||||
@out_busy = false
|
||||
@pending = {}
|
||||
@channels = {}
|
||||
@buffers = {}
|
||||
@the_connector = self
|
||||
@socket = Browser::Socket.new(the_url) do |the_socket|
|
||||
the_socket.on :open do
|
||||
@active = true
|
||||
@the_connector.transmit({:operation => "introduction", :uuid => @uuid.to_s, :location => `window.location["href"]`})
|
||||
end
|
||||
the_socket.on :close do
|
||||
@active = false
|
||||
end
|
||||
the_socket.on :message do |the_event|
|
||||
@the_connector.receive(the_event.data)
|
||||
end
|
||||
end
|
||||
# Set trap for page closure / refresh / unload / etc
|
||||
`jQuery(window).bind("beforeunload", function() { Opal.eval("GxG::CONNECTION.close"); });`
|
||||
self
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
140
gxg/web/gxg_layout.rb
Normal file
140
gxg/web/gxg_layout.rb
Normal file
|
|
@ -0,0 +1,140 @@
|
|||
module GxG
|
||||
module Gui
|
||||
#
|
||||
# Layout System Supports:
|
||||
def self.layout_refresh()
|
||||
# get rectangle of item -> convert to page % offsets
|
||||
# `window.innerHeight`.to_i
|
||||
# `window.innerWidth`.to_i
|
||||
# <element>.getClientRects()[0].left
|
||||
page_width = `window.innerWidth`.to_i
|
||||
# Calculating proper page height, See: https://stackoverflow.com/questions/1145850/how-to-get-height-of-entire-document-with-javascript
|
||||
body = `document.body`
|
||||
html = `document.documentElement`
|
||||
page_bottom = 0
|
||||
%x{
|
||||
page_bottom = Math.max(body.scrollHeight, body.offsetHeight, html.clientHeight, html.scrollHeight, html.offsetHeight);
|
||||
}
|
||||
page_height = [(`window.innerHeight`.to_i),(page_bottom.to_i)].max
|
||||
# bounding areas:
|
||||
top_bound = 0.0
|
||||
left_bound = 0.0
|
||||
right_bound = 1.0
|
||||
bottom_bound = 1.0
|
||||
# Update object percentages
|
||||
[:"top-left", :top, :"top-right", :right, :"bottom-right", :bottom, :"bottom-left", :left].each do |the_position|
|
||||
::GxG::LAYOUT[(the_position)].each_pair do |the_uuid,the_record|
|
||||
object = self.find_object(the_uuid)
|
||||
if object
|
||||
rectangle = {:top => 0, :left => 0, :right => 0, :bottom => 0}
|
||||
rectangle[:top] = `#{object.element}.getBoundingClientRect().top`.to_i
|
||||
rectangle[:left] = `#{object.element}.getBoundingClientRect().left`.to_i
|
||||
rectangle[:right] = `#{object.element}.getBoundingClientRect().right`.to_i
|
||||
rectangle[:bottom] = `#{object.element}.getBoundingClientRect().bottom`.to_i
|
||||
percentages = {:top => 0.0, :left => 0.0, :right => 0.0, :bottom => 0.0}
|
||||
percentages[:top] = (rectangle[:top].to_f / page_height.to_f)
|
||||
percentages[:left] = (rectangle[:left].to_f / page_width.to_f)
|
||||
percentages[:right] = (rectangle[:right].to_f / page_width.to_f)
|
||||
percentages[:bottom] = (rectangle[:bottom].to_f / page_height.to_f)
|
||||
::GxG::LAYOUT[(the_position.to_s.to_sym)][(the_uuid.to_s.to_sym)] = percentages
|
||||
end
|
||||
end
|
||||
end
|
||||
#
|
||||
[:"top-left", :top, :"top-right", :right, :"bottom-right", :bottom, :"bottom-left", :left].each do |the_position|
|
||||
::GxG::LAYOUT[(the_position)].values.each do |the_record|
|
||||
case the_position
|
||||
when :top
|
||||
if the_record[:bottom] > top_bound
|
||||
top_bound = the_record[:bottom]
|
||||
end
|
||||
when :left
|
||||
if the_record[:right] > left_bound
|
||||
left_bound = the_record[:right]
|
||||
end
|
||||
when :right
|
||||
if the_record[:left] < right_bound
|
||||
right_bound = the_record[:left]
|
||||
end
|
||||
when :bottom
|
||||
if the_record[:top] < bottom_bound
|
||||
bottom_bound = the_record[:top]
|
||||
end
|
||||
when :"top-left"
|
||||
if the_record[:bottom] > top_bound
|
||||
top_bound = the_record[:bottom]
|
||||
end
|
||||
if the_record[:right] > left_bound
|
||||
left_bound = the_record[:right]
|
||||
end
|
||||
when :"top-right"
|
||||
if the_record[:bottom] > top_bound
|
||||
top_bound = the_record[:bottom]
|
||||
end
|
||||
if the_record[:left] < right_bound
|
||||
right_bound = the_record[:left]
|
||||
end
|
||||
when :"bottom-right"
|
||||
if the_record[:top] < bottom_bound
|
||||
bottom_bound = the_record[:top]
|
||||
end
|
||||
if the_record[:left] < right_bound
|
||||
right_bound = the_record[:left]
|
||||
end
|
||||
when :"bottom-left"
|
||||
if the_record[:top] < bottom_bound
|
||||
bottom_bound = the_record[:top]
|
||||
end
|
||||
if the_record[:right] > left_bound
|
||||
left_bound = the_record[:right]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
#
|
||||
::GxG::LAYOUT_LIMITS[:page_width] = page_width.to_i
|
||||
::GxG::LAYOUT_LIMITS[:page_height] = page_height.to_i
|
||||
::GxG::LAYOUT_LIMITS[:top] = (top_bound * page_height.to_f).to_i
|
||||
::GxG::LAYOUT_LIMITS[:top_percent] = top_bound * 100.0
|
||||
::GxG::LAYOUT_LIMITS[:left] = (left_bound * page_width.to_f).to_i
|
||||
::GxG::LAYOUT_LIMITS[:left_percent] = left_bound * 100.0
|
||||
::GxG::LAYOUT_LIMITS[:right] = (right_bound * page_width.to_f).to_i
|
||||
::GxG::LAYOUT_LIMITS[:right_percent] = right_bound * 100.0
|
||||
::GxG::LAYOUT_LIMITS[:bottom] = (bottom_bound * page_height.to_f).to_i
|
||||
::GxG::LAYOUT_LIMITS[:bottom_percent] = bottom_bound * 100.0
|
||||
true
|
||||
end
|
||||
#
|
||||
def self.layout_add_item(the_uuid=nil, the_position=nil, refresh=true)
|
||||
# store by uuid in position's record
|
||||
if [:"top-left", :top, :"top-right", :right, :"bottom-right", :bottom, :"bottom-left", :left].include?(the_position.to_s.to_sym)
|
||||
#
|
||||
::GxG::LAYOUT[(the_position.to_s.to_sym)][(the_uuid.to_s.to_sym)] = {:top => 0.0, :left => 0.0, :right => 0.0, :bottom => 0.0}
|
||||
if refresh
|
||||
::GxG::Gui::layout_refresh
|
||||
end
|
||||
#
|
||||
true
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
#
|
||||
def self.layout_remove_item(the_uuid=nil)
|
||||
[:"top-left", :top, :"top-right", :right, :"bottom-right", :bottom, :"bottom-left", :left].each do |the_position|
|
||||
if ::GxG::LAYOUT[(the_position.to_s.to_sym)][(the_uuid.to_s.to_sym)]
|
||||
::GxG::LAYOUT[(the_position.to_s.to_sym)].delete((the_uuid.to_s.to_sym))
|
||||
break
|
||||
end
|
||||
end
|
||||
::GxG::Gui::layout_refresh
|
||||
#
|
||||
true
|
||||
end
|
||||
#
|
||||
def layout_content_area()
|
||||
::GxG::LAYOUT_LIMITS
|
||||
end
|
||||
#
|
||||
end
|
||||
end
|
||||
207
gxg/web/gxg_libraries.rb
Normal file
207
gxg/web/gxg_libraries.rb
Normal file
|
|
@ -0,0 +1,207 @@
|
|||
|
||||
# ----------------------------------------------------------------------------------------------------
|
||||
# Provided Module Space for libraries to declare their :global within.
|
||||
module Libraries
|
||||
end
|
||||
# ----------------------------------------------------------------------------------------------------
|
||||
module GxG
|
||||
class Library
|
||||
#
|
||||
def initialize(the_definition=nil)
|
||||
unless the_definition.is_a?(GxG::Database::PersistedHash)
|
||||
raise ArgumentError, "You MUST provide a persisted source object."
|
||||
end
|
||||
#
|
||||
@uuid = the_definition.uuid
|
||||
@title = the_definition.title
|
||||
@version = the_definition.version
|
||||
@type = (the_definition[:type] || "text/javascript").to_s
|
||||
#
|
||||
load_list = []
|
||||
if the_definition[:requirements].is_a?(::GxG::Database::PersistedArray)
|
||||
the_definition[:requirements].each do |the_requirement|
|
||||
unless GxG::DISPLAY_DETAILS[:object].library_loaded?(the_requirement)
|
||||
load_list << the_requirement
|
||||
end
|
||||
end
|
||||
end
|
||||
GxG::DISPLAY_DETAILS[:object].load_libraries(load_list) do |all_loaded|
|
||||
if all_loaded == true
|
||||
#
|
||||
@src = the_definition[:options][:src].to_s
|
||||
unless @src.size > 0
|
||||
@src = nil
|
||||
end
|
||||
@charset = (the_definition[:options][:charset] || "utf-8").to_s
|
||||
@local = the_definition[:options][:local]
|
||||
unless @local
|
||||
@local = false
|
||||
end
|
||||
@global = the_definition[:options][:global].to_s
|
||||
unless @global.size > 0
|
||||
@global = nil
|
||||
end
|
||||
# Load native code portion if supplied.
|
||||
# If the global is already defined, your wrapper code will still work.
|
||||
unless self.loaded?()
|
||||
unless @global.to_s.size > 0
|
||||
if @type = "text/javascript"
|
||||
@global = "Opal"
|
||||
else
|
||||
@global = "GxG"
|
||||
end
|
||||
end
|
||||
if @src.is_a?(::String)
|
||||
the_uuid = @uuid
|
||||
the_type = @type
|
||||
the_charset = @charset
|
||||
the_src = @src
|
||||
%x{
|
||||
var the_element = document.createElement('script');
|
||||
the_element.id = the_uuid;
|
||||
the_element.type = the_type;
|
||||
the_element.charset = the_charset;
|
||||
the_element.src = the_src;
|
||||
document.body.appendChild(the_element);
|
||||
}
|
||||
else
|
||||
the_script = the_definition[:options][:native].to_s
|
||||
if the_script.is_a?(::String)
|
||||
if the_script.size > 0
|
||||
if the_script.base64?
|
||||
the_script = the_script.to_s.decode64
|
||||
end
|
||||
the_uuid = @uuid
|
||||
the_type = @type
|
||||
the_charset = @charset
|
||||
%x{
|
||||
var the_element = document.createElement('script');
|
||||
the_element.id = the_uuid;
|
||||
the_element.type = the_type;
|
||||
the_element.charset = the_charset;
|
||||
the_element.innerHTML = the_script;
|
||||
document.body.appendChild(the_element);
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@source = the_definition
|
||||
self.set_script(@source[:script].to_s)
|
||||
@source = nil
|
||||
else
|
||||
raise Exception, "Library load error - skipping library intitialization: #{@title}"
|
||||
end
|
||||
end
|
||||
#
|
||||
self
|
||||
end
|
||||
#
|
||||
def uuid
|
||||
@uuid
|
||||
end
|
||||
#
|
||||
def title
|
||||
@title
|
||||
end
|
||||
#
|
||||
def version()
|
||||
@version
|
||||
end
|
||||
#
|
||||
def type()
|
||||
@type
|
||||
end
|
||||
#
|
||||
def local()
|
||||
@local
|
||||
end
|
||||
#
|
||||
def global()
|
||||
@global
|
||||
end
|
||||
#
|
||||
def set_script(the_script_body="")
|
||||
result = false
|
||||
if the_script_body.size > 0
|
||||
#
|
||||
begin
|
||||
#
|
||||
if the_script_body.base64?
|
||||
the_script_body = the_script_body.decode64
|
||||
end
|
||||
#
|
||||
if @local == true
|
||||
instance_eval(the_script_body)
|
||||
else
|
||||
eval(the_script_body)
|
||||
end
|
||||
result = true
|
||||
rescue Exception => the_error
|
||||
log_error({:error => the_error, :parameters => {:script => the_script_body}})
|
||||
end
|
||||
end
|
||||
result
|
||||
end
|
||||
#
|
||||
def require_resource(the_reference=nil)
|
||||
# call self.require_resource("script-name") to load content.script-name
|
||||
result = false
|
||||
if @source
|
||||
# content source record format: {:component => "script", :type => "text/ruby", :script => ""}
|
||||
@source[:content].each do |the_script|
|
||||
if (the_script.title == the_reference || the_script.uuid == the_refrence.to_s.to_sym) && the_script[:component].to_s == "script" && the_script[:type].to_s == "text/ruby"
|
||||
self.set_script(the_script[:script].to_s)
|
||||
result = true
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
result
|
||||
end
|
||||
#
|
||||
def require_module_resource(the_reference=nil)
|
||||
# call self.require_module_resource("script-name") to load content.script-name
|
||||
result = false
|
||||
if @source
|
||||
# content source record format: {:component => "script", :type => "text/ruby", :script => ""}
|
||||
@source[:content].each do |the_script|
|
||||
if (the_script.title == the_reference || the_script.uuid == the_refrence.to_s.to_sym) && the_script[:component].to_s == "script" && the_script[:type].to_s == "text/ruby"
|
||||
if the_script[:script].size > 0
|
||||
if the_script[:script].base64?
|
||||
eval(the_script[:script].to_s.decode64)
|
||||
else
|
||||
eval(the_script[:script].to_s)
|
||||
end
|
||||
result = true
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
result
|
||||
end
|
||||
#
|
||||
def loaded?
|
||||
# Opal/JS Note: by saying `!!<js-value>` it will coerse it into a ruby boolean.
|
||||
if @type == "text/javascript"
|
||||
if (`(window.hasOwnProperty(#{@global.to_s}))`)
|
||||
true
|
||||
else
|
||||
false
|
||||
end
|
||||
else
|
||||
if @global
|
||||
if (eval("defined? #{@global.to_s}"))
|
||||
true
|
||||
else
|
||||
false
|
||||
end
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
#
|
||||
end
|
||||
1111
gxg/web/gxg_pre_foundation.rb
Normal file
1111
gxg/web/gxg_pre_foundation.rb
Normal file
File diff suppressed because it is too large
Load diff
3786
gxg/web/gxg_uri.rb
Normal file
3786
gxg/web/gxg_uri.rb
Normal file
File diff suppressed because it is too large
Load diff
2673
gxg/web/gxg_vdom.rb
Normal file
2673
gxg/web/gxg_vdom.rb
Normal file
File diff suppressed because it is too large
Load diff
2283
gxg/web/gxg_windows.rb
Normal file
2283
gxg/web/gxg_windows.rb
Normal file
File diff suppressed because it is too large
Load diff
741
gxg/web/gxg_xhr.rb
Normal file
741
gxg/web/gxg_xhr.rb
Normal file
|
|
@ -0,0 +1,741 @@
|
|||
#
|
||||
#require 'gxg_uri'
|
||||
#
|
||||
module GxG
|
||||
module Networking
|
||||
class Channel
|
||||
#
|
||||
end
|
||||
#
|
||||
class Connector
|
||||
#
|
||||
def remote_uuid()
|
||||
@remote_uuid
|
||||
end
|
||||
#
|
||||
def open?()
|
||||
@active
|
||||
end
|
||||
#
|
||||
def close()
|
||||
the_message = new_message({:operation => "close", :display_path => @display_path})
|
||||
the_message[:sender] = @uuid
|
||||
the_message[:to] = @remote_uuid
|
||||
self.pull(the_message,{:synchronous => true})
|
||||
@active = false
|
||||
@csrf = nil
|
||||
if @heart_beat_timer.is_a?(::Hash)
|
||||
GxG::DISPATCHER.cancel_timer(@heart_beat_timer)
|
||||
end
|
||||
end
|
||||
#
|
||||
def push(the_message=nil,options={})
|
||||
if @active
|
||||
# Uses PUT method, with data payload
|
||||
if the_message.is_a?(::GxG::Events::Message)
|
||||
payload = the_message.body.to_json.to_s
|
||||
the_xhr = `new XMLHttpRequest`
|
||||
`#{the_xhr}.open('PUT',#{@host_prefix},true)`
|
||||
`#{the_xhr}.timeout = #{(options[:timeout] || 5000)}`
|
||||
`#{the_xhr}.setRequestHeader('X-Requested-With','XMLHttpRequest')`
|
||||
`#{the_xhr}.setRequestHeader('Accept','application/json')`
|
||||
`#{the_xhr}.setRequestHeader('X-CSRF-Token', #{@csrf})`
|
||||
`#{the_xhr}.setRequestHeader('Content-Type', 'application/json')`
|
||||
# unless (`navigator.userAgent`).include?("Firefox") || (`navigator.userAgent`).include?("Chrome")
|
||||
# `#{the_xhr}.setRequestHeader('Content-Length', #{payload.size})`
|
||||
# end
|
||||
failed = Proc.new do |reason=nil|
|
||||
the_message.fail(reason)
|
||||
end
|
||||
succeeded = Proc.new do
|
||||
response_code = (`#{the_xhr}.status`).to_i
|
||||
if response_code < 300
|
||||
the_message.succeed(::JSON.parse(`#{the_xhr}.response`,{:symbolize_names => true}))
|
||||
else
|
||||
failed.call(response_code)
|
||||
end
|
||||
end
|
||||
`#{the_xhr}.addEventListener('load', #{succeeded})`
|
||||
`#{the_xhr}.addEventListener('error', #{failed})`
|
||||
`#{the_xhr}.addEventListener('timeout', #{failed})`
|
||||
`#{the_xhr}.addEventListener('abort', #{failed})`
|
||||
`#{the_xhr}.send(#{payload})`
|
||||
#
|
||||
true
|
||||
else
|
||||
# error?
|
||||
false
|
||||
end
|
||||
end
|
||||
#
|
||||
end
|
||||
#
|
||||
def pull(the_message=nil,options={:synchronous => false})
|
||||
if @active
|
||||
# Uses GET method, with in-URL query specifiers (no data payload)
|
||||
if the_message.is_a?(::GxG::Events::Message)
|
||||
the_query = ""
|
||||
the_message.body.keys.each do |the_key|
|
||||
the_query = the_query + ("#{the_key.to_s}=#{the_message.body[(the_key)].to_s}")
|
||||
unless the_key == the_message.body.keys.last
|
||||
the_query = the_query + "&"
|
||||
end
|
||||
end
|
||||
the_url = (@host_prefix.to_s + "?" + the_query)
|
||||
the_xhr = `new XMLHttpRequest`
|
||||
if options[:synchronous] == true
|
||||
`#{the_xhr}.open('GET',#{the_url.to_s},false)`
|
||||
else
|
||||
`#{the_xhr}.open('GET',#{the_url.to_s},true)`
|
||||
`#{the_xhr}.timeout = #{(options[:timeout] || 600000)}`
|
||||
end
|
||||
`#{the_xhr}.setRequestHeader('X-Requested-With','XMLHttpRequest')`
|
||||
`#{the_xhr}.setRequestHeader('Accept','application/json')`
|
||||
failed = Proc.new do |reason=nil|
|
||||
the_message.fail(reason)
|
||||
end
|
||||
succeeded = Proc.new do
|
||||
response_code = (`#{the_xhr}.status`).to_i
|
||||
if response_code < 300
|
||||
the_message.succeed(::JSON.parse(`#{the_xhr}.response`,{:symbolize_names => true}))
|
||||
else
|
||||
failed.call(response_code)
|
||||
end
|
||||
end
|
||||
`#{the_xhr}.addEventListener('load', #{succeeded})`
|
||||
`#{the_xhr}.addEventListener('error', #{failed})`
|
||||
`#{the_xhr}.addEventListener('timeout', #{failed})`
|
||||
`#{the_xhr}.addEventListener('abort', #{failed})`
|
||||
`#{the_xhr}.send(null)`
|
||||
#
|
||||
true
|
||||
else
|
||||
# error?
|
||||
false
|
||||
end
|
||||
end
|
||||
#
|
||||
end
|
||||
#
|
||||
def push_message(the_message=nil)
|
||||
# TODO: establish a sensible routing scheme and make better use of message :sender and :to in that idea.
|
||||
end
|
||||
#
|
||||
def pull_message(options={})
|
||||
# Use to pull a message portion or look for application messages on the server.
|
||||
end
|
||||
#
|
||||
def process_pending(options={})
|
||||
#
|
||||
end
|
||||
# Support-level methods for pulling/pushing objects or object-deltas.
|
||||
def push_object_portions(details={}, error_handler=nil, &the_handler)
|
||||
# supply thread and index to send, if successful, will post event to keep chain going.
|
||||
# Details: {:thread => "", :index => 0, :portions => 100}
|
||||
end
|
||||
# Search The Database(s)
|
||||
def search_database(details={}, error_handler=nil, &the_handler)
|
||||
# FOR NOW: limit 100 records returned at a time.
|
||||
# Use: {:limit => 100, :offset => 0} || {:page => 1}
|
||||
if details.is_a?(::Hash) && the_handler
|
||||
self.pull_data({:operation => :search_database, :criteria => details},error_handler) do |data|
|
||||
if data.is_a?(::Hash)
|
||||
the_handler.call(data)
|
||||
end
|
||||
end
|
||||
true
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
# High-level methods for pulling/pushing objects or object-deltas.
|
||||
def destroy_object(the_specifier=nil,error_handler=nil,&the_handler)
|
||||
if the_specifier && the_handler
|
||||
self.pull_data({:operation => :destroy_object, :location => the_specifier},error_handler) do |data|
|
||||
if data.is_a?(::Hash)
|
||||
the_handler.call(data)
|
||||
end
|
||||
end
|
||||
true
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
#
|
||||
def push_data(details={}, error_handler=nil,&the_handler)
|
||||
if details.is_a?(::Hash) && the_handler
|
||||
content = details.to_json.to_s.encode64
|
||||
msg_frame = {:sender => @uuid.to_s, :to => @remote_uuid.to_s, :display_path => @display_path, :operation => "put_portion", :thread => "00000000-0000-4000-0000-000000000000", :index => 99999, :total => 100000, :payload => ""}
|
||||
portions = ::GxG::apportioned_ranges(content.size,(65536 - msg_frame.to_json.size))
|
||||
#
|
||||
if portions.size > 1
|
||||
the_message = new_message({ :operation => "put_data", :display_path => @display_path})
|
||||
the_message[:sender] = @uuid
|
||||
the_message[:to] = @remote_uuid
|
||||
the_message[:body][:thread] = the_message.id().to_s
|
||||
#
|
||||
if the_handler.respond_to?(:call)
|
||||
the_message.on(:success) do |response|
|
||||
@buffer.delete(the_message[:body][:thread])
|
||||
the_handler.call(response)
|
||||
end
|
||||
end
|
||||
if error_handler.respond_to?(:call)
|
||||
the_message.on(:fail) do |response|
|
||||
@buffer.delete(the_message[:body][:thread])
|
||||
error_handler.call(response)
|
||||
end
|
||||
end
|
||||
#
|
||||
@buffer[(the_message[:body][:thread])] = []
|
||||
#
|
||||
portions.each_with_index do |the_range, indexer|
|
||||
the_msg_portion = msg_frame.clone
|
||||
the_msg_portion[:thread] = the_message[:body][:thread]
|
||||
the_msg_portion[:index] = indexer
|
||||
the_msg_portion[:total] = portions.size
|
||||
the_msg_portion[:payload] = content[(the_range)]
|
||||
@buffer[(the_message[:body][:thread])] << the_msg_portion
|
||||
end
|
||||
# -----------------------------------------------------
|
||||
# Send portions
|
||||
(0..(portions.size - 1)).each do |the_portion|
|
||||
::GxG::DISPATCHER.post_event(:communications) do
|
||||
if @buffer[(the_message[:body][:thread])].is_a?(::Array)
|
||||
if @buffer[(the_message[:body][:thread])][(the_portion)].is_a?(::Hash)
|
||||
#
|
||||
portion_message = new_message(@buffer[(the_message[:body][:thread])][(the_portion)])
|
||||
portion_message[:sender] = @uuid
|
||||
portion_message[:to] = @remote_uuid
|
||||
#
|
||||
portion_message.on(:succeed) do |response|
|
||||
if the_portion == (@buffer[(the_message[:body][:thread])].size - 1)
|
||||
if @buffer[(the_message[:body][:thread])]
|
||||
GxG::CONNECTION.push(the_message)
|
||||
end
|
||||
end
|
||||
end
|
||||
#
|
||||
portion_message.on(:fail) do |response|
|
||||
the_message.fail(response)
|
||||
end
|
||||
#
|
||||
GxG::CONNECTION.push(portion_message)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
#
|
||||
else
|
||||
# single message payload
|
||||
the_message = new_message({ :operation => "put_data", :display_path => @display_path, :payload => content})
|
||||
the_message[:sender] = @uuid
|
||||
the_message[:to] = @remote_uuid
|
||||
GxG::CONNECTION.push(the_message)
|
||||
end
|
||||
#
|
||||
true
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
#
|
||||
def push_object(details={}, error_handler=nil,&the_handler)
|
||||
if details.is_a?(::Hash) && the_handler
|
||||
self.push_data({:operation => :put_object, :data => details},error_handler) do |data|
|
||||
if data.is_a?(::Hash)
|
||||
the_handler.call(data)
|
||||
end
|
||||
end
|
||||
true
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
#
|
||||
def pull_data(the_specifier={},error_handler=nil,&the_handler)
|
||||
if the_specifier && the_handler
|
||||
the_message = new_message({:operation => "get_data", :thread => nil, :display_path => @display_path, :details => the_specifier.to_json.encode64})
|
||||
the_message[:sender] = @uuid
|
||||
the_message[:to] = @remote_uuid
|
||||
the_message[:body][:thread] = the_message.id().to_s
|
||||
if error_handler.respond_to?(:call)
|
||||
the_message.on(:fail,&error_handler)
|
||||
end
|
||||
the_message.on(:success) do |response|
|
||||
if response.is_a?(::Hash)
|
||||
if response[:result] == true
|
||||
case response[:operation]
|
||||
when "this_portion"
|
||||
data = response[:payload]
|
||||
if data.is_a?(::String)
|
||||
# FIXME: <String>.base64? needs a proper working regex (or go brute force)
|
||||
# data = Base64::strict_decode64(data)
|
||||
if data.base64?
|
||||
data = data.decode64
|
||||
end
|
||||
if data.json?
|
||||
begin
|
||||
data = ::JSON::parse(data,{:symbolize_names => true})
|
||||
rescue Exception => the_error
|
||||
log_error({:error => the_error, :parameters => {:data => data}})
|
||||
end
|
||||
end
|
||||
the_handler.call(data)
|
||||
else
|
||||
log_error({:error => Exception.new("Malformed Response Data"), :parameters => {:data => data}})
|
||||
end
|
||||
when "get_portions"
|
||||
@pending[(response[:thread])] = the_message
|
||||
@buffers[(response[:thread])] = []
|
||||
the_messages = []
|
||||
(0..(response[:total] - 1)).each do |indexer|
|
||||
portion_message = new_message({:operation => "get_portion", :thread => (response[:thread]), :index => indexer, :display_path => @display_path})
|
||||
portion_message[:sender] = @uuid
|
||||
portion_message[:to] = @remote_uuid
|
||||
#
|
||||
portion_message.on(:success) do |reply|
|
||||
@buffers[(response[:thread])] << reply
|
||||
if @buffers[(response[:thread])].size == response[:total]
|
||||
already = []
|
||||
the_data = ""
|
||||
until already.size == response[:total] do
|
||||
(0..(response[:total] - 1)).each do |referencer|
|
||||
unless already.index(referencer)
|
||||
the_data = (the_data + (@buffers[(response[:thread])][(referencer)][:payload]))
|
||||
already << referencer
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
signal_message = new_message({:operation => "got_portions", :thread => response[:thread], :display_path => @display_path})
|
||||
signal_message[:sender] = @uuid
|
||||
signal_message[:to] = @remote_uuid
|
||||
GxG::CONNECTION.pull(signal_message)
|
||||
#
|
||||
@pending.delete(response[:thread])
|
||||
@buffers.delete(response[:thread])
|
||||
if the_data.size > 0
|
||||
# FIXME: <String>.base64? needs a proper working regex (or go brute force)
|
||||
# the_data = Base64::strict_decode64(the_data)
|
||||
if the_data.base64?
|
||||
the_data = the_data.decode64
|
||||
end
|
||||
if the_data.json?
|
||||
begin
|
||||
the_data = ::JSON::parse(the_data,{:symbolize_names => true})
|
||||
rescue Exception => the_error
|
||||
log_error({:error => the_error, :parameters => {:data => the_data}})
|
||||
end
|
||||
end
|
||||
the_handler.call(the_data)
|
||||
else
|
||||
log_warn("Empty payload from portions.")
|
||||
end
|
||||
end
|
||||
end
|
||||
#
|
||||
portion_message.on(:fail) do |reply|
|
||||
log_warn("Cancelling Transfer: portion #{indexer} of thread #{response[:thread]}.")
|
||||
@pending.delete(response[:thread])
|
||||
@buffers.delete(response[:thread])
|
||||
if error_handler.respond_to?(:call)
|
||||
error_handler.call(reply)
|
||||
end
|
||||
end
|
||||
#
|
||||
# GxG::CONNECTION.pull(portion_message)
|
||||
the_messages << portion_message
|
||||
#
|
||||
end
|
||||
the_messages.each do |a_message|
|
||||
GxG::CONNECTION.pull(a_message)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
GxG::CONNECTION.pull(the_message)
|
||||
end
|
||||
end
|
||||
#
|
||||
def pull_object(the_specifier=nil,error_handler=nil,&the_handler)
|
||||
# Use pull_data({:operation => :get_object, :location => the_specifier})
|
||||
if the_specifier && the_handler
|
||||
# GxG::DISPLAY_DETAILS[:object].set_busy(true)
|
||||
self.pull_data({:operation => :get_object, :location => the_specifier},error_handler) do |data|
|
||||
if data.is_a?(::Hash)
|
||||
the_handler.call(::GxG::Database::process_detached_import(data))
|
||||
# GxG::DISPLAY_DETAILS[:object].set_busy(false)
|
||||
end
|
||||
end
|
||||
true
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
#
|
||||
def get_permissions(the_specifier=nil,error_handler=nil,&the_handler)
|
||||
# See vfs: :action => 'set_permissions', :path => (location), :alterations => [{:credential => nil, :permissions => {...}}], :revocations => [(a-credential)]
|
||||
if the_specifier && the_handler
|
||||
# GxG::DISPLAY_DETAILS[:object].set_busy(true)
|
||||
self.pull_data({:operation => :get_permissions, :location => the_specifier},error_handler) do |data|
|
||||
if data.is_a?(::Hash)
|
||||
the_handler.call(data)
|
||||
# GxG::DISPLAY_DETAILS[:object].set_busy(false)
|
||||
end
|
||||
end
|
||||
true
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
#
|
||||
# Libarary Loading Support:
|
||||
def library_pull(details={}, error_handler=nil, &the_handler)
|
||||
if self.open?
|
||||
if details.is_a?(::Hash) && the_handler
|
||||
self.pull_data({:operation => :get_library}.merge!(details),error_handler) do |data|
|
||||
the_handler.call(data)
|
||||
end
|
||||
true
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
end
|
||||
# Format Support:
|
||||
def format_pull(details={}, error_handler=nil, &the_handler)
|
||||
if self.open?
|
||||
if details.is_any?(::Array, ::Hash) && the_handler
|
||||
self.pull_data({:operation => :format_pull, :criteria => details},error_handler) do |data|
|
||||
the_handler.call(data)
|
||||
end
|
||||
true
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
end
|
||||
# TODO: format_push(data={})
|
||||
# Admin:
|
||||
def admin(details={}, error_handler=nil, &the_handler)
|
||||
if self.open?
|
||||
if details.is_a?(::Hash) && the_handler
|
||||
self.pull_data(({:operation => :admin}.merge!(details)),error_handler) do |data|
|
||||
the_handler.call(data)
|
||||
end
|
||||
true
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
end
|
||||
#
|
||||
# VFS:
|
||||
def vfs(details={}, error_handler=nil, &the_handler)
|
||||
# Specify either a :location or :name
|
||||
if self.open?
|
||||
if details.is_a?(::Hash) && the_handler
|
||||
self.pull_data(({:operation => :vfs}.merge!(details)),error_handler) do |data|
|
||||
the_handler.call(data)
|
||||
end
|
||||
true
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
end
|
||||
#
|
||||
# Application Support:
|
||||
def app_state_push(details={}, error_handler=nil, &the_handler)
|
||||
if details.is_a?(::Hash) && the_handler
|
||||
self.push_data({:operation => :app_state_push, :application => details[:application], :data => details[:data]},error_handler) do |data|
|
||||
if data.is_a?(::Hash)
|
||||
the_handler.call(data)
|
||||
end
|
||||
end
|
||||
true
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
#
|
||||
def app_state_pull(details={}, error_handler=nil, &the_handler)
|
||||
if self.open?
|
||||
if details.is_a?(::Hash) && the_handler
|
||||
self.pull_data({:operation => :app_state_pull, :application => details[:application]},error_handler) do |data|
|
||||
the_handler.call(data)
|
||||
end
|
||||
true
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
end
|
||||
#
|
||||
def application_open(details={}, error_handler=nil, &the_handler)
|
||||
# Specify either a :location or :name
|
||||
if self.open?
|
||||
if details.is_a?(::Hash) && the_handler
|
||||
if details[:name]
|
||||
self.pull_data({:operation => :application_open, :restore => (details[:restore] || false), :name => details[:name]},error_handler) do |data|
|
||||
the_handler.call(data)
|
||||
end
|
||||
else
|
||||
self.pull_data({:operation => :application_open, :restore => (details[:restore] || false), :location => details[:location]},error_handler) do |data|
|
||||
the_handler.call(data)
|
||||
end
|
||||
end
|
||||
true
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
end
|
||||
#
|
||||
def application_close(details={}, error_handler=nil, &the_handler)
|
||||
if self.open?
|
||||
if details.is_a?(::Hash) && the_handler
|
||||
self.pull_data({:operation => :application_close, :application => details[:application]},error_handler) do |data|
|
||||
the_handler.call(data)
|
||||
end
|
||||
true
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
end
|
||||
#
|
||||
def application_list(error_handler=nil, &the_handler)
|
||||
if self.open?
|
||||
if the_handler
|
||||
self.pull_data({:operation => :application_list},error_handler) do |data|
|
||||
the_handler.call(data)
|
||||
end
|
||||
true
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
end
|
||||
#
|
||||
def application_menu(error_handler=nil, &the_handler)
|
||||
if self.open?
|
||||
if the_handler
|
||||
self.pull_data({:operation => :application_menu},error_handler) do |data|
|
||||
the_handler.call(data)
|
||||
end
|
||||
true
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
end
|
||||
#
|
||||
def entries(details={}, error_handler=nil, &the_handler)
|
||||
if self.open?
|
||||
if details.is_a?(::Hash) && the_handler
|
||||
GxG::DISPLAY_DETAILS[:object].set_busy(true)
|
||||
self.pull_data({:operation => :entries, :location => details[:location]},error_handler) do |data|
|
||||
the_handler.call(data)
|
||||
GxG::DISPLAY_DETAILS[:object].set_busy(false)
|
||||
end
|
||||
true
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
end
|
||||
#
|
||||
def heartbeat()
|
||||
if @active
|
||||
heartbeat_message = new_message({:operation => "heartbeat", :display_path => @display_path})
|
||||
heartbeat_message[:sender] = @uuid.to_s
|
||||
heartbeat_message[:to] = @remote_uuid.to_s
|
||||
heartbeat_message.on(:success) do |response|
|
||||
if response.is_a?(::Hash)
|
||||
# FIXME: possible conflict with proper credential setting.
|
||||
if response[:result][:status] == "credentialed"
|
||||
# GxG::DISPLAY_DETAILS[:logged_in] = true
|
||||
else
|
||||
# GxG::DISPLAY_DETAILS[:logged_in] = false
|
||||
end
|
||||
GxG::DISPLAY_DETAILS[:server_status] = response[:result][:server].to_s.to_sym
|
||||
end
|
||||
end
|
||||
GxG::CONNECTION.pull(heartbeat_message)
|
||||
end
|
||||
end
|
||||
#
|
||||
# Set Credential:
|
||||
def update_credential(details={}, error_handler=nil, &the_handler)
|
||||
if self.open?
|
||||
if details.is_a?(::Hash) && the_handler
|
||||
content = details.to_json.to_s.encrypt(@csrf.to_s).encode64
|
||||
the_message = new_message({:operation => "update_credential", :display_path => @display_path, :payload => content})
|
||||
the_message[:sender] = @uuid
|
||||
the_message[:to] = @remote_uuid
|
||||
the_message[:body][:thread] = the_message.id().to_s
|
||||
the_message.on(:success) do |the_response|
|
||||
if the_response.is_a?(::Hash)
|
||||
if the_response[:status] == "credentialed"
|
||||
GxG::DISPLAY_DETAILS[:logged_in] = true
|
||||
end
|
||||
end
|
||||
the_handler.call(the_response)
|
||||
end
|
||||
if error_handler
|
||||
the_message.on(:fail) do |the_response|
|
||||
error_handler.call(the_response)
|
||||
end
|
||||
end
|
||||
GxG::CONNECTION.push(the_message)
|
||||
end
|
||||
end
|
||||
end
|
||||
#
|
||||
def downgrade_credential(&the_handler)
|
||||
# ????
|
||||
if @active
|
||||
downgrade_message = new_message({:operation => "downgrade_credential", :display_path => @display_path})
|
||||
downgrade_message[:sender] = @uuid.to_s
|
||||
downgrade_message[:to] = @remote_uuid.to_s
|
||||
downgrade_message.on(:success) do |response|
|
||||
# var evt = new CustomEvent('credentialchanged', { detail: "state" }); ??
|
||||
# window.dispatchEvent(evt);
|
||||
if response.is_a?(::Hash)
|
||||
# FIXME: possible conflict with proper credential setting.
|
||||
if response[:status] == "uncredentialed"
|
||||
GxG::DISPLAY_DETAILS[:logged_in] = false
|
||||
if the_handler.respond_to?(:call)
|
||||
the_handler.call(response)
|
||||
end
|
||||
new_event = `new CustomEvent('credentialchanged', { detail: #{response[:status].to_s} })`
|
||||
`window.dispatchEvent(#{new_event})`
|
||||
else
|
||||
# GxG::DISPLAY_DETAILS[:logged_in] = true
|
||||
end
|
||||
# GxG::DISPLAY_DETAILS[:server_status] = response[:server].to_s.to_sym
|
||||
end
|
||||
end
|
||||
GxG::CONNECTION.pull(downgrade_message)
|
||||
end
|
||||
end
|
||||
#
|
||||
def relative_url()
|
||||
@relative_url
|
||||
end
|
||||
#
|
||||
def query()
|
||||
@query
|
||||
end
|
||||
#
|
||||
def article()
|
||||
@article
|
||||
end
|
||||
#
|
||||
def initialize()
|
||||
if (`window.location['href']`).include?("https://")
|
||||
@host_prefix = ("https://" + `window.location['host']`)
|
||||
else
|
||||
@host_prefix = ("http://" + `window.location['host']`)
|
||||
end
|
||||
@relative_url = ""
|
||||
# Find Relative URL
|
||||
page_name = File::basename(`window.location['pathname']`)
|
||||
subpath_array = (`window.location['pathname']`.split("/"))
|
||||
found_at = subpath_array.index(page_name)
|
||||
if found_at.is_a?(::Numeric)
|
||||
if found_at > 1
|
||||
@relative_url = subpath_array[(0..(found_at - 1))].join("/")
|
||||
end
|
||||
end
|
||||
# Process Query if found
|
||||
@article = nil
|
||||
@query = {}
|
||||
query_string = `window.location.href`.split("?")[1]
|
||||
if query_string
|
||||
@article = query_string.split("#")[1]
|
||||
query_string = query_string.split("#")[0]
|
||||
if query_string.include?("&")
|
||||
query_array = query_string.split("&")
|
||||
else
|
||||
query_array = query_string.split("&")
|
||||
end
|
||||
query_array.each do |entry|
|
||||
buffer = entry.split("=")
|
||||
@query[(buffer[0].to_s.to_sym)] = buffer[1]
|
||||
end
|
||||
else
|
||||
@article = `window.location.href`.split("#")[1]
|
||||
end
|
||||
#
|
||||
@uuid = GxG.uuid_generate().to_sym
|
||||
@remote_uuid = nil
|
||||
@active = true
|
||||
@display_path = nil
|
||||
@in_busy = false
|
||||
@out_busy = false
|
||||
@pending = {}
|
||||
@channels = {}
|
||||
@buffers = {}
|
||||
@csrf = nil
|
||||
@the_connector = self
|
||||
# Make introduction to host and aquire csrf token
|
||||
the_introduction = new_message({:operation => "introduction", :introducing => @uuid.to_s})
|
||||
the_introduction[:sender] = @uuid.to_s
|
||||
the_introduction[:to] = @host_prefix.to_s
|
||||
the_introduction.on(:success) do |response|
|
||||
if response[:result] == "OK"
|
||||
if response[:csrf]
|
||||
@csrf = response[:csrf]
|
||||
end
|
||||
if response[:status] == "credentialed"
|
||||
GxG::DISPLAY_DETAILS[:logged_in] = true
|
||||
else
|
||||
GxG::DISPLAY_DETAILS[:logged_in] = false
|
||||
end
|
||||
#
|
||||
if response[:display] == "unavailable"
|
||||
`alert('Display Resource Unavailable')`
|
||||
else
|
||||
@display_path = response[:display]
|
||||
@active = true
|
||||
# Are we logged in at the server and recovering from a crash ??
|
||||
GxG::DISPATCHER.post_event(:display) do
|
||||
until (`document.readyState`.to_s == "complete") do
|
||||
sleep 0.5
|
||||
end
|
||||
::GxG::DISPLAY_DETAILS[:query] = @query
|
||||
::GxG::DISPLAY_DETAILS[:article] = @article
|
||||
::GxG::DISPLAY_DETAILS[:object] = ::GxG::Gui::Page.new(@display_path)
|
||||
GxG::DISPATCHER.post_event(:display) do
|
||||
sleep 1.0
|
||||
::GxG::DISPLAY_DETAILS[:object].layout_refresh
|
||||
end
|
||||
end
|
||||
#
|
||||
end
|
||||
#
|
||||
end
|
||||
end
|
||||
the_introduction.on(:fail) do |response|
|
||||
@active = false
|
||||
log_error({:error => Exception.new("Communication Error"), :parameters => {:response => response}})
|
||||
end
|
||||
GxG::DISPATCHER.post_event(:communications) do
|
||||
GxG::CONNECTION.pull(the_introduction)
|
||||
@heart_beat_timer = GxG::DISPATCHER.add_periodic_timer({:interval => 60.0}) do
|
||||
GxG::CONNECTION.heartbeat()
|
||||
end
|
||||
end
|
||||
#
|
||||
`window.addEventListener('unload', do_unload,false);function do_unload() { Opal.GxG.$eval("GxG::CONNECTION.close"); };`
|
||||
#
|
||||
self
|
||||
end
|
||||
#
|
||||
end
|
||||
end
|
||||
end
|
||||
Loading…
Reference in a new issue