mirror of
https://github.com/mistergibson/gxg-web-client.git
synced 2026-08-15 03:26:00 -07:00
523 lines
No EOL
21 KiB
Ruby
523 lines
No EOL
21 KiB
Ruby
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 menu_item_select(data=nil)
|
|
# override this in your object script.
|
|
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 |