gxg-web-client/gxg/web/gxg_ferro.rb
2021-11-16 08:58:51 -08:00

8418 lines
379 KiB
Ruby

#
module GxG
DISPLAY_DETAILS = {:server_status => :running, :logged_in => false, :host_path => "", :use_ssl => false, :connection => nil, :socket => nil, :object => nil, :theme => "default", :query => {}, :article => nil, :mode => :browsing, :mouse_x => 0, :mouse_y => 0, :mousedown => false}
module Gui
#
module Css
@@CssCache = []
@@CssRuleProperties = {}
#
def self.load_font(font_profile=nil)
if font_profile.is_any?(::Hash, ::GxG::Database::PersistedHash)
if font_profile.is_a?(::GxG::Database::PersistedHash)
font_profile = font_profile.unpersist()
end
if font_profile[:resource_type] == "font-face"
thefamily = (font_profile[:font_family] || "Unknown")
thestyle = (font_profile[:font_style] || "normal")
theweight = (font_profile[:font_weight] || 400).to_s
if font_profile[:format]
fontformat = font_profile[:format].to_s.downcase
else
case File.extname(font_profile[:source]).downcase
when ".ttf", ".otf"
fontformat = "truetype"
when ".eot"
fontformat = "embedded-opentype"
when ".svg"
fontformat = "svg"
when ".woff"
fontformat = "woff"
when ".woff2"
fontformat = "woff2"
else
fontformat = "unknown"
end
end
if ["sftp:","ftp:/","http:", "https"].include?(font_profile[:source][0..4].downcase)
# reach out over the internet for the resource
source_url = font_profile[:source]
else
# search local site at resource path
source_url = GxG::DISPLAY_DETAILS[:object].site_asset(font_profile[:source])
end
#
%x{
var family = #{thefamily.to_s};
var sourceurl = "url('" + #{source_url} + "')";
var theformat = "format('" + #{fontformat} + "')";
if (theformat == "format('unknown')") {
var sourcestring = sourceurl;
} else {
var sourcestring = (sourceurl + " " + theformat);
};
var theweight = #{theweight};
var thestyle = #{thestyle};
var thefont = new FontFace(family, sourceurl, {weight: theweight, style: thestyle});
thefont.load().then(function(loaded_face) {
document.fonts.add(loaded_face);
}).catch(function(error) {
alert(error);
});
}
true
else
log_warn("You attempted to load a non-font resource as a font: #{font_profile.inspect}")
false
end
else
log_warn("You attempted to load a font with the wrong value: #{font_profile.inspect}")
false
end
end
#
class CSSRule
# Provide EASY interface to native CSS Rule.
def initialize(the_native_rule)
@native_rule = the_native_rule
@name = (`#{@native_rule}.selectorText` || "").to_s.to_sym
end
#
def native_object()
@native_rule
end
#
def inspect()
"<CSSRule: #{@name}>"
end
#
def name()
@name
end
#
def keys()
GxG::Gui::Css::valid_properties()
end
#
def [](the_key)
`#{@native_rule}.style[#{the_key}]`
end
#
def []=(the_key, the_value)
#
`#{@native_rule}.style[#{the_key}] = #{(the_value || "")}`
the_value
end
#
def merge(properties={})
properties.keys.each do |the_key|
self[(the_key)] = properties[(the_key)]
end
self
end
#
end
#
def self.gxg_style_sheet()
result = nil
sheets = `document.styleSheets.length`.to_i
(0..(sheets - 1)).each do |sheet_index|
the_sheet = `document.styleSheets[#{sheet_index}]`
if the_sheet
if `#{the_sheet}.href`.to_s.include?("/themes/page.css")
result = the_sheet
break
end
end
end
result
end
#
def self.refresh_css_cache()
the_style_sheet = GxG::Gui::Css::gxg_style_sheet()
if the_style_sheet
new_cache = []
begin
ruleset = `#{the_style_sheet}.cssRules`
rescue Exception => the_error
ruleset = `#{the_style_sheet}.rules`
end
if ruleset
rules_count = (`#{ruleset}.length`.to_i)
if rules_count > 0
end_index = ((`#{ruleset}.length`.to_i) - 1)
else
end_index = 0
end
(0..(end_index)).each do |indexer|
begin
native_rule = `#{the_style_sheet}.cssRules[#{indexer}]`
rescue Exception => the_error
native_rule = `#{the_style_sheet}.rules[#{indexer}]`
end
if native_rule
new_cache << GxG::Gui::Css::CSSRule.new(native_rule)
end
end
end
@@CssCache = new_cache
end
true
end
#
def self.valid_properties()
@@CssRuleProperties.keys
end
#
def self.get_property_name(the_key=nil)
result = nil
if the_key.is_a?(::Symbol)
result = @@CssRuleProperties[(the_key)]
end
result
end
#
def self.rules()
result = []
@@CssCache.each do |entry|
result << entry.name
end
result
end
#
def self.find_rule(the_rule_selector=nil)
result = []
@@CssCache.each do |entry|
if entry.name == (the_rule_selector.to_s.to_sym)
result << entry
end
end
result
end
#
def self.set_rule(the_rule_selector=nil, the_properties={}, options={:merge => true})
# find existing
# remove existing
# create or re-create
the_style_sheet = GxG::Gui::Css::gxg_style_sheet()
if the_style_sheet
found = GxG::Gui::Css::find_rule(the_rule_selector)
if found.size > 0
GxG::Gui::Css::remove_rule(the_rule_selector)
end
# create it
the_site_prefix = ::GxG::CONNECTION.relative_url()
rule_string = "{ "
the_properties.keys.each do |property_key|
the_value = the_properties[(property_key)].to_s
if the_value.include?("url(")
end_point = the_value.index(")")
the_path = the_value[4..(end_point - 1)]
unless the_path.include?("data:") || the_path.include?("http:") || the_path.include?("https:")
unless (the_path[(0..(the_site_prefix.size - 1))] == the_site_prefix)
the_value = ("url(" + File.expand_path(the_site_prefix + "/" + the_path) + ")")
end
end
end
rule_string = rule_string + ("#{property_key.to_s}: #{the_value}; ")
end
rule_string = rule_string + " }"
if `#{the_style_sheet}.insertRule(#{the_rule_selector.to_s + rule_string.to_s},(#{the_style_sheet}.cssRules.length))`.to_i == `(#{the_style_sheet}.cssRules.length - 1)`.to_i
begin
native_rule = `#{the_style_sheet}.cssRules[(#{the_style_sheet}.cssRules.length - 1)]`
rescue Exception => the_error
native_rule = `#{the_style_sheet}.rules[(#{the_style_sheet}.cssRules.length - 1)]`
end
@@CssCache << GxG::Gui::Css::CSSRule.new(native_rule)
true
else
log_warn("GxG Style Sheet Rule creation failed..")
false
end
else
log_warn("GxG Style Sheet not found.")
false
end
#
end
#
def self.remove_rule(the_rule_selector=nil)
found_indexes = []
@@CssCache.each_with_index do |entry, index|
if entry.name == the_rule_selector.to_s.to_sym || entry.name == the_rule_selector.to_s
found_indexes << index
end
end
found = []
found_indexes.reverse.each do |the_index|
found << @@CssCache.delete_at(the_index)
end
if found.size > 0
found.each do |the_rule|
native_rule = the_rule.native_object()
the_sheet = GxG::Gui::Css::gxg_style_sheet()
if the_sheet
rule_count = `#{the_sheet}.cssRules.length`.to_i
if rule_count > 0
(0..(rule_count - 1)).to_a.reverse.each do |rule_index|
other_native_rule = `#{the_sheet}.cssRules[#{rule_index}]`
if other_native_rule
if `#{native_rule}.selectorText == #{other_native_rule}.selectorText`
`#{the_sheet}.deleteRule(#{rule_index})`
end
end
end
end
end
end
true
else
false
end
end
#
end
#
# --------------------------------------------------------------------------------------------------------------------------------------
module EventHandlers
# Constants:
EVENT_TYPES = [
"abort",
"afterprint",
"animatedend",
"animationiteration",
"animationstart",
"beforeprint",
"beforeunload",
"blur",
"canplay",
"canplaythrough",
"change",
"click",
"contextmenu",
"copy",
"cut",
"dblclick",
"drag",
"dragend",
"dragcenter",
"dragleave",
"dragover",
"dragstart",
"drop",
"durationchange",
"ended",
"error",
"focus",
"focusin",
"focusout",
"fullscreenchange",
"fullscreenerror",
"hashchange",
"input",
"invalid",
"keydown",
"keypress",
"keyup",
"load",
"loadeddata",
"loadedmetadata",
"loadstart",
"message",
"mousedown",
"mouseenter",
"mouseleave",
"mousemove",
"mouseover",
"mouseup",
"offline",
"online",
"open",
"pagehide",
"pageshow",
"paste",
"play",
"playing",
"popstate",
"progress",
"ratechange",
"resize",
"reset",
"scroll",
"search",
"seeked",
"select",
"show",
"stalled",
"storage",
"submit",
"suspend",
"timeupdate",
"toggle",
"touchcancel",
"touchend",
"touchmove",
"touchstart",
"transitionend",
"unload",
"volumechange",
"waiting",
"wheel"
]
#
EVENT_GENRES = {
:click => :mouse,
:contextmenu => :mouse,
:dblclick => :mouse,
:mouseenter => :mouse,
:mouseleave => :mouse,
:mouseover => :mouse,
:mouseup => :mouse,
:mousedown => :mouse,
:mousemove => :mouse,
:mouseout => :mouse,
:keydown => :keyboard,
:keypress => :keyboard,
:keyup => :keyboard,
:touchcancel => :touch,
:touchmove => :touch,
:touchstart => :touch,
:touchend => :touch,
:animationstart => :animation,
:animationend => :animation,
:animationiteration => :animation,
:animationpause => :animation,
:animationresume => :animation,
:animationstop => :animation,
:input => :input,
:drag => :drag,
:dragend => :drag,
:dragenter => :drag,
:dragleave => :drag,
:dragover => :drag,
:dragstart => :drag,
:drop => :drag,
:wheel => :wheel,
:abort => :ui,
:beforeunload => :ui,
:error => :ui,
:load => :ui,
:resize => :ui,
:scroll => :ui,
:select => :ui,
:unload => :ui,
:blur => :focus,
:focus => :focus,
:focusin => :focus,
:focusout => :focus,
:afterprint => :event,
:beforeprint => :event,
:canplay => :event,
:canplaythrough => :event,
:change => :event,
:fullscreenchange => :event,
:fullscreenerror => :event,
:invalid => :event,
:loadeddata => :event,
:loadedmetadata => :event,
:message => :event,
:offline => :event,
:online => :event,
:open => :event,
:close => :event,
:pause => :event,
:play => :event,
:playing => :event,
:progress => :event,
:changerate => :event,
:reset => :event,
:search => :event,
:seek => :event,
:seeking => :event,
:show => :event,
:hide => :event,
:stalled => :event,
:submit => :event,
:suspend => :event,
:timeupdate => :event,
:toggle => :event,
:waiting => :event,
:copy => :clipboard,
:cut => :clipboard,
:paste => :clipboard,
:pagehide => :transition,
:pageshow => :transition,
:popstate => :history,
:loadstart => :progress,
:storage => :storage,
:credentialchanged => :ui
}
#TODO: look into fully supporting touch screen gleaning functions.
EVENT_FIELDS = {
:mouse => [:altKey, :button, :buttons, :clientX, :clientY, :ctrlKey, :metaKey, :movementX, :movementY, :offsetX, :offsetY, :pageX, :pageY, :screenX, :screenY, :shiftKey, :which, :detail, :view, :bubbles, :cancelable, :currentTarget, :defaultPrevented, :eventPhase, :isTrusted, :target, :timeStamp],
:keyboard => [:altKey, :charCode, :code, :ctrlKey, :metaKey, :isComposing, :key, :keyCode, :location, :shift, :repeat, :shiftKey, :which, :detail, :view, :bubbles, :cancelable, :currentTarget, :defaultPrevented, :eventPhase, :isTrusted, :target, :timeStamp],
:touch => [:altKey, :ctrlKey, :metaKey, :shiftKey, :detail, :view, :bubbles, :cancelable, :currentTarget, :defaultPrevented, :eventPhase, :isTrusted, :target, :timeStamp],
:animation => [:animationName, :elapsedTime, :pseudoElement, :bubbles, :cancelable, :currentTarget, :defaultPrevented, :eventPhase, :isTrusted, :target, :timeStamp],
:input => [:data, :inputType, :isComposing, :detail, :view, :bubbles, :cancelable, :currentTarget, :defaultPrevented, :eventPhase, :isTrusted, :target, :timeStamp],
:drag => [:dataTranser, :altKey, :button, :buttons, :clientX, :clientY, :ctrlKey, :metaKey, :movementX, :movementY, :offsetX, :offsetY, :pageX, :pageY, :screenX, :screenY, :shiftKey, :which, :bubbles, :cancelable, :currentTarget, :defaultPrevented, :eventPhase, :isTrusted, :target, :timeStamp],
:wheel => [:deltaX, :deltaY, :deltaZ, :deltaMode, :altKey, :button, :buttons, :clientX, :clientY, :ctrlKey, :metaKey, :movementX, :movementY, :offsetX, :offsetY, :pageX, :pageY, :screenX, :screenY, :shiftKey, :which, :detail, :view, :bubbles, :cancelable, :currentTarget, :defaultPrevented, :eventPhase, :isTrusted, :target, :timeStamp],
:ui => [:detail, :view,:bubbles, :cancelable, :currentTarget, :defaultPrevented, :eventPhase, :isTrusted, :target, :timeStamp],
:focus => [:relatedTarget, :detail, :view, :bubbles, :cancelable, :currentTarget, :defaultPrevented, :eventPhase, :isTrusted, :target, :timeStamp],
:event => [:bubbles, :cancelable, :currentTarget, :defaultPrevented, :eventPhase, :isTrusted, :target, :timeStamp],
:transition => [:persisted, :bubbles, :cancelable, :currentTarget, :defaultPrevented, :eventPhase, :isTrusted, :target, :timeStamp],
:history => [:state, :bubbles, :cancelable, :currentTarget, :defaultPrevented, :eventPhase, :isTrusted, :target, :timeStamp],
:progress => [:lengthComputable, :loaded, :total, :bubbles, :cancelable, :currentTarget, :defaultPrevented, :eventPhase, :isTrusted, :target, :timeStamp],
:storage => [:key, :newValue, :oldValue, :storageArea, :url, :bubbles, :cancelable, :currentTarget, :defaultPrevented, :eventPhase, :isTrusted, :target, :timeStamp],
:clipboard => [:clipboardData, :bubbles, :cancelable, :currentTarget, :defaultPrevented, :eventPhase, :isTrusted, :target, :timeStamp]
}
# Internal method on
def on(the_event_type=nil, captured=false, &block)
unless @event_handlers.is_a?(::Hash)
@event_handlers = {}
@event_listeners = {}
end
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 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
#
instance_eval(the_script_body)
result = true
rescue Exception => the_error
log_error({:error => the_error, :parameters => {:script => the_script_body}})
end
end
result
end
#
end
# --------------------------------------------------------------------------------------------------------------------------------------
# Animation Support:
class Animation
#
def initialize(details={}, the_uuid=nil)
@event_handlers = {}
if details.is_any?(::Hash, ::GxG::Database::PersistedHash)
if details[:origin]
if details[:origin].is_a?(::GxG::Database::PersistedHash)
@origin = details[:origin].unpersist
else
@origin = details[:origin]
end
else
@origin = nil
end
#
unless details[:destination]
raise ArgumentError, "You MUST provide a :destination state for the object."
end
#
if details[:destination].is_a?(::GxG::Database::PersistedHash)
@destination = details[:destination].unpersist
else
@destination = details[:destination]
end
#
if details[:options]
@options = {}
if details[:options].is_a?(::GxG::Database::PersistedHash)
the_options = details[:options].unpersist
else
the_options = details[:options]
end
callback_map = {:animationstart => :start, :animationend => :complete, :animationiteration => :update, :animationstop => :stop, :animationpause => :pause, :animationresume => :resume}
the_options.each_pair do |the_key, the_value|
if callback_map.keys.include?(the_key)
if the_value.respond_to?(:call)
# Expansion: compile formatted hashes of script records ??
@event_handlers[(the_key)] = the_value
@options[(the_key)] = the_value
else
log_warn("Attempted to pass an invalid object as an event handler. #{the_key.inspect} --> #{the_value.inspect}")
end
else
@options[(the_key)] = the_value
end
end
else
@options = nil
end
#
unless details[:targets]
raise ArgumentError, "You MUST provide a one or more :targets."
end
#
if details[:targets].is_a?(::GxG::Database::PersistedHash)
@target_list = details[:targets].unpersist
else
@target_list = details[:targets]
end
end
@uuid = (the_uuid || ::GxG::uuid_generate).to_s.to_sym
@targets = nil
@tweens = []
#
self
end
#
def update_targets(data=nil)
result = false
if data.is_any?(::Hash, ::GxG::Database::PersistedHash)
if data.is_a?(::GxG::Database::PersistedHash)
@target_list = data.unpersist
else
@target_list = data
end
result = true
end
result
end
#
def update_origin(data=nil)
result = false
if data.is_any?(::Hash, ::GxG::Database::PersistedHash)
if data.is_a?(::GxG::Database::PersistedHash)
@origin = data.unpersist
else
@origin = data
end
result = true
end
result
end
#
def update_destination(data=nil)
result = false
if data.is_any?(::Hash, ::GxG::Database::PersistedHash)
if data.is_a?(::GxG::Database::PersistedHash)
@destination = data.unpersist
else
@destination = data
end
result = true
end
result
end
#
def update_options(data=nil)
result = false
if data.is_any?(::Hash, ::GxG::Database::PersistedHash)
if data.is_a?(::GxG::Database::PersistedHash)
the_options = data.unpersist
else
the_options = data
end
@options = {}
callback_map = {:animationstart => :start, :animationend => :complete, :animationiteration => :update, :animationstop => :stop, :animationpause => :pause, :animationresume => :resume}
the_options.each_pair do |the_key, the_value|
if callback_map.keys.include?(the_key)
if the_value.respond_to?(:call)
# Expansion: compile formatted hashes of script records ??
@event_handlers[(the_key)] = the_value
@options[(the_key)] = the_value
else
log_warn("Attempted to pass an invalid object as an event handler. #{the_key.inspect} --> #{the_value.inspect}")
end
else
@options[(the_key)] = the_value
end
end
result = true
end
result
end
#
def uuid()
@uuid
end
#
def on(the_event_type=nil, captured=false,&block)
unless @event_handlers.is_a?(::Hash)
@event_handlers = {}
end
if the_event_type.is_any?(::String, ::Symbol)
if block.respond_to?(:call)
@event_handlers[(the_event_type.to_s.downcase.to_sym)] = block
end
end
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
# See: https://stackoverflow.com/questions/11547672/how-to-stringify-event-object
%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 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
#
instance_eval(the_script_body)
result = true
rescue Exception => the_error
log_error({:error => the_error, :parameters => {:script => the_script_body}})
end
end
result
end
#
def compile()
result = false
begin
@targets = []
native_targets = `[]`
@target_list.keys.each do |the_method|
if the_method == :class
unless @target_list[:class].is_a?(::String)
raise Exception, "You MUST provide :class target as a String, not: #{@target_list[:class].class.inspect}"
end
@target_list[:class].split(" ").each do |the_classname|
if the_classname.size > 0
found_list = `document.getElementsByClassName(#{the_classname})`
found_length = `#{found_list}.length`.to_i
if found_length > 0
(0..(found_length - 1)).each do |the_index|
the_element = `#{found_list}.item(#{the_index})`
unless @targets.include?(the_element)
@targets << the_element
end
end
end
end
end
end
#
if the_method == :objects
# provide a list of UUIDs of components to include as targets
unless @target_list[:objects].is_a?(::Array)
raise Exception, "You MUST provide :object target list as an Array, not: #{@target_list[:objects].class.inspect}"
end
@target_list[:objects].each do |the_reference|
found_object = GxG::DISPLAY_DETAILS[:object].find_object(the_reference.to_s.to_sym)
if found_object
element_id = `#{found_object.element}.attributes.getNamedItem('id') || 0`
unless `#{element_id} == 0`
found_element = `document.getElementById(#{element_id})`
if found_element
unless @targets.include?(found_element)
@targets << found_element
end
end
end
end
end
end
if the_method == :elements
# provide a list of native elements to include as targets
unless @target_list[:elements].is_a?(::Array)
raise Exception, "You MUST provide :elements target list as an Array, not: #{@target_list[:elements].class.inspect}"
end
@target_list[:elements].each do |the_element|
element_id = `#{the_element}.attributes.getNamedItem('id') || 0`
unless `#{element_id} == 0`
element_id = `#{element_id}.value`
found_element = `document.getElementById(#{element_id})`
if found_element
unless @targets.include?(found_element)
@targets << found_element
end
end
end
end
end
if the_method == :assets
# Future expansion
end
end
# DEBUG:
unless @targets.size > 0
puts "No valid targets resulted."
end
@targets.each do |the_element|
`#{native_targets}.push(#{the_element})`
end
#
if @origin.is_a?(::Hash)
if @origin.size > 0
from_state = {}
@origin.each_pair do |the_key, the_value|
# key translations:
if the_key == :attribue || the_key == "attribute"
new_key = :attr
else
new_key = the_key
end
#
from_state[(new_key)] = the_value
end
else
from_state = nil
end
else
from_state = nil
end
#
if @destination.size > 0
to_state = {}
@destination.each_pair do |the_key, the_value|
# key translations:
if the_key == :attribue || the_key == "attribute"
new_key = :attr
else
new_key = the_key
end
#
to_state[(new_key)] = the_value
end
else
raise Exception, "You MUST define a valid :destination state. not: #{@destination.inspect}"
end
#
if @options.is_a?(::Hash)
if @options.size > 0
options = {}
callback_map = {:animationstart => :start, :animationend => :complete, :animationiteration => :update, :animationstop => :stop, :animationpause => :pause, :animationresume => :resume}
@options.each_pair do |the_key, the_value|
# Key translations:
if callback_map.keys.include?(the_key)
# include handlers
new_key = callback_map[(the_key)]
#
if @event_handlers[(the_key)].respond_to?(:call)
# FIXME
# puts "Setting callback for: #{new_key.inspect}"
options[(new_key)] = @event_handlers[(the_key)]
end
else
unless [:start_time].include?(the_key)
# TODO: research ALL options and only allow valid options to be set.
options[(the_key)] = the_value
end
end
end
else
options = nil
end
else
options = nil
end
#
@tweens = []
# FIXME:
# puts "Compiling Options: #{options.inspect}"
@targets.each do |the_target|
if from_state
if options
@tweens << `KUTE.fromTo(#{the_target}, #{from_state.to_n}, #{to_state.to_n}, #{options.to_n})`
else
@tweens << `KUTE.fromTo(#{the_target}, #{from_state.to_n}, #{to_state.to_n})`
end
else
if options
@tweens << `KUTE.to(#{the_target},#{to_state.to_n}, #{options.to_n})`
else
@tweens << `KUTE.to(#{the_target},#{to_state.to_n})`
end
end
end
#
result = true
rescue Exception => the_error
log_error({:error => the_error})
@targets = nil
@tweens = []
end
result
end
#
def start(the_time=nil)
unless @tweens.size > 0
self.compile
end
unless the_time.is_a?(::Numeric)
the_time = `window.performance.now()`
end
if @tweens.size > 0
@tweens.each do |the_tween|
`#{the_tween}.start(#{the_time})`
end
end
end
#
def stop()
if @tweens.size > 0
@tweens.each do |the_tween|
`#{the_tween}.stop()`
end
end
end
#
def pause()
if @tweens.size > 0
@tweens.each do |the_tween|
`#{the_tween}.pause()`
end
end
end
#
def resume()
if @tweens.size > 0
@tweens.each do |the_tween|
`#{the_tween}.resume()`
end
end
end
#
def included()
# return a list of all page components included in this animation
result = []
if @targets
@targets.each do |the_element|
element_id = `#{the_element}.attributes.getNamedItem('id') || 0`
unless `#{element_id} == 0`
element_id = `#{element_id}.value`
found_object = GxG::DISPLAY_DETAILS[:object].find_object_by_id(element_id.to_s)
if found_object
result << found_object
end
end
end
end
result
end
#
def each_included(&block)
if block.respond_to?(:call)
self.included.each do |the_object|
block.call(the_object)
end
else
self.included.each
end
end
# send events to all target components (not their elements)
def broadcast(the_method=nil, the_parameters=nil)
self.each_included do |the_object|
the_object.send(the_method, the_parameters)
end
end
#
end
#
module AnimationSupport
#
def animate(animation_parameters=nil)
if animation_parameters[:destination].is_any?(::Hash, ::GxG::Database::PersistedHash)
the_animation = ::GxG::Gui::Animation.new({:targets => {:elements => [(self.element)]}, :origin => animation_parameters[:origin], :destination => animation_parameters[:destination], :options => animation_parameters[:options]})
start_time = `window.performance.now()`
if animation_parameters[:options].is_any?(::Hash, ::GxG::Database::PersistedHash)
start_time = animation_parameters[:options][:start_time]
end
# FIXME:
# GxG::ANIMATION[:animations][(the_animation.uuid)] = the_animation
#
the_animation.compile
the_animation.start(start_time)
true
else
log_warn("Ignoring invalid animation: NO valid :destination state specified (required). not: #{animation_parameters[:destination].inspect}")
false
end
end
# ------------------------------------------------------------------------------
def show(transition_parameters=nil,&block)
if transition_parameters.is_any?(::Hash, ::GxG::Database::PersistedHash)
if transition_parameters.is_a?(::GxG::Database::PersistedHash)
transition_parameters = transition_parameters.unpersist
end
#
if transition_parameters[:destination].is_a?(::Hash)
if transition_parameters[:destination][:opacity].is_a?(::Numeric)
if transition_parameters[:origin].is_a?(::Hash)
if transition_parameters[:origin][:opacity].is_a?(::Numeric)
self.gxg_merge_style({:opacity => transition_parameters[:origin][:opacity]})
else
self.gxg_merge_style({:opacity => 0})
end
else
self.gxg_merge_style({:opacity => 0})
end
end
end
self.gxg_set_state(:hidden, false)
if block.respond_to?(:call)
unless transition_parameters[:options]
transition_parameters[:options] = {}
end
transition_parameters[:options][:animationend] = Proc.new do
block.call()
end
end
#
self.animate(transition_parameters)
else
self.gxg_set_state(:hidden, false)
if block.respond_to?(:call)
block.call()
end
end
# GxG::DISPLAY_DETAILS[:object].layout_refresh
true
end
#
def hide(transition_parameters=nil,&block)
if transition_parameters.is_any?(::Hash, ::GxG::Database::PersistedHash)
if transition_parameters.is_a?(::GxG::Database::PersistedHash)
transition_parameters = transition_parameters.unpersist
end
if block.respond_to?(:call)
unless transition_parameters[:options]
transition_parameters[:options] = {}
end
transition_parameters[:options][:animationend] = Proc.new do
self.gxg_set_state(:hidden, true)
block.call()
end
else
set_state = nil
if transition_parameters[:options]
if transition_parameters[:options][:animationend].respond_to?(:call)
old_complete = transition_parameters[:options][:animationend]
set_state = Proc.new do
old_complete.call()
self.gxg_set_state(:hidden, true)
end
else
set_state = Proc.new do
self.gxg_set_state(:hidden, true)
end
end
else
transition_parameters[:options] = {}
set_state = Proc.new do
self.gxg_set_state(:hidden, true)
end
end
if set_state
unless transition_parameters[:options].is_a?(::Hash)
transition_parameters[:options] = {}
end
transition_parameters[:options][:animationend] = set_state
end
end
#
self.animate(transition_parameters)
else
self.gxg_set_state(:hidden, true)
if block.respond_to?(:call)
block.call()
end
end
# GxG::DISPLAY_DETAILS[:object].layout_refresh
true
end
#
end
# --------------------------------------------------------------------------------------------------------------------------------------
module ApplicationSupport
#
def find_parent_type(the_type=nil)
# return the first parent of type: the_type (Class, String, Symbol)
result = nil
if the_type.is_any?(::Class, ::String, ::Symbol)
if the_type.is_any?(::String, ::Symbol)
the_type = GxG::Gui::component_class(the_type.to_s.to_sym)
end
if the_type.is_a?(::Class)
search_queue = [(self)]
while search_queue.size > 0 do
entry = search_queue.shift
if entry
if entry.is_a?(the_type)
result = entry
break
end
search_queue << entry.parent
end
end
end
end
result
end
# deprecated - switch over to find_parent_type (eventually)
def viewport()
# find nearest viewport in object heirarchy.
self.find_parent_type(::GxG::Gui::ApplicationViewport)
end
#
def tree_node()
# find nearest :tree_node parent in object heirarchy.
self.find_parent_type(::GxG::Gui::TreeNode)
end
#
def tree()
# find nearest :tree parent in object heirarchy.
self.find_parent_type(::GxG::Gui::Tree)
end
#
def window()
# find nearest window in object heirarchy.
self.find_parent_type(::GxG::Gui::Window)
end
#
def find_parent(the_reference=nil)
result = nil
if GxG::valid_uuid?(the_reference)
uuid_check = true
else
uuid_check = false
end
search_queue = [(self.parent)]
while search_queue.size > 0 do
entry = search_queue.shift
if entry
if uuid_check
if entry.uuid == the_reference
result = entry
break
end
else
if entry.title == the_reference
result = entry
break
end
end
unless result
search_queue << entry.parent
end
end
end
result
end
#
def find_child_type(the_type=nil)
# return the first parent of type: the_type (Class, String, Symbol)
result = nil
if the_type.is_any?(::Class, ::String, ::Symbol)
if the_type.is_any?(::String, ::Symbol)
the_type = GxG::Gui::component_class(the_type.to_s.to_sym)
end
if the_type.is_a?(::Class)
search_queue = self.children.values
while search_queue.size > 0 do
entry = search_queue.shift
if entry
if entry.is_a?(the_type)
result = entry
break
end
end
end
end
end
result
end
#
def find_child(the_reference=nil)
result = nil
if GxG::valid_uuid?(the_reference)
uuid_check = true
else
uuid_check = false
end
search_queue = self.children.values
while search_queue.size > 0 do
entry = search_queue.shift
if entry
if uuid_check
if entry.uuid == the_reference
result = entry
break
end
else
if entry.title == the_reference
result = entry
break
end
end
unless result
entry.children.values.each do |the_child|
search_queue << the_child
end
end
end
end
result
end
#
def build_interior_components(build_list=[])
# You get ONE @content 'internal_component' (for now).
build_queue = [{:parent => nil, :record => {:content => build_list}, :element => self}]
while build_queue.size > 0 do
entry = build_queue.shift
if entry
if entry[:record][:content].size > 0
entry[:record][:content].each do |sub_component|
element_class = GxG::Gui::component_class(sub_component[:component].to_s.to_sym)
if element_class
if sub_component.is_a?(::GxG::Database::PersistedHash)
the_options = sub_component[:options].unpersist()
the_uuid = sub_component.uuid.to_s.to_sym
if the_options[:title]
the_title = the_options.delete(:title)
else
the_title = sub_component.title.to_s
end
else
the_options = sub_component[:options].clone
the_uuid = ::GxG::uuid_generate.to_sym
if the_options[:title]
the_title = the_options.delete(:title)
else
the_title = "Untitled Component #{the_uuid.to_s}"
end
end
#
the_style = the_options.delete(:style)
the_states = (the_options.delete(:states) || {})
the_options[:uuid] = the_uuid
if the_options[:track]
tracking = the_options.delete(:track)
else
tracking = nil
end
if entry[:element] == self
if entry[:element].respond_to?(:original_add_child)
new_entry = {:parent => entry[:element], :record => sub_component, :element => entry[:element].original_add_child((the_uuid), element_class, the_options)}
else
new_entry = {:parent => entry[:element], :record => sub_component, :element => entry[:element].add_child((the_uuid), element_class, the_options)}
end
else
new_entry = {:parent => entry[:element], :record => sub_component, :element => entry[:element].add_child((the_uuid), element_class, the_options)}
end
if the_title == "interior_component"
@content = new_entry[:element]
else
new_entry[:element].set_title(the_title)
end
# Window component tracking:
if tracking
the_window = new_entry[:element].window()
if the_window
the_window.track_deltas(the_uuid,new_entry[:element],tracking)
end
end
# Set states:
unless the_states.keys.include?(:hidden)
the_states[:hidden] = false
end
new_entry[:element].gxg_set_states(the_states)
# process style info:
if the_style.is_a?(::Hash)
new_entry[:element].gxg_set_style(the_style)
end
#
# Set Script if any is provided
if sub_component[:script].size > 0
new_entry[:element].set_script(sub_component[:script].to_s)
end
build_queue << new_entry
end
end
end
end
end
#
end
#
end
# --------------------------------------------------------------------------------------------------------------------------------------
module ThemeSupport
def page()
GxG::DISPLAY_DETAILS[:object]
end
def theme_icon(image_name=nil)
GxG::DISPLAY_DETAILS[:object].theme_icon(image_name)
end
#
def theme_background(image_name=nil)
GxG::DISPLAY_DETAILS[:object].theme_background(image_name)
end
#
def theme_widget(image_name=nil)
GxG::DISPLAY_DETAILS[:object].theme_widget(image_name)
end
#
def theme_font(font_name=nil)
GxG::DISPLAY_DETAILS[:object].theme_font(font_name)
end
# Asset retrieval support:
def site_asset(asset_path=nil)
GxG::DISPLAY_DETAILS[:object].site_asset(asset_path)
end
end
# --------------------------------------------------------------------------------------------------------------------------------------
# Page objects and components:
# ### Components
# Universal to all components and elements:
#
# Override this method to return a Hash of styles.
# Hash-key is the CSS style name, hash-value is the CSS style value.
# def style;end
class Component < ::Ferro::Component::Base
#
def _before_create
if @options[:uuid]
@uuid = @options.delete(:uuid).to_s.to_sym
end
super()
end
#
def uuid()
@uuid
end
#
#
include GxG::Gui::EventHandlers
include GxG::Gui::AnimationSupport
include GxG::Gui::ApplicationSupport
include GxG::Gui::ThemeSupport
#
end
#
class Header < ::Ferro::Component::Header
#
def _before_create
if @options[:uuid]
@uuid = @options.delete(:uuid).to_s.to_sym
end
super()
end
#
def uuid()
@uuid
end
#
#
include GxG::Gui::EventHandlers
include GxG::Gui::AnimationSupport
include GxG::Gui::ApplicationSupport
include GxG::Gui::ThemeSupport
#
end
#
class Navigator < ::Ferro::Component::Navigation
#
def _before_create
if @options[:uuid]
@uuid = @options.delete(:uuid).to_s.to_sym
end
super()
end
#
def uuid()
@uuid
end
#
#
include GxG::Gui::EventHandlers
include GxG::Gui::AnimationSupport
include GxG::Gui::ApplicationSupport
include GxG::Gui::ThemeSupport
#
end
#
class Section < ::Ferro::Component::Section
#
def _before_create
if @options[:uuid]
@uuid = @options.delete(:uuid).to_s.to_sym
end
super()
end
#
def uuid()
@uuid
end
#
#
include GxG::Gui::EventHandlers
include GxG::Gui::AnimationSupport
include GxG::Gui::ApplicationSupport
include GxG::Gui::ThemeSupport
#
end
#
class Article < ::Ferro::Component::Article
#
def _before_create
if @options[:uuid]
@uuid = @options.delete(:uuid).to_s.to_sym
end
super()
end
#
def uuid()
@uuid
end
#
#
include GxG::Gui::EventHandlers
include GxG::Gui::AnimationSupport
include GxG::Gui::ApplicationSupport
include GxG::Gui::ThemeSupport
#
end
#
class Aside < ::Ferro::Component::Aside
#
def _before_create
if @options[:uuid]
@uuid = @options.delete(:uuid).to_s.to_sym
end
super()
end
#
def uuid()
@uuid
end
#
#
include GxG::Gui::EventHandlers
include GxG::Gui::AnimationSupport
include GxG::Gui::ApplicationSupport
include GxG::Gui::ThemeSupport
#
end
#
class Footer < ::Ferro::Component::Footer
#
def _before_create
if @options[:uuid]
@uuid = @options.delete(:uuid).to_s.to_sym
end
super()
end
#
def uuid()
@uuid
end
#
#
include GxG::Gui::EventHandlers
include GxG::Gui::AnimationSupport
include GxG::Gui::ApplicationSupport
include GxG::Gui::ThemeSupport
#
end
# ### Forms
#
class Form < ::Ferro::Form::Base
#
def _before_create
if @options[:uuid]
@uuid = @options.delete(:uuid).to_s.to_sym
end
super()
end
#
def uuid()
@uuid
end
#
#
include GxG::Gui::EventHandlers
include GxG::Gui::AnimationSupport
include GxG::Gui::ApplicationSupport
include GxG::Gui::ThemeSupport
#
def form_data()
result = {}
search_queue = self.children.values
while search_queue.size > 0 do
entry = search_queue.shift
if entry
if entry.domtype == :input
result[(entry.title.to_s.to_sym)] = entry.value()
else
entry.children.values.each do |the_child|
search_queue << the_child
end
end
end
end
result
end
end
#
class Clickable < ::Ferro::Form::Clickable
#
def _before_create
if @options[:uuid]
@uuid = @options.delete(:uuid).to_s.to_sym
end
super()
end
#
def uuid()
@uuid
end
#
## Override this method to define what happens after
# element has been clicked.
# def clicked;end
include GxG::Gui::AnimationSupport
include GxG::Gui::ApplicationSupport
include GxG::Gui::ThemeSupport
end
#
class Fieldset < ::Ferro::Form::Fieldset
#
def _before_create
if @options[:uuid]
@uuid = @options.delete(:uuid).to_s.to_sym
end
super()
end
#
def uuid()
@uuid
end
#
#
include GxG::Gui::EventHandlers
include GxG::Gui::AnimationSupport
include GxG::Gui::ApplicationSupport
include GxG::Gui::ThemeSupport
#
end
#
class Input < ::Ferro::Form::Input
#
def _before_create
super()
@domtype = :input
if @options[:uuid]
@uuid = @options.delete(:uuid).to_s.to_sym
end
end
#
def uuid()
@uuid
end
#
# Creates an input element.
# In the DOM creates a: <input type="?">.
# Specify option :type to set the location.
# Use one of these values:
# :text, :password, :reset, :radio, :checkbox, :color,
# :date, :datetime-local, :email, :month, :number,
# :range, :search, :tel, :time, :url, :week.
# Or leave blank to create a :text input.
# def _before_create
# @options[:type] = :text
# super
# end
include GxG::Gui::AnimationSupport
include GxG::Gui::ApplicationSupport
include GxG::Gui::ThemeSupport
end
#
class TextInput < ::GxG::Gui::Input
#
def _before_create
super()
@domtype = :input
if @options[:uuid]
@uuid = @options.delete(:uuid).to_s.to_sym
end
@options[:type] = :text
end
#
include GxG::Gui::EventHandlers
include GxG::Gui::AnimationSupport
include GxG::Gui::ApplicationSupport
#
end
#
class PasswordInput < ::GxG::Gui::Input
#
def _before_create
super()
@domtype = :input
if @options[:uuid]
@uuid = @options.delete(:uuid).to_s.to_sym
end
@options[:type] = :password
end
#
include GxG::Gui::EventHandlers
include GxG::Gui::AnimationSupport
include GxG::Gui::ApplicationSupport
#
end
#
class ResetInput < ::GxG::Gui::Input
#
def _before_create
super()
@domtype = :input
if @options[:uuid]
@uuid = @options.delete(:uuid).to_s.to_sym
end
@options[:type] = :reset
end
#
include GxG::Gui::EventHandlers
include GxG::Gui::AnimationSupport
include GxG::Gui::ApplicationSupport
#
end
#
class RadioButton < ::GxG::Gui::Input
#
def _before_create
super()
@domtype = :input
if @options[:uuid]
@uuid = @options.delete(:uuid).to_s.to_sym
end
@options[:type] = :radio
end
#
def uuid()
@uuid
end
#
include GxG::Gui::EventHandlers
include GxG::Gui::AnimationSupport
include GxG::Gui::ApplicationSupport
#
end
#
class ColorPicker < ::GxG::Gui::Input
#
def _before_create
super()
@domtype = :input
if @options[:uuid]
@uuid = @options.delete(:uuid).to_s.to_sym
end
@options[:type] = :color
end
#
def uuid()
@uuid
end
#
include GxG::Gui::EventHandlers
include GxG::Gui::AnimationSupport
include GxG::Gui::ApplicationSupport
#
end
#
class DatePicker < ::GxG::Gui::Input
#
def _before_create
super()
@domtype = :input
if @options[:uuid]
@uuid = @options.delete(:uuid).to_s.to_sym
end
@options[:type] = :date
end
#
def uuid()
@uuid
end
#
include GxG::Gui::EventHandlers
include GxG::Gui::AnimationSupport
include GxG::Gui::ApplicationSupport
#
end
#
class DateTimeLocal < ::GxG::Gui::Input
#
def _before_create
super()
@domtype = :input
if @options[:uuid]
@uuid = @options.delete(:uuid).to_s.to_sym
end
@options[:type] = :"datetime-local"
end
#
def uuid()
@uuid
end
#
include GxG::Gui::EventHandlers
include GxG::Gui::AnimationSupport
include GxG::Gui::ApplicationSupport
#
end
#
class EmailInput < ::GxG::Gui::Input
#
def _before_create
super()
@domtype = :input
if @options[:uuid]
@uuid = @options.delete(:uuid).to_s.to_sym
end
@options[:type] = :email
end
#
def uuid()
@uuid
end
#
include GxG::Gui::EventHandlers
include GxG::Gui::AnimationSupport
include GxG::Gui::ApplicationSupport
#
end
#
class MonthPicker < ::GxG::Gui::Input
#
def _before_create
super()
@domtype = :input
if @options[:uuid]
@uuid = @options.delete(:uuid).to_s.to_sym
end
@options[:type] = :month
end
#
def uuid()
@uuid
end
#
include GxG::Gui::EventHandlers
include GxG::Gui::AnimationSupport
include GxG::Gui::ApplicationSupport
#
end
#
class NumberInput < ::GxG::Gui::Input
#
def _before_create
super()
@domtype = :input
if @options[:uuid]
@uuid = @options.delete(:uuid).to_s.to_sym
end
@options[:type] = :number
end
#
def uuid()
@uuid
end
#
include GxG::Gui::EventHandlers
include GxG::Gui::AnimationSupport
include GxG::Gui::ApplicationSupport
#
end
#
class RangeInput < ::GxG::Gui::Input
#
def _before_create
super()
@domtype = :input
if @options[:uuid]
@uuid = @options.delete(:uuid).to_s.to_sym
end
@options[:type] = :range
end
#
def uuid()
@uuid
end
#
include GxG::Gui::EventHandlers
include GxG::Gui::AnimationSupport
include GxG::Gui::ApplicationSupport
#
end
#
class SearchInput < ::GxG::Gui::Input
#
def _before_create
super()
@domtype = :input
if @options[:uuid]
@uuid = @options.delete(:uuid).to_s.to_sym
end
@options[:type] = :search
end
#
def uuid()
@uuid
end
#
include GxG::Gui::EventHandlers
include GxG::Gui::AnimationSupport
include GxG::Gui::ApplicationSupport
#
end
#
class PhoneInput < ::GxG::Gui::Input
#
def _before_create
super()
@domtype = :input
if @options[:uuid]
@uuid = @options.delete(:uuid).to_s.to_sym
end
@options[:type] = :tel
end
#
def uuid()
@uuid
end
#
include GxG::Gui::EventHandlers
include GxG::Gui::AnimationSupport
include GxG::Gui::ApplicationSupport
#
end
#
class TimePicker < ::GxG::Gui::Input
#
def _before_create
super()
@domtype = :input
if @options[:uuid]
@uuid = @options.delete(:uuid).to_s.to_sym
end
@options[:type] = :time
end
#
def uuid()
@uuid
end
#
include GxG::Gui::EventHandlers
include GxG::Gui::AnimationSupport
include GxG::Gui::ApplicationSupport
#
end
#
class UrlInput < ::GxG::Gui::Input
#
def _before_create
super()
@domtype = :input
if @options[:uuid]
@uuid = @options.delete(:uuid).to_s.to_sym
end
@options[:type] = :url
end
#
def uuid()
@uuid
end
#
#
include GxG::Gui::EventHandlers
include GxG::Gui::AnimationSupport
include GxG::Gui::ApplicationSupport
#
end
#
class WeekPicker < ::GxG::Gui::Input
#
def _before_create
super()
@domtype = :input
if @options[:uuid]
@uuid = @options.delete(:uuid).to_s.to_sym
end
@options[:type] = :week
end
#
def uuid()
@uuid
end
#
include GxG::Gui::EventHandlers
include GxG::Gui::AnimationSupport
include GxG::Gui::ApplicationSupport
#
end
#
class Label < ::Ferro::Form::Label
#
def _before_create
if @options[:uuid]
@uuid = @options.delete(:uuid).to_s.to_sym
end
super()
end
#
def uuid()
@uuid
end
#
#
include GxG::Gui::EventHandlers
include GxG::Gui::AnimationSupport
include GxG::Gui::ApplicationSupport
include GxG::Gui::ThemeSupport
#
end
#
class TextArea < ::Ferro::Form::Textarea
#
def _before_create
if @options[:uuid]
@uuid = @options.delete(:uuid).to_s.to_sym
end
super()
end
#
def uuid()
@uuid
end
#
#
include GxG::Gui::EventHandlers
include GxG::Gui::AnimationSupport
include GxG::Gui::ApplicationSupport
include GxG::Gui::ThemeSupport
#
end
#
class Output < ::Ferro::Form::Output
#
def _before_create
if @options[:uuid]
@uuid = @options.delete(:uuid).to_s.to_sym
end
super()
end
#
def uuid()
@uuid
end
#
#
include GxG::Gui::EventHandlers
include GxG::Gui::AnimationSupport
include GxG::Gui::ApplicationSupport
include GxG::Gui::ThemeSupport
#
end
#
class ButtonInput < ::Ferro::Form::Button
#
def disable()
@enabled = false
self.gxg_set_attribute(:disabled)
end
#
def enable()
@enabled = true
self.gxg_clear_attribute(:disabled)
end
#
def enabled?()
@enabled || false
end
#
def _before_create
if @options[:uuid]
@uuid = @options.delete(:uuid).to_s.to_sym
end
@enabled = @options.delete(:enabled)
unless @enabled == false
@enabled = true
end
super()
end
#
def _after_create
if @enabled == true
self.enable
else
self.disable
end
end
#
def uuid()
@uuid
end
#
#
include GxG::Gui::EventHandlers
include GxG::Gui::AnimationSupport
include GxG::Gui::ApplicationSupport
include GxG::Gui::ThemeSupport
#
end
#
class SubmitButton < ::Ferro::Form::Submit
#
def disable()
@enabled = false
self.gxg_set_attribute(:disabled)
end
#
def enable()
@enabled = true
self.gxg_clear_attribute(:disabled)
end
#
def enabled?()
@enabled || false
end
#
def _before_create
if @options[:uuid]
@uuid = @options.delete(:uuid).to_s.to_sym
end
@enabled = @options.delete(:enabled)
unless @enabled == false
@enabled = true
end
super()
end
#
def _after_create
if @enabled == true
self.enable
else
self.disable
end
end
#
def uuid()
@uuid
end
#
#
include GxG::Gui::EventHandlers
include GxG::Gui::AnimationSupport
include GxG::Gui::ApplicationSupport
include GxG::Gui::ThemeSupport
#
end
#
class ClickBlock < ::Ferro::Form::Block
#
def _before_create
if @options[:uuid]
@uuid = @options.delete(:uuid).to_s.to_sym
end
super()
end
#
def uuid()
@uuid
end
#
## Override this method to define what happens after
# element has been clicked.
# def clicked;end
include GxG::Gui::AnimationSupport
include GxG::Gui::ApplicationSupport
include GxG::Gui::ThemeSupport
end
#
class CheckBox < ::Ferro::Form::CheckBox
#
def _before_create
if @options[:uuid]
@uuid = @options.delete(:uuid).to_s.to_sym
end
super()
end
#
def uuid()
@uuid
end
#
#
include GxG::Gui::EventHandlers
include GxG::Gui::AnimationSupport
include GxG::Gui::ApplicationSupport
include GxG::Gui::ThemeSupport
#
def is_set?()
# sleep 0.5
if (`#{self.element}.checked == true`)
true
else
false
end
end
#
def set(the_state=false)
if the_state == true
`#{self.element}.checked = true`
else
`#{self.element}.checked = false`
end
self.is_set?()
end
#
end
#
class Selector < ::Ferro::Form::Select
#
def _before_create
if @options[:uuid]
@uuid = @options.delete(:uuid).to_s.to_sym
end
super()
end
#
def uuid()
@uuid
end
#
#
include GxG::Gui::EventHandlers
include GxG::Gui::AnimationSupport
include GxG::Gui::ApplicationSupport
include GxG::Gui::ThemeSupport
#
end
# ### Inline Elements
#
class Block < ::Ferro::Element::Block
#
def _before_create
if @options[:uuid]
@uuid = @options.delete(:uuid).to_s.to_sym
end
super()
end
#
def uuid()
@uuid
end
#
#
include GxG::Gui::EventHandlers
include GxG::Gui::AnimationSupport
include GxG::Gui::ApplicationSupport
include GxG::Gui::ThemeSupport
#
end
#
class Text < ::Ferro::Element::Text
#
def _before_create
if @options[:uuid]
@uuid = @options.delete(:uuid).to_s.to_sym
end
super()
end
#
def uuid()
@uuid
end
#
#
include GxG::Gui::EventHandlers
include GxG::Gui::AnimationSupport
include GxG::Gui::ApplicationSupport
include GxG::Gui::ThemeSupport
#
end
#
class List < ::Ferro::Element::List
#
def _before_create
if @options[:uuid]
@uuid = @options.delete(:uuid).to_s.to_sym
end
super()
end
#
def uuid()
@uuid
end
#
include GxG::Gui::EventHandlers
include GxG::Gui::AnimationSupport
include GxG::Gui::ApplicationSupport
include GxG::Gui::ThemeSupport
#
def initialize(the_name,the_class,the_options={})
#
@items = []
# Item: {:icon => "<src>", :icon_width => 32, :icon_height => 32, :label => "", :uuid => "<uuid>"}
@selection = nil
@select_responder = nil
@open_responder = nil
#
super(the_name, the_class, the_options)
self
end
#
def set_selection(uuid=nil)
@selection = uuid
self.children.values.each do |the_item|
if the_item.gxg_get_attribute('item-uuid') == uuid
the_item.select
else
the_item.unselect
end
end
uuid
end
#
def selection()
@selection
end
#
def set_select_responder(&block)
if block.respond_to?(:call)
@select_responder = block
end
end
#
def invoke_select_responder()
if @select_responder.respond_to?(:call)
@select_responder.call(@selection)
end
end
#
def set_open_responder(&block)
if block.respond_to?(:call)
@open_responder = block
end
end
#
def invoke_open_responder()
if @open_responder.respond_to?(:call)
@open_responder.call(@selection)
end
end
#
#
def add_list_item(item_details={})
build_list = []
#
if item_details[:row_style].is_a?(::Hash)
item_row = {:component=>"block_table_row", :options=>{:style => {:padding => "0px", :margin => "0px"}.merge(item_details[:row_style])}, :content=>[], :script=>""}
else
item_row = {:component=>"block_table_row", :options=>{:style => {:padding => "0px", :margin => "0px"}}, :content=>[], :script=>""}
end
if item_details[:icon]
icon_width = (item_details[:icon_width] || 32).to_i
icon_height = (item_details[:icon_height] || 32).to_i
item_icon = {:component=>"image", :options=>{:src=>(item_details[:icon]), :width=>icon_width, :height=>icon_height, :style => {:clear => "both"}}, :content=>[], :script=>""}
item_row[:content] << {:component => "block_table_cell", :options => {:style => {:padding => "0px", :margin => "0px", :"vertical-align" => "middle"}}, :content => [(item_icon)], :script => ""}
end
if item_details[:label].to_s.size > 0
item_label = {:component=>"text", :options=> {:content => (item_details[:label].to_s), :style => {:padding => "0px 0px 2px 0px", :"font-size" => "16px"}}, :content => [], :script => ""}
if item_details[:label_cell_style].is_a?(::Hash)
item_row[:content] << {:component => "block_table_cell", :options => {:style => {:padding => "0px", :margin => "0px", :"vertical-align" => "middle"}.merge(item_details[:label_cell_style])}, :content => [(item_label)], :script => ""}
else
item_row[:content] << {:component => "block_table_cell", :options => {:style => {:padding => "0px", :margin => "0px", :"vertical-align" => "middle"}}, :content => [(item_label)], :script => ""}
end
end
if item_details[:content].is_any?(::Array, ::GxG::Database::PersistedArray)
if item_details[:content].size > 0
item_details[:content].each do |the_component|
item_row[:content] << {:component => "block_table_cell", :options => {:style => {:padding => "0px", :margin => "0px"}}, :content => [(the_component)], :script => ""}
end
end
end
item_table = {:component=>"block_table", :options=>{:"item-uuid" => (item_details[:uuid].to_s), :style => {:padding => "0px", :margin => "0px"}}, :content=>[(item_row)], :script=>""}
item_table[:script] = "
def select()
@selected = true
self.gxg_merge_style({:'background-color' => '#87acd5'})
end
def unselect()
@selected = false
self.gxg_merge_style({:'background-color' => '#f2f2f2'})
end
on(:dblclick) do |event|
parent.set_selection(self.gxg_get_attribute('item-uuid'))
parent.invoke_open_responder()
end
on(:mouseup) do |event|
parent.set_selection(self.gxg_get_attribute('item-uuid'))
parent.invoke_select_responder()
end
"
#
build_list << item_table
#
@items << item_details
self.build_interior_components(build_list)
end
end
#
class OrderedList < ::Ferro::Element::List
#
def _before_create
if @options[:uuid]
@uuid = @options.delete(:uuid).to_s.to_sym
end
super()
@domtype = "ol"
end
#
def uuid()
@uuid
end
#
#
include GxG::Gui::EventHandlers
include GxG::Gui::AnimationSupport
include GxG::Gui::ApplicationSupport
include GxG::Gui::ThemeSupport
#
end
#
class ListItem < ::Ferro::Element::ListItem
#
def _before_create
if @options[:uuid]
@uuid = @options.delete(:uuid).to_s.to_sym
end
super()
end
#
def uuid()
@uuid
end
#
#
include GxG::Gui::EventHandlers
include GxG::Gui::AnimationSupport
include GxG::Gui::ApplicationSupport
include GxG::Gui::ThemeSupport
#
end
#
class Anchor < ::Ferro::Element::Anchor
#
def _before_create
if @options[:uuid]
@uuid = @options.delete(:uuid).to_s.to_sym
end
super()
end
#
def uuid()
@uuid
end
#
#
include GxG::Gui::EventHandlers
include GxG::Gui::AnimationSupport
include GxG::Gui::ApplicationSupport
include GxG::Gui::ThemeSupport
#
end
#
class ExternalLink < ::Ferro::Element::ExternalLink
#
def _before_create
if @options[:uuid]
@uuid = @options.delete(:uuid).to_s.to_sym
end
super()
end
#
def uuid()
@uuid
end
#
#
include GxG::Gui::EventHandlers
include GxG::Gui::AnimationSupport
include GxG::Gui::ApplicationSupport
include GxG::Gui::ThemeSupport
#
end
#
class Button < ::Ferro::Element::Button
# TODO: flesh out enable/disable methods for RadioButton and CheckBox as well.
def disable()
@enabled = false
self.gxg_set_attribute(:disabled)
end
#
def enable()
@enabled = true
self.gxg_clear_attribute(:disabled)
end
#
def enabled?()
@enabled || false
end
#
def _before_create
if @options[:uuid]
@uuid = @options.delete(:uuid).to_s.to_sym
end
@enabled = @options.delete(:enabled)
unless @enabled == false
@enabled = true
end
super()
end
#
def _after_create
if @enabled == true
self.enable
else
self.disable
end
end
#
def uuid()
@uuid
end
#
#
include GxG::Gui::EventHandlers
include GxG::Gui::AnimationSupport
include GxG::Gui::ApplicationSupport
include GxG::Gui::ThemeSupport
#
end
# ### Misc. Elements
#
class Image < ::Ferro::Element::Image
#
def _before_create
if @options[:uuid]
@uuid = @options.delete(:uuid).to_s.to_sym
end
super()
end
#
def uuid()
@uuid
end
#
#
include GxG::Gui::EventHandlers
include GxG::Gui::AnimationSupport
include GxG::Gui::ApplicationSupport
include GxG::Gui::ThemeSupport
#
end
#
class Video < ::Ferro::Element::Video
#
def _before_create
if @options[:uuid]
@uuid = @options.delete(:uuid).to_s.to_sym
end
super()
end
#
def uuid()
@uuid
end
#
#
include GxG::Gui::EventHandlers
include GxG::Gui::AnimationSupport
include GxG::Gui::ApplicationSupport
include GxG::Gui::ThemeSupport
#
end
#
class Canvas < ::Ferro::Element::Canvas
#
def _before_create
if @options[:uuid]
@uuid = @options.delete(:uuid).to_s.to_sym
end
super()
end
#
def uuid()
@uuid
end
#
#
include GxG::Gui::EventHandlers
include GxG::Gui::AnimationSupport
include GxG::Gui::ApplicationSupport
include GxG::Gui::ThemeSupport
#
end
#
class Table < ::Ferro::Component::Base
#
def _before_create
@domtype = :table
if @options[:uuid]
@uuid = @options.delete(:uuid).to_s.to_sym
end
super()
end
#
def uuid()
@uuid
end
#
include GxG::Gui::EventHandlers
include GxG::Gui::AnimationSupport
include GxG::Gui::ApplicationSupport
include GxG::Gui::ThemeSupport
#
end
#
class TableHeader < ::Ferro::Component::Base
def _before_create
@domtype = :th
if @options[:uuid]
@uuid = @options.delete(:uuid).to_s.to_sym
end
super()
end
#
def uuid()
@uuid
end
#
#
include GxG::Gui::EventHandlers
include GxG::Gui::AnimationSupport
include GxG::Gui::ApplicationSupport
include GxG::Gui::ThemeSupport
#
end
#
class TableRow < ::Ferro::Component::Base
def _before_create
@domtype = :tr
if @options[:uuid]
@uuid = @options.delete(:uuid).to_s.to_sym
end
super()
end
#
def uuid()
@uuid
end
#
#
include GxG::Gui::EventHandlers
include GxG::Gui::AnimationSupport
include GxG::Gui::ApplicationSupport
include GxG::Gui::ThemeSupport
#
end
#
class TableCell < ::Ferro::Component::Base
#
def _before_create
@domtype = :td
if @options[:uuid]
@uuid = @options.delete(:uuid).to_s.to_sym
end
super()
end
#
def uuid()
@uuid
end
#
include GxG::Gui::EventHandlers
include GxG::Gui::AnimationSupport
include GxG::Gui::ApplicationSupport
include GxG::Gui::ThemeSupport
#
end
#
class BlockTable < ::Ferro::Component::Base
#
def _before_create
@states = {:table => true}
if @options[:uuid]
@uuid = @options.delete(:uuid).to_s.to_sym
end
super()
end
#
def uuid()
@uuid
end
#
#
include GxG::Gui::EventHandlers
include GxG::Gui::AnimationSupport
include GxG::Gui::ApplicationSupport
include GxG::Gui::ThemeSupport
#
end
#
class BlockTableHeader < ::Ferro::Component::Base
#
def _before_create
@states = {:th => true}
if @options[:uuid]
@uuid = @options.delete(:uuid).to_s.to_sym
end
super()
end
#
def uuid()
@uuid
end
#
#
include GxG::Gui::EventHandlers
include GxG::Gui::AnimationSupport
include GxG::Gui::ApplicationSupport
include GxG::Gui::ThemeSupport
#
end
#
class BlockTableRow < ::Ferro::Component::Base
#
def _before_create
@states = {:tr => true}
if @options[:uuid]
@uuid = @options.delete(:uuid).to_s.to_sym
end
super()
end
#
def uuid()
@uuid
end
#
include GxG::Gui::EventHandlers
include GxG::Gui::AnimationSupport
include GxG::Gui::ApplicationSupport
include GxG::Gui::ThemeSupport
#
end
#
class BlockTableCell < ::Ferro::Component::Base
def _before_create
@states = {:td => true}
if @options[:uuid]
@uuid = @options.delete(:uuid).to_s.to_sym
end
super()
end
#
def uuid()
@uuid
end
#
include GxG::Gui::EventHandlers
include GxG::Gui::AnimationSupport
include GxG::Gui::ApplicationSupport
include GxG::Gui::ThemeSupport
#
end
#
class Script < ::Ferro::Element::Script
#
def _before_create
if @options[:uuid]
@uuid = @options.delete(:uuid).to_s.to_sym
end
super()
end
#
def uuid()
@uuid
end
#
include GxG::Gui::EventHandlers
#
end
#
class Tree < ::Ferro::Component::Base
#
def _before_create
if @options[:uuid]
@uuid = @options.delete(:uuid).to_s.to_sym
end
super()
end
#
def uuid()
@uuid
end
#
#
include GxG::Gui::EventHandlers
include GxG::Gui::AnimationSupport
include GxG::Gui::ApplicationSupport
include GxG::Gui::ThemeSupport
#
alias :original_add_child :add_child
def add_child(name, element_class, options = {})
if element_class == GxG::Gui::TreeNode
if @content
@content.add_child(name, element_class, options)
end
else
log_warn("Attempted to add a non-TreeNode object - ignoring: #{element_class.inspect}")
end
end
#
alias :original_children :children
def children()
if @content
@content.children
else
{}
end
end
#
def find_child(the_reference=nil, interior=false)
result = nil
if GxG::valid_uuid?(the_reference)
uuid_check = true
else
uuid_check = false
end
if interior == true
search_queue = self.original_children.values
else
search_queue = self.children.values
end
while search_queue.size > 0 do
entry = search_queue.shift
if entry
if uuid_check
if entry.uuid == the_reference
result = entry
break
end
else
if entry.title == the_reference
result = entry
break
end
end
unless result
entry.children.values.each do |the_child|
search_queue << the_child
end
end
end
end
result
end
#
def initialize(the_name,the_class,the_options)
@content = nil
@selection = nil
@processors = {}
super(the_name, the_class, the_options)
self
end
#
def cascade()
#
self.build_interior_components([{:component=>"list", :options=>{:title => "interior_component", :style => {:"white-space" => "nowrap", :width => "auto", :height => "auto", :"list-style" => "none", :margin => "0px", :padding => "0px"}}, :content=>[], :script=>""}])
#
end
#
def update_appearance()
# TODO ??
end
# Selection / Processing:
def set_processor(the_type=nil, &block)
# :select, :expand, :collapse
if block.respond_to?(:call)
if [:select, :expand, :collapse].include?(the_type.to_s.to_sym)
@processors[(the_type.to_s.to_sym)] = block
else
log_warn("Unsupported processor type: #{the_type.to_s.to_sym.inspect}")
end
end
end
#
def node_at_path(the_path=nil)
result = nil
search_queue = self.children.values.clone
while search_queue.size > 0
entry = search_queue.shift
if entry.is_a?(GxG::Gui::TreeNode)
if entry.node_path() == the_path
result = entry
break
else
entry.nodes.values.each do |the_child|
search_queue << the_child
end
end
end
end
result
end
#
def expand_path(the_path=nil,&block)
if the_path.is_a?(::String)
path_stack = []
path_array = the_path.split("/")
path_array.each do |entry|
if entry.size > 0
path_stack << entry
the_node = self.node_at_path(('/' + path_stack.join("/")))
if the_node.is_a?(GxG::Gui::TreeNode)
if the_node.gxg_get_state(:expanded) == false
the_node.expand()
GxG::DISPATCHER.post_event(:display) do
self.expand_path(the_path,&block)
end
break
end
if the_node.node_path() == the_path
if block.respond_to?(:call)
block.call()
end
end
else
GxG::DISPATCHER.post_event(:display) do
self.expand_path(the_path,&block)
end
break
end
end
end
end
true
end
#
def selection()
@selection
end
#
def select(the_node=nil)
if the_node.is_a?(GxG::Gui::TreeNode)
if @selection != the_node
if @selection
@selection.unhighlight()
end
@selection = the_node
@selection.highlight()
# TODO: scroll to reveal highlighted node ??
if @processors[:select].respond_to?(:call)
@processors[:select].call(@selection)
end
end
end
end
#
def expand(the_node=nil)
if the_node.is_a?(GxG::Gui::TreeNode)
if @processors[:expand].respond_to?(:call)
@processors[:expand].call(the_node)
end
end
end
#
def collapse(the_node=nil)
if the_node.is_a?(GxG::Gui::TreeNode)
if @processors[:collapse].respond_to?(:call)
@processors[:collapse].call(the_node)
end
end
end
end
#
class TreeNode < ::Ferro::Element::ListItem
#
def _before_create
if @options[:uuid]
@uuid = @options.delete(:uuid).to_s.to_sym
end
if @data.is_a?(::Hash)
if @data[:title]
@title = @data[:title]
end
end
super()
end
#
def uuid()
@uuid
end
#
include GxG::Gui::EventHandlers
include GxG::Gui::AnimationSupport
include GxG::Gui::ApplicationSupport
include GxG::Gui::ThemeSupport
#
alias :original_add_child :add_child
def add_child(name, element_class, options = {})
if element_class == GxG::Gui::TreeNode
if @content
@content.add_child(name, element_class, options)
end
else
self.original_add_child(name, element_class, options)
end
end
#
def nodes()
if @content
@content.children
else
{}
end
end
#
def node_path()
the_path = ""
lineage = [(self)]
the_parent = self.parent
while the_parent != nil do
if the_parent.is_a?(GxG::Gui::TreeNode)
lineage.unshift(the_parent)
end
the_parent = the_parent.parent
if the_parent.is_a?(GxG::Gui::Tree)
the_parent = nil
end
end
lineage.each do |the_node|
the_path = the_path + "/#{the_node.title}"
end
the_path
end
#
def find_child(the_reference=nil)
result = nil
if GxG::valid_uuid?(the_reference)
uuid_check = true
else
uuid_check = false
end
search_queue = self.children.values
while search_queue.size > 0 do
entry = search_queue.shift
if entry
if uuid_check
if entry.uuid == the_reference
result = entry
break
end
else
if entry.title == the_reference
result = entry
break
end
end
unless result
entry.children.values.each do |the_child|
search_queue << the_child
end
end
end
end
result
end
#
def update_appearance()
if @appearance.is_any?(::Hash, ::GxG::Database::PersistedHash)
#
# TODO: update icon and label if different ??
#
# @icon, @label component objects available.
true
else
false
end
end
#
def data()
@data
end
#
def appearance()
@appearance
end
#
def set_appearance(the_record=nil)
if the_record.is_any?(::Hash, ::GxG::Database::PersistedHash)
@appearance = the_record
# Look for :label, :icon w/in the hash record w/in the array.
self.update_appearance()
end
end
#
def initialize(the_name,the_class,the_options={})
@content = nil
@appearance = {:icon => GxG::DISPLAY_DETAILS[:object].theme_icon("folder.svg"), :label => "Untitled"}
if the_options.is_any?(::Hash, ::GxG::Database::PersistedHash)
if the_options.is_a?(::GxG::Database::PersistedHash)
the_options = the_options.unpersist
else
the_options = the_options.clone
end
# Pass an Hash record containing at LEAST: :title, :type, and :content.
@data = (the_options.delete(:data) || {:title => "Untitled", :type => "virtual_directory", :content => {}})
# select proper icon for @data[:type] value ?? (do in application!)
@appearance[:icon] = (the_options.delete(:icon) || GxG::DISPLAY_DETAILS[:object].theme_icon("folder.svg")).to_s
@appearance[:label] = (the_options.delete(:label) || @data[:title]).to_s
end
super(the_name, the_class, the_options)
self
end
#
def cascade()
#
node_frame = {:component=>"block_table", :options=>{:nodepath => (self.node_path()), :style => {}}, :content=>[], :script=>""}
#
frame_row_one = {:component=>"block_table_row", :options=>{}, :content=>[], :script=>""}
expander_object_title = "Object #{::GxG::uuid_generate.to_s}"
expander = {:component=>"image", :options=>{:title => (expander_object_title), :src=>(GxG::DISPLAY_DETAILS[:object].theme_widget("collapse.svg")), :width=>32, :height=>32, :style => {:clear => "both", :'vertical-align' => 'middle'}}, :content=>[], :script=>""}
expander[:script] = "
on(:mouseup) do |event|
the_node = self.tree_node()
if the_node
unless the_node.gxg_get_state(:disabled) == true
if self.gxg_get_state(:expanded) == true
self.gxg_set_attribute(:src, GxG::DISPLAY_DETAILS[:object].theme_widget('collapse.svg'))
self.gxg_set_state(:expanded,false)
the_node.collapse()
else
self.gxg_set_attribute(:src, GxG::DISPLAY_DETAILS[:object].theme_widget('expand.svg'))
self.gxg_set_state(:expanded,true)
the_node.expand()
end
end
end
end
"
node_expander_cell = {:component=>"block_table_cell", :options=>{:style => {:float => "left", :width => "32px", :'vertical-align' => 'middle'}}, :content=>[], :script=>""}
node_expander_cell[:content] = [(expander)]
#
icon_object_title = "Object #{::GxG::uuid_generate.to_s}"
icon = {:component=>"image", :options=>{:title => (icon_object_title), :src=>@appearance[:icon], :style => {:clear => "both", :width=>"32px", :height=>"32px", :'vertical-align' => 'middle'}}, :content=>[], :script=>""}
icon[:script] = "
on(:mouseup) do |event|
the_node = self.tree_node()
if the_node
the_tree = the_node.tree()
if the_tree
the_tree.select(the_node)
end
end
end
"
node_icon_cell = {:component=>"block_table_cell", :options=>{:style => {:float => "left", :width => "32px", :'vertical-align' => 'middle'}}, :content=>[], :script=>""}
node_icon_cell[:content] = [(icon)]
#
title_object_title = "Object #{::GxG::uuid_generate.to_s}"
title = {:component=>'label', :options=>{:title => (title_object_title), :content => @appearance[:label], :style => {:float => "left", :'font-size' => '16px', :'vertical-align' => 'middle', :'text-align' => 'left', :margin => "2px", :padding => "2px"}}, :content=>[], :script=>''}
title[:script] = "
on(:mouseup) do |event|
the_node = self.tree_node()
if the_node
the_tree = the_node.tree()
if the_tree
the_tree.select(the_node)
end
end
end
"
node_label_cell = {:component=>"block_table_cell", :options=>{:style => {:float => "left", :'vertical-align' => 'middle'}}, :content=>[], :script=>""}
node_label_cell[:content] = [(title)]
#
# frame_row_one[:content] = [(node_indent_cell),(node_expander_cell), (node_icon_cell), (node_label_cell)]
frame_row_one[:content] = [(node_expander_cell), (node_icon_cell), (node_label_cell)]
frame_row_one[:script] = "
on(:mouseenter) do |event|
the_node = self.tree_node()
if the_node
the_tree = the_node.highlight()
end
end
on(:mouseleave) do |event|
the_node = self.tree_node()
if the_node
the_tree = the_node.unhighlight()
end
end
"
#
sublist_object_title = "Object #{::GxG::uuid_generate.to_s}"
sublist = {:component=>"list", :options=>{:title => "interior_component", :width => "100%", :height => "100%", :style => {:"list-style" => "none"}}, :content=>[], :script=>""}
#
subnodes_frame_cell = {:component=>"block_table_cell", :options=>{}, :content=>[], :script=>""}
frame_row_two = {:component=>"block_table_row", :options=>{}, :content=>[], :script=>""}
subnodes_frame_cell[:content] = [(sublist)]
# frame_row_two[:content] = [(subnodes_indent_cell),(subnodes_frame_cell)]
frame_row_two[:content] = [(subnodes_frame_cell)]
#
node_frame[:content] = [(frame_row_one),(frame_row_two)]
#
self.build_interior_components([(node_frame)])
#
# Review: content or sublist ??
# @content = self.find_child(sublist_object_title)
@expander = self.find_child(expander_object_title)
@icon = self.find_child(icon_object_title)
@label = self.find_child(title_object_title)
#
end
# TODO: Add animation / transition effects.
def highlight(transition_parameters=nil)
if @label
@label.gxg_merge_style({:'background-color' => '#87acd5'})
end
end
#
def unhighlight(transition_parameters=nil)
if @label
@label.gxg_merge_style({:'background-color' => '#f2f2f2'})
end
end
#
def expand(transition_parameters=nil)
@content.show(transition_parameters)
self.gxg_set_state(:expanded, true)
if @expander
@expander.gxg_set_attribute(:src,GxG::DISPLAY_DETAILS[:object].theme_widget("expand.svg"))
end
the_tree = self.tree()
if the_tree
the_tree.expand(self)
end
end
#
def collapse(transition_parameters=nil)
@content.hide(transition_parameters)
self.gxg_set_state(:expanded, false)
if @expander
@expander.gxg_set_attribute(:src,GxG::DISPLAY_DETAILS[:object].theme_widget("collapse.svg"))
end
the_tree = self.tree()
if the_tree
the_tree.collapse(self)
end
end
#
def enable(transition_parameters=nil)
if transition_parameters.is_any?(::Hash, ::GxG::Database::PersistedHash)
if transition_parameters.is_a?(::GxG::Database::PersistedHash)
transition_parameters = transition_parameters.unpersist
end
else
transition_parameters = {:origin => {:opacity => 0.5}, :destination => {:opacity => 1.0}}
end
self.children.each_pair do |name, object|
unless name == :interior_component
object.gxg_set_state(:disabled, false)
object.animate(transition_parameters)
end
end
end
#
def disable(transition_parameters=nil)
if transition_parameters.is_any?(::Hash, ::GxG::Database::PersistedHash)
if transition_parameters.is_a?(::GxG::Database::PersistedHash)
transition_parameters = transition_parameters.unpersist
end
else
transition_parameters = {:origin => {:opacity => 1.0}, :destination => {:opacity => 0.5}}
end
self.children.each_pair do |name, object|
unless name == :interior_component
object.animate(transition_parameters)
object.gxg_set_state(:disabled, true)
end
end
end
#
end
#
class Panel < ::Ferro::Component::Base
#
def _before_create
if @options[:uuid]
@uuid = @options.delete(:uuid).to_s.to_sym
end
super()
end
#
def uuid()
@uuid
end
#
def cascade
super()
GxG::DISPLAY_DETAILS[:object].layout_refresh
end
include GxG::Gui::EventHandlers
include GxG::Gui::AnimationSupport
include GxG::Gui::ApplicationSupport
include GxG::Gui::ThemeSupport
# TODO: Add auto-hide/hide/roll up-left-down-right capabilites with animations.
# ????
end
#
class ApplicationViewport < ::Ferro::Component::Base
#
def _before_create
if @options[:uuid]
@uuid = @options.delete(:uuid).to_s.to_sym
end
super()
end
#
include GxG::Gui::EventHandlers
include GxG::Gui::AnimationSupport
include GxG::Gui::ApplicationSupport
include GxG::Gui::ThemeSupport
#
#
def uuid()
@uuid
end
#
def set_application(the_application)
@application = the_application
end
#
def application()
@application
end
#
def find_child(the_reference=nil)
result = nil
if the_reference
search_queue = [(self)]
while search_queue.size > 0 do
entry = search_queue.shift
if entry
if GxG::valid_uuid?(the_reference)
if entry.children[(object_source.uuid())]
result = entry.children[(object_source.uuid())]
break
else
entry.gxg_each_child do |the_child|
search_queue << the_child
end
end
else
entry.gxg_each_child do |the_child|
if the_child.title == the_reference
result = the_child
break
else
search_queue << the_child
end
end
end
end
end
end
result
end
end
#
class Window < ::Ferro::Component::Base
#
def _before_create
if @options[:uuid]
@uuid = @options.delete(:uuid).to_s.to_sym
end
super()
end
#
include GxG::Gui::EventHandlers
include GxG::Gui::AnimationSupport
include GxG::Gui::ApplicationSupport
include GxG::Gui::ThemeSupport
#
def uuid()
@uuid
end
# Methods to override in window's script
def before_close(data=nil)
end
#
def close(data=nil)
end
#
def after_close(data=nil)
end
#
def before_open(data=nil)
end
#
def open(data=nil)
end
#
def after_open(data=nil)
end
#
def set_application(the_application)
@application = the_application
end
#
def application()
@application
end
#
# Support methods:
def commit_settings()
# conform to layout restrictions:
bounds = GxG::DISPLAY_DETAILS[:object].layout_content_area()
# convert to % ??
top_percent = (@top.to_f / bounds[:page_height].to_f) * 100.0
left_percent = (@left.to_f / bounds[:page_width].to_f) * 100.0
height_percent = (self.height.to_f / bounds[:page_height].to_f) * 100.0
width_percent = (self.width.to_f / bounds[:page_width].to_f) * 100.0
#
setting = {}
#
setting[:position] = "absolute"
setting[:display] = "block"
# setting[:overflow] = "hidden"
setting[:tabindex] = -1
setting[:top] = "#{top_percent}%"
setting[:left] = "#{left_percent}%"
setting[:height] = "#{height_percent}%"
setting[:width] = "#{width_percent}%"
#
self.gxg_merge_style(setting)
if @title_object
@title_object.parent.gxg_merge_style({:padding => "0px 0px 0px #{((self.width() / 2) - ((@window_title.size / 2) * 8)) - 60}px"})
end
if @content
@content.gxg_merge_style({:height => "#{self.height - (@title_margin + @menu_margin + @resize_margin)}px", :width => "#{self.width - (@resize_margin * 2)}px"})
# update tracking objects
@tracking.each_pair do |selector, record|
new_style = {}
#
record[:details].each_pair do |op,value|
case op.to_s.to_sym
when :width
if value.to_s.include?("%")
# convert this % to px value width in new_style
new_style[:width] = (((value.to_s.gsub("%","").to_f / 100.0) * (self.width - (@resize_margin * 2)).to_f).to_i.to_s + "px")
else
# assume numeric px value
new_style[:width] = "#{value.to_s}px"
end
when :height
if value.to_s.include?("%")
# convert this % to px value width in new_style
new_style[:height] = (((value.to_s.gsub("%","").to_f / 100.0) * (self.height - (@title_margin + @menu_margin + @resize_margin)).to_f).to_i.to_s + "px")
else
# assume numeric px value
new_style[:height] = "#{value.to_s}px"
end
end
end
#
record[:object].gxg_merge_style(new_style)
end
end
if @menu_bar
@menu_area.gxg_merge_style({:width => "#{self.width - (@resize_margin * 2)}px"})
# @menu_bar.gxg_merge_style({:width => "100%"})
if @menu_bar.respond_to?(:update_appearance)
@menu_bar.update_appearance()
end
end
end
#
def track_deltas(the_reference=nil, the_object=nil, details={})
if the_reference && the_object && details.is_any?(::Hash, ::GxG::Database::PersistedHash)
@tracking[(the_reference)] = {:object => the_object, :details => details}
true
else
false
end
end
def untrack_deltas(the_reference=nil)
if the_reference
@tracking.delete(the_reference)
true
else
false
end
end
#
def content()
@content
end
#
def content_width()
if @content
(`#{@content.element}.getBoundingClientRect().right - #{@content.element}.getBoundingClientRect().left`).to_i
else
0
end
end
#
def content_height()
if @content
(`#{@content.element}.getBoundingClientRect().bottom - #{@content.element}.getBoundingClientRect().top`).to_i
else
0
end
end
#
def set_top(the_top=nil)
if the_top.is_a?(::Numeric)
if @bottom - the_top >= 100
if the_top >= GxG::DISPLAY_DETAILS[:object].layout_content_area()[:top]
@top = the_top
self.fit_within_boundaries()
commit_settings
end
end
end
end
#
def top()
@top
end
#
def set_left(the_left=nil)
if the_left.is_a?(::Numeric)
if @right - the_left >= 200
if the_left >= GxG::DISPLAY_DETAILS[:object].layout_content_area()[:left]
@left = the_left
self.fit_within_boundaries()
commit_settings
end
end
end
end
#
def left()
@left
end
#
def set_right(the_right=nil)
if the_right.is_a?(::Numeric)
if the_right - @left >= 200
if the_right <= GxG::DISPLAY_DETAILS[:object].layout_content_area()[:right]
@right = the_right
self.fit_within_boundaries()
commit_settings
end
end
end
end
#
def right()
@right
end
#
def set_bottom(the_bottom=nil)
if the_bottom.is_a?(::Numeric)
if the_bottom - @top >= 100
if the_bottom <= GxG::DISPLAY_DETAILS[:object].layout_content_area()[:bottom]
@bottom = the_bottom
self.fit_within_boundaries()
commit_settings
end
end
end
end
#
def bottom()
@bottom
end
#
def set_width(the_width=nil)
if the_width.is_a?(::Numeric)
if the_width >= 200
if (@left + the_width) <= GxG::DISPLAY_DETAILS[:object].layout_content_area()[:right]
@right = (@left + the_width)
self.fit_within_boundaries()
commit_settings
end
end
end
end
#
def width()
(@right - @left)
end
#
def set_height(the_height=nil)
if the_height.is_a?(::Numeric)
if the_height >= 100
if (@top + the_height) <= GxG::DISPLAY_DETAILS[:object].layout_content_area()[:bottom]
@bottom = (@top + the_height)
self.fit_within_boundaries()
commit_settings
end
end
end
end
#
def height()
(@bottom - @top)
end
#
def set_position(the_x=nil,the_y=nil)
the_width = self.width
the_height = self.height
bounds = GxG::DISPLAY_DETAILS[:object].layout_content_area()
if ((bounds[:top])..(bounds[:bottom])).include?(the_y.to_i) && ((bounds[:top])..(bounds[:bottom])).include?((the_y.to_i + the_height))
@top = (the_y.to_i)
end
if ((bounds[:left])..(bounds[:right])).include?(the_x.to_i) && ((bounds[:left])..(bounds[:right])).include?((the_x.to_i + the_width))
@left = (the_x.to_i)
end
@bottom = (@top + the_height)
@right = (@left + the_width)
self.fit_within_boundaries()
# commit_settings
# convert to % ??
# top_percent = (@top.to_f / bounds[:page_height].to_f) * 100.0
# left_percent = (@left.to_f / bounds[:page_width].to_f) * 100.0
# self.gxg_merge_style({:left => "#{left_percent}%", :top => "#{top_percent}%"})
self.commit_settings
end
#
def set_frame(settings=nil)
if settings.is_a?(::Hash)
@top = (settings[:top] || @top)
@left = (settings[:left] || @left)
@right = (settings[:right] || @right)
@bottom = (settings[:bottom] || @bottom)
self.fit_within_boundaries()
self.commit_settings
end
end
#
def position()
{:x => @left, :y => @top}
end
#
def fit_within_boundaries()
the_width = self.width
the_height = self.height
bounds = GxG::DISPLAY_DETAILS[:object].layout_content_area()
if @top < bounds[:top]
@top = bounds[:top]
end
if @left < bounds[:left]
@left = bounds[:left]
end
@right = @left + the_width
@bottom = @top + the_height
if @bottom > bounds[:bottom]
@bottom = bounds[:bottom]
end
if @right > bounds[:right]
@right = bounds[:right]
end
end
#
def set_window_title(the_title=nil)
if @title_object
if the_title.is_a?(::String)
@window_title = the_title
@title_object.set_text(@window_title)
@title_object.parent.gxg_merge_style({:padding => "0px 0px 0px #{((self.width() / 2) - ((@window_title.size / 2) * 8)) - 60}px"})
end
end
end
#
def window_title()
if @title_object
@window_title
else
nil
end
end
#
def special_state()
@special_state
end
#
def set_special_state(settings=nil)
if settings.is_a?(::Hash)
@special_state = settings
@top = (settings[:top] || @top)
@left = (settings[:left] || @left)
@right = (settings[:right] || @right)
@bottom = (settings[:bottom] || @bottom)
self.fit_within_boundaries()
self.commit_settings
end
end
#
def clear_special_state(settings=nil)
@special_state = nil
if settings.is_a?(::Hash)
self.set_frame(settings)
end
end
# Initialization and Setup
def initialize(the_name,the_class,the_options)
# FIXME: work around for strange bug in layout_refresh
::GxG::DISPLAY_DETAILS[:object].layout_refresh
bounds = GxG::DISPLAY_DETAILS[:object].layout_content_area()
#
@special_state = nil
@content = nil
# Process the_options
unless the_options.is_a?(::Hash)
the_options = {}
end
# So, at this point I've decided to use pixels internally, but percentages externally. (interface)
# You can pass Integer (as Pixels), Float, or String Percentage
if the_options[:top].is_a?(::String) || the_options[:top].to_s.include?(".")
if the_options[:top].is_a?(::String)
@top = (bounds[:page_height] * (the_options.delete(:top).gsub("%","").to_f / 100.0))
else
@top = (bounds[:page_height] * (the_options.delete(:top) / 100.0))
end
else
@top = (the_options.delete(:top) || ((`window.innerHeight`.to_i / 2) - 50))
end
if the_options[:left].is_a?(::String) || the_options[:left].to_s.include?(".")
if the_options[:left].is_a?(::String)
@left = (bounds[:page_width] * (the_options.delete(:left).gsub("%","").to_f / 100.0))
else
@left = (bounds[:page_width] * (the_options.delete(:left) / 100.0))
end
else
@left = (the_options.delete(:left) || ((`window.innerWidth`.to_i / 2) - 100))
end
if the_options[:right].is_a?(::String) || the_options[:right].to_s.include?(".")
if the_options[:right].is_a?(::String)
@right = (bounds[:page_width] * (the_options.delete(:right).gsub("%","").to_f / 100.0))
else
@right = (bounds[:page_width] * (the_options.delete(:right) / 100.0))
end
else
@right = (the_options.delete(:right) || ((`window.innerWidth`.to_i / 2) + 100))
end
if the_options[:bottom].is_a?(::String) || the_options[:bottom].to_s.include?(".")
if the_options[:bottom].is_a?(::String)
@bottom = (bounds[:page_height] * (the_options.delete(:bottom).gsub("%","").to_f / 100.0))
else
@bottom = (bounds[:page_height] * (the_options.delete(:bottom) / 100.0))
end
else
@bottom = (the_options.delete(:bottom) || ((`window.innerHeight`.to_i / 2) + 50))
end
#
@window_title = the_options[:window_title] || "Untitled"
@title_object = nil
#
if the_options[:width].is_a?(::String) || the_options[:width].to_s.include?(".")
if the_options[:width].is_a?(::String)
@right = @left + ((bounds[:right] - bounds[:left]) * (the_options.delete(:width).gsub("%","").to_f / 100.0))
else
@right = @left + ((bounds[:right] - bounds[:left]) * (the_options.delete(:width) / 100.0))
end
else
if the_options[:width].is_a?(::Numeric)
@right = (@left + the_options.delete(:width))
end
end
if the_options[:height].is_a?(::String) || the_options[:height].to_s.include?(".")
if the_options[:height].is_a?(::String)
@bottom = @top + ((bounds[:bottom] - bounds[:top]) * (the_options.delete(:height).gsub("%","").to_f / 100.0))
else
@bottom = @top + ((bounds[:bottom] - bounds[:top]) * (the_options.delete(:height) / 100.0))
end
else
if the_options[:height].is_a?(::Numeric)
@bottom = (@top + the_options.delete(:height))
end
end
# Bounds check top, left, right, and bottom
self.fit_within_boundaries()
#
@modal = (the_options[:modal] || false)
@scrolling = the_options.delete(:scroll)
@title_margin = 24
if @modal == true
@resize = false
@resize_margin = 0
else
@resize = (the_options[:resize] || true)
@resize_margin = 2
@title_margin = 26
end
if @modal == true
@closebox = false
@minimizebox = false
@maximizebox = false
else
if the_options[:close] == false
@closebox = false
else
@closebox = (the_options[:close] || :enabled).to_s.to_sym
end
if the_options[:minimize] == false
@minimizebox = false
else
@minimizebox = (the_options[:minimize] || :enabled).to_s.to_sym
end
if the_options[:maximize] == false
@maximizebox = false
else
@maximizebox = (the_options[:maximize] || :enabled).to_s.to_sym
end
end
#
@tracking = {}
#
@menu_reference = the_options.delete(:menu)
@menu_area = nil
@menu_bar = nil
if @menu_reference
@menu_margin = 16
else
@menu_margin = 0
end
#
super(the_name,the_class,the_options)
#
# self.gxg_set_attribute(:draggable,true)
#
self
end
#
def menu_reference()
@menu_reference
end
#
def menu()
@menu_bar
end
#
def set_menu(the_menu_source=nil)
if the_menu_source.is_any?(::Hash, ::GxG::Database::PersistedHash)
if @menu_area
page.build_components([{:parent => @menu_area.parent, :record => {:content => [(the_menu_source)]}, :element => @menu_area}])
#
@menu_margin = (`#{@menu_area.element}.getBoundingClientRect().bottom - #{@menu_area.element}.getBoundingClientRect().top`.to_i)
#
@menu_bar = self.find_child(@menu_reference,true)
#
if @menu_bar
self.commit_settings
true
else
log_warn("Unable to properly initialize window menu #{@menu_reference.inspect} .")
false
end
else
false
end
else
false
end
end
#
alias :original_add_child :add_child
def add_child(name, element_class, options = {})
if @content
@content.add_child(name, element_class, options)
end
end
#
alias :original_children :children
def children()
if @content
@content.children
else
{}
end
end
#
def find_child(the_reference=nil, interior=false)
result = nil
if GxG::valid_uuid?(the_reference)
uuid_check = true
else
uuid_check = false
end
if interior == true
search_queue = self.original_children.values
else
search_queue = self.children.values
end
while search_queue.size > 0 do
entry = search_queue.shift
if entry
if uuid_check
if entry.uuid == the_reference
result = entry
break
end
else
if entry.title == the_reference
result = entry
break
end
end
unless result
entry.children.values.each do |the_child|
search_queue << the_child
end
end
end
end
result
end
#
def cascade()
commit_settings
#
build_list = []
#
# Define Title Bar
# FIXME: CSS override of image :src does NOT work -- research.
# title_object_title = "Object #{::GxG::uuid_generate.to_s}"
the_title = {:component=>"label", :options=>{:title => "title_object", :content => @window_title, :style => {:width => "100%", :"font-size" => "16px", :"text-align" => "center"}}, :content=>[], :script=>""}
#
close_box = {:component=>"image", :options=>{:src => theme_widget("close.png"), :width=>20, :height=>20, :style => {:padding => "2px"}}, :content=>[], :script=>""}
close_box[:script] = "
on(:mouseup) do |event|
unless self.gxg_get_state(:disabled) == true
self.gxg_set_attribute(:src,theme_widget('close.png'))
the_window = self.window
if the_window
if the_window.application
the_window.application.window_close({:window => the_window.uuid})
else
GxG::DISPLAY_DETAILS[:object].window_close(the_window.uuid)
end
else
log_warn('Window Not Found')
end
end
end
on(:mousedown) do |event|
unless self.gxg_get_state(:disabled) == true
self.gxg_set_attribute(:src,theme_widget('close_mousedown.png'))
end
end
on(:mouseenter) do |event|
unless self.gxg_get_state(:disabled) == true
self.gxg_set_attribute(:src,theme_widget('close_mouseenter.png'))
end
end
on(:mouseleave) do |event|
unless self.gxg_get_state(:disabled) == true
self.gxg_set_attribute(:src,theme_widget('close.png'))
end
end
"
minimize_box = {:component=>"image", :options=>{:src => theme_widget("minimize.png"), :width=>20, :height=>20, :style => {:padding => "2px"}}, :content=>[], :script=>""}
minimize_box[:script] = "
on(:mouseup) do |event|
unless self.gxg_get_state(:disabled) == true
self.gxg_set_attribute(:src,theme_widget('minimize.png'))
the_window = self.window
if the_window
special = the_window.special_state()
if special.is_a?(::Hash)
if special[:action] == :minimize
GxG::DISPLAY_DETAILS[:object].window_restore(the_window.uuid)
else
GxG::DISPLAY_DETAILS[:object].window_minimize(the_window.uuid,{:top => the_window.top, :left => the_window.left, :right => the_window.right, :bottom => the_window.bottom})
end
else
GxG::DISPLAY_DETAILS[:object].window_minimize(the_window.uuid,{:top => the_window.top, :left => the_window.left, :right => the_window.right, :bottom => the_window.bottom})
end
end
end
end
on(:mousedown) do |event|
unless self.gxg_get_state(:disabled) == true
self.gxg_set_attribute(:src,theme_widget('minimize_mousedown.png'))
end
end
on(:mouseenter) do |event|
unless self.gxg_get_state(:disabled) == true
self.gxg_set_attribute(:src,theme_widget('minimize_mouseenter.png'))
end
end
on(:mouseleave) do |event|
unless self.gxg_get_state(:disabled) == true
self.gxg_set_attribute(:src,theme_widget('minimize.png'))
end
end
"
maximize_box = {:component=>"image", :options=>{:src => theme_widget("maximize.png"), :width=>20, :height=>20, :style => {:padding => "2px"}}, :content=>[], :script=>""}
maximize_box[:script] = "
on(:mouseup) do |event|
unless self.gxg_get_state(:disabled) == true
self.gxg_set_attribute(:src,theme_widget('maximize.png'))
the_window = self.window
if the_window
special = the_window.special_state()
if special.is_a?(::Hash)
if special[:action] == :maximize
GxG::DISPLAY_DETAILS[:object].window_restore(the_window.uuid)
else
GxG::DISPLAY_DETAILS[:object].window_maximize(the_window.uuid,{:top => the_window.top, :left => the_window.left, :right => the_window.right, :bottom => the_window.bottom})
end
else
GxG::DISPLAY_DETAILS[:object].window_maximize(the_window.uuid,{:top => the_window.top, :left => the_window.left, :right => the_window.right, :bottom => the_window.bottom})
end
end
end
end
on(:mousedown) do |event|
unless self.gxg_get_state(:disabled) == true
self.gxg_set_attribute(:src,theme_widget('maximize_mousedown.png'))
end
end
on(:mouseenter) do |event|
unless self.gxg_get_state(:disabled) == true
self.gxg_set_attribute(:src,theme_widget('maximize_mouseenter.png'))
end
end
on(:mouseleave) do |event|
unless self.gxg_get_state(:disabled) == true
self.gxg_set_attribute(:src,theme_widget('maximize.png'))
end
end
"
table = {:component=>"block_table", :options=>{:style => {:clear => "both", :height=>"24px", :padding => "0px", :margin => "0px", :"border-radius" => "5px 5px 0px 0px", :"border-bottom" => "1px solid #c2c2c2"}, :states => {:"default-background-color" => true}}, :content=>[], :script=>""}
row = {:component=>"block_table_row", :options=>{:style => {:padding => "0px", :margin => "0px", :"vertical-align" => "middle", :"background-color" => "transparent"}}, :content=>[], :script=>""}
cell_one = {:component=>"block_table_cell", :options=>{:style => {:padding => "0px", :margin => "0px", :"vertical-align" => "middle", :"background-color" => "transparent"}}, :content=>[], :script=>""}
cell_two = {:component=>"block_table_cell", :options=>{:style => {:width => "100%",:padding => "0px 0px 0px #{((self.width() / 2) - ((@window_title.size / 2) * 8)) - 60}px", :"vertical-align" => "middle", :margin => "0px", :"background-color" => "transparent"}}, :content=>[], :script=>""}
cell_three = {:component=>"block_table_cell", :options=>{:style => {:float => "right", :padding => "0px", :"vertical-align" => "middle", :margin => "0px", :"background-color" => "transparent"}}, :content=>[], :script=>""}
# link pieces
if @closebox
if @closebox == :disabled
close_box[:options][:src] = theme_widget("close_disable.png")
close_box[:options][:states][:disabled] = true
end
cell_one[:content] << close_box
end
if @minimizebox
if @minimizebox == :disabled
minimize_box[:options][:src] = theme_widget("minimize_disable.png")
minimize_box[:options][:states][:disabled] = true
end
cell_one[:content] << minimize_box
end
if @maximizebox
if @maximizebox == :disabled
maximize_box[:options][:src] = theme_widget("maximize_disable.png")
maximize_box[:options][:states][:disabled] = true
end
cell_one[:content] << maximize_box
end
cell_two[:content] = [(the_title)]
cell_two[:script] = "
def do_drag(event)
if ((event[:buttons] & 1) == 1)
#
if @window_handle
unless @last_mouse_x
@last_mouse_x = event[:pageX]
end
unless @last_mouse_y
@last_mouse_y = event[:pageY]
end
offset_x = (event[:pageX] - @window_handle.left)
if event[:pageY] < @window_handle.top
offset_y = (@window_handle.top - event[:pageY])
else
offset_y = (event[:pageY] - @window_handle.top)
end
new_x = ((event[:pageX] - offset_x) + (event[:pageX] - @last_mouse_x))
new_y = ((event[:pageY] - offset_y) + (event[:pageY] - @last_mouse_y))
@window_handle.set_position(new_x,new_y)
@last_mouse_x = event[:pageX]
@last_mouse_y = event[:pageY]
end
end
end
on(:dblclick) do |event|
the_window = self.window
if the_window
special = the_window.special_state()
if special.is_a?(::Hash)
if special[:action] == :rollup
GxG::DISPLAY_DETAILS[:object].window_restore(the_window.uuid)
else
GxG::DISPLAY_DETAILS[:object].window_rollup(the_window.uuid,{:top => the_window.top, :left => the_window.left, :right => the_window.right, :bottom => the_window.bottom})
end
else
GxG::DISPLAY_DETAILS[:object].window_rollup(the_window.uuid,{:top => the_window.top, :left => the_window.left, :right => the_window.right, :bottom => the_window.bottom})
end
end
end
on(:mouseup) do |event|
@mode = :hover
the_window = self.window
if the_window
GxG::DISPLAY_DETAILS[:object].window_bring_to_front(the_window.uuid)
end
end
on(:mousemove) do |event|
@window_handle = self.window
if ((event[:buttons] & 1) == 1)
@mode = :drag
do_drag(event)
else
@mode = :hover
end
end
on(:mouseover) do |event|
if @mode == :drag
do_drag(event)
end
end
on(:mouseleave) do |event|
if @mode == :drag
do_drag(event)
end
end
on(:mouseenter) do |event|
if @mode == :drag
do_drag(event)
end
end
"
row[:content] = [(cell_one), (cell_two), (cell_three)]
table[:content] = [(row)]
#
build_list << table
#
interior_object_title = "Object #{::GxG::uuid_generate.to_s}"
interior_component = {:component=>"block", :options=>{:title => "interior_component", :style => {:clear => "both", :padding => "0px", :margin => "0px", :border => "0px", :width => "#{(self.width - (@resize_margin * 2))}px", :height => "#{self.height - (@title_margin + @menu_margin + @resize_margin)}px"}, :states => {:"default-background-color" => true}}, :content=>[], :script=>""}
#
if @scrolling
if @scrolling == true
interior_component[:options][:style][:overflow] = "scroll"
else
if @scrolling == "vertical"
interior_component[:options][:style][:"overflow-y"] = "scroll"
end
if @scrolling == "horizontal"
interior_component[:options][:style][:"overflow-x"] = "scroll"
end
end
else
interior_component[:options][:style][:overflow] = "hidden"
end
#
left_resizer = {:component => "block_table_cell", :options => {:style => {:width => "2px", :cursor => "w-resize"}}, :content => [], :script => ""}
left_resizer[:script] = "
def do_drag(event)
if ((event[:buttons] & 1) == 1)
window_handle = self.window
if window_handle
window_handle.set_left(event[:pageX])
end
end
end
on(:mouseup) do |event|
@mode = :hover
the_window = self.window
if the_window
GxG::DISPLAY_DETAILS[:object].window_bring_to_front(the_window.uuid)
end
end
on(:mousedown) do |event|
@mode = :drag
do_drag(event)
end
on(:mouseover) do |event|
if @mode == :drag
do_drag(event)
end
end
on(:mouseleave) do |event|
if @mode == :drag
do_drag(event)
end
end
on(:mouseenter) do |event|
if @mode == :drag
do_drag(event)
end
end
"
right_resizer = {:component => "block_table_cell", :options => {:style => {:width => "2px", :cursor => "e-resize"}}, :content => [], :script => ""}
right_resizer[:script] = "
def do_drag(event)
if ((event[:buttons] & 1) == 1)
window_handle = self.window
if window_handle
window_handle.set_right(event[:pageX] + 1)
end
end
end
on(:mouseup) do |event|
@mode = :hover
the_window = self.window
if the_window
GxG::DISPLAY_DETAILS[:object].window_bring_to_front(the_window.uuid)
end
end
on(:mousedown) do |event|
@mode = :drag
do_drag(event)
end
on(:mouseover) do |event|
if @mode == :drag
do_drag(event)
end
end
on(:mouseleave) do |event|
if @mode == :drag
do_drag(event)
end
end
on(:mouseenter) do |event|
if @mode == :drag
do_drag(event)
end
end
"
bottom_resizer = {:component => "block_table_cell", :options => {:style => {:height => "2px", :cursor => "s-resize"}}, :content => [], :script => ""}
bottom_resizer[:script] = "
def do_drag(event)
window_handle = self.window
if window_handle
if ((event[:buttons] & 1) == 1)
window_handle.set_bottom(event[:pageY] + 4)
end
end
end
on(:mouseup) do |event|
@mode = :hover
the_window = self.window
if the_window
GxG::DISPLAY_DETAILS[:object].window_bring_to_front(the_window.uuid)
end
end
on(:mousedown) do |event|
@mode = :drag
do_drag(event)
end
on(:mouseover) do |event|
if @mode == :drag
do_drag(event)
end
end
on(:mouseleave) do |event|
if @mode == :drag
do_drag(event)
end
end
on(:mouseenter) do |event|
if @mode == :drag
do_drag(event)
end
end
"
sw_resizer = {:component => "block_table_cell", :options => {:style => {:width => "2px", :height => "2px", :cursor => "sw-resize"}}, :content => [], :script => ""}
sw_resizer[:script] = "
def do_drag(event)
if ((event[:buttons] & 1) == 1)
window_handle = self.window
if window_handle
window_handle.set_left(event[:pageX] - 2)
window_handle.set_bottom(event[:pageY] + 4)
end
end
end
on(:mouseup) do |event|
@mode = :hover
the_window = self.window
if the_window
GxG::DISPLAY_DETAILS[:object].window_bring_to_front(the_window.uuid)
end
end
on(:mousedown) do |event|
@mode = :drag
do_drag(event)
end
on(:mouseover) do |event|
if @mode == :drag
do_drag(event)
end
end
on(:mouseleave) do |event|
if @mode == :drag
do_drag(event)
end
end
on(:mouseenter) do |event|
if @mode == :drag
do_drag(event)
end
end
"
se_resizer = {:component => "block_table_cell", :options => {:style => {:width => "2px", :height => "2px", :cursor => "se-resize"}}, :content => [], :script => ""}
se_resizer[:script] = "
def do_drag(event)
if ((event[:buttons] & 1) == 1)
window_handle = self.window
if window_handle
window_handle.set_right(event[:pageX] + 2)
window_handle.set_bottom(event[:pageY] + 4)
end
end
end
on(:mouseup) do |event|
@mode = :hover
the_window = self.window
if the_window
GxG::DISPLAY_DETAILS[:object].window_bring_to_front(the_window.uuid)
end
end
on(:mousedown) do |event|
@mode = :drag
do_drag(event)
end
on(:mouseover) do |event|
if @mode == :drag
do_drag(event)
end
end
on(:mouseleave) do |event|
if @mode == :drag
do_drag(event)
end
end
on(:mouseenter) do |event|
if @mode == :drag
do_drag(event)
end
end
"
left_menu_resize = {:component => "block_table_cell", :options => {:style => {:width => "2px", :cursor => "w-resize"}}, :content => [], :script => ""}
left_menu_resize[:script] = "
def do_drag(event)
if ((event[:buttons] & 1) == 1)
window_handle = self.window
if window_handle
window_handle.set_left(event[:pageX])
end
end
end
on(:mouseup) do |event|
@mode = :hover
the_window = self.window
if the_window
GxG::DISPLAY_DETAILS[:object].window_bring_to_front(the_window.uuid)
end
end
on(:mousedown) do |event|
@mode = :drag
do_drag(event)
end
on(:mouseover) do |event|
if @mode == :drag
do_drag(event)
end
end
on(:mouseleave) do |event|
if @mode == :drag
do_drag(event)
end
end
on(:mouseenter) do |event|
if @mode == :drag
do_drag(event)
end
end
"
right_menu_resize = {:component => "block_table_cell", :options => {:style => {:width => "2px", :cursor => "e-resize"}}, :content => [], :script => ""}
right_menu_resize[:script] = "
def do_drag(event)
if ((event[:buttons] & 1) == 1)
window_handle = self.window
if window_handle
window_handle.set_right(event[:pageX] + 1)
end
end
end
on(:mouseup) do |event|
@mode = :hover
the_window = self.window
if the_window
GxG::DISPLAY_DETAILS[:object].window_bring_to_front(the_window.uuid)
end
end
on(:mousedown) do |event|
@mode = :drag
do_drag(event)
end
on(:mouseover) do |event|
if @mode == :drag
do_drag(event)
end
end
on(:mouseleave) do |event|
if @mode == :drag
do_drag(event)
end
end
on(:mouseenter) do |event|
if @mode == :drag
do_drag(event)
end
end
"
# menu_object_title = "Object #{::GxG::uuid_generate.to_s}"
menu_cell = {:component => "block_table_cell", :options => {:title => "menu_area", :style => {:overflow => "hidden", :height=>"16px",:padding => "0px", :margin => "0px"}}, :content => [], :script => ""}
# menu_row_object_title = "Object #{::GxG::uuid_generate.to_s}"
menu_row = {:component=>"block_table_row", :options=>{:title => "menu_area_row", :style => {:overflow => "hidden", :clear => "both", :padding => "0px", :width => "100%", :height=>"16px", :margin => "0px", :"vertical-align" => "middle", :"background-color" => "transparent", :"border-bottom" => "1px solid #c2c2c2"}}, :content => [(left_menu_resize),(menu_cell),(right_menu_resize)], :script=>""}
content_area = {:component => "block_table_cell", :options => {:style => {:padding => "0px", :margin => "0px", :border => "0px"}}, :content => [], :script => ""}
row_one = {:component => "block_table_row", :options => {}, :content => [], :script => ""}
row_two = {:component => "block_table_row", :options => {}, :content => [], :script => ""}
# window_body_title = "Object #{::GxG::uuid_generate.to_s}"
resizing_areas = {:component => "block_table", :options => {:title => "window_body", :style => {:width => "100%"}, :states => {:"default-background-color" => true}}, :content => [], :script => ""}
# link pieces
content_area[:content] = [(interior_component)]
if @resize
row_one[:content] = [(left_resizer),(content_area),(right_resizer)]
row_two[:content] = [(sw_resizer),(bottom_resizer),(se_resizer)]
if @menu_reference
resizing_areas[:content] = [(menu_row),(row_one),(row_two)]
else
resizing_areas[:content] = [(row_one),(row_two)]
end
else
if @menu_reference
row_one[:content] = [(content_area)]
resizing_areas[:content] = [(menu_row),(row_one)]
else
row_one[:content] = [(content_area)]
resizing_areas[:content] = [(row_one)]
end
end
#
build_list << resizing_areas
#
self.build_interior_components(build_list)
#
# the_object = self.find_child(interior_object_title,true)
# if the_object
# @content = the_object
# end
#
# the_object = self.find_child(window_body_title,true)
# if the_object
# @window_body = the_object
# end
#
the_object = self.find_child("title_object",true)
if the_object
@title_object = the_object
end
#
the_object = self.find_child("menu_area",true)
if the_object
@menu_area = the_object
@menu_margin = (`#{@menu_area.element}.getBoundingClientRect().bottom - #{@menu_area.element}.getBoundingClientRect().top`.to_i)
# border-collapse: collapse
if @menu_area
@menu_area.parent.parent.gxg_merge_style({:"border-collapse" => "collapse"})
end
else
# test: log_warn("Unable to establish Menu Area link internally.")
end
# FIXME: does not set correct size of content by default (!) (workaround here:)
# self.set_bottom(self.bottom())
# (result: no effect)
# other attempt: (THIS worked!)
GxG::DISPATCHER.post_event do
self.commit_settings
end
self
end
end
#
class DialogBox < ::Ferro::Component::Base
#
def _before_create
if @options[:uuid]
@uuid = @options.delete(:uuid).to_s.to_sym
end
super()
end
#
include GxG::Gui::EventHandlers
include GxG::Gui::AnimationSupport
include GxG::Gui::ApplicationSupport
include GxG::Gui::ThemeSupport
#
def uuid()
@uuid
end
# Methods to override in window's script
def before_close(data=nil)
# override in window's script
end
#
def after_close(data=nil)
# override in window's script
end
#
def before_open(data=nil)
# override in window's script
end
#
def after_open(data=nil)
# override in window's script
end
#
def set_application(the_application)
@application = the_application
end
#
def application()
@application
end
#
def set_window_title(the_title=nil)
if @title_object
if the_title.is_a?(::String)
@window_title = the_title
@title_object.set_text(@window_title)
end
end
end
#
def window_title()
if @title_object
@window_title
else
nil
end
end
#
def set_data(data={})
@data = data
end
#
def data()
@data
end
#
def set_responder(block=nil)
if block.respond_to?(:call)
@responder = block
end
end
#
def respond(data=nil)
self.page.dialog_close()
if @responder.respond_to?(:call)
@responder.call(data)
end
end
# Geometry details:
def top()
@top
end
#
def left()
@left
end
#
def right()
@right
end
#
def bottom()
@bottom
end
#
def width()
(@right - @left)
end
#
def height()
(@bottom - @top)
end
#
def position()
{:x => @left, :y => @top}
end
#
def layer()
@layer
end
#
# Initialization and Setup
def initialize(the_name,the_class,the_options)
# FIXME: work around for strange bug in layout_refresh
# ::GxG::DISPLAY_DETAILS[:object].layout_refresh
# bounds = GxG::DISPLAY_DETAILS[:object].layout_content_area()
#
@special_state = nil
@content = nil
# Process the_options
unless the_options.is_a?(::Hash)
the_options = {}
end
# So, at this point I've decided to use pixels internally, but percentages externally. (interface)
# You can pass Integer (as Pixels), Float, or String Percentage
the_width = nil
if the_options[:width].is_a?(::String) || the_options[:width].to_s.include?(".")
if the_options[:width].is_a?(::String)
the_width = ((`window.innerWidth`.to_f) * (the_options.delete(:width).gsub("%","").to_f / 100.0))
else
the_width = ((`window.innerWidth`.to_f) * (the_options.delete(:width) / 100.0))
end
else
if the_options[:width].is_a?(::Numeric)
the_width = the_options.delete(:width).to_f
end
end
if the_width
@left = ((`window.innerWidth`.to_f / 2.0) - (the_width / 2.0))
@right = ((`window.innerWidth`.to_f / 2.0) + (the_width / 2.0))
end
#
the_height = nil
if the_options[:height].is_a?(::String) || the_options[:height].to_s.include?(".")
if the_options[:height].is_a?(::String)
the_height = ((`window.innerHeight`.to_f) * (the_options.delete(:height).gsub("%","").to_f / 100.0))
else
the_height = ((`window.innerHeight`.to_f) * (the_options.delete(:height) / 100.0))
end
else
if the_options[:height].is_a?(::Numeric)
the_height = the_options.delete(:height).to_f
end
end
if the_height
@top = ((`window.innerHeight`.to_f / 2.0) - (the_height / 2.0))
@bottom = ((`window.innerHeight`.to_f / 2.0) + (the_height / 2.0))
end
#
if the_options[:top].is_a?(::String) || the_options[:top].to_s.include?(".")
if the_options[:top].is_a?(::String)
@top = (`window.innerHeight`.to_f * (the_options.delete(:top).gsub("%","").to_f / 100.0))
else
@top = (`window.innerHeight`.to_f * (the_options.delete(:top) / 100.0))
end
else
unless the_height
@top = (the_options.delete(:top) || ((`window.innerHeight`.to_i / 2) - 150))
end
end
if the_options[:left].is_a?(::String) || the_options[:left].to_s.include?(".")
if the_options[:left].is_a?(::String)
@left = (`window.innerWidth`.to_f * (the_options.delete(:left).gsub("%","").to_f / 100.0))
else
@left = (`window.innerWidth`.to_f * (the_options.delete(:left) / 100.0))
end
else
unless the_width
@left = (the_options.delete(:left) || ((`window.innerWidth`.to_i / 2) - 300))
end
end
if the_options[:right].is_a?(::String) || the_options[:right].to_s.include?(".")
if the_options[:right].is_a?(::String)
@right = (`window.innerWidth`.to_f * (the_options.delete(:right).gsub("%","").to_f / 100.0))
else
@right = (`window.innerWidth`.to_f * (the_options.delete(:right) / 100.0))
end
else
unless the_width
@right = (the_options.delete(:right) || ((`window.innerWidth`.to_i / 2) + 300))
end
end
if the_options[:bottom].is_a?(::String) || the_options[:bottom].to_s.include?(".")
if the_options[:bottom].is_a?(::String)
@bottom = (`window.innerHeight`.to_f * (the_options.delete(:bottom).gsub("%","").to_f / 100.0))
else
@bottom = (`window.innerHeight`.to_f * (the_options.delete(:bottom) / 100.0))
end
else
unless the_height
@bottom = (the_options.delete(:bottom) || ((`window.innerHeight`.to_i / 2) + 150))
end
end
#
@window_title = the_options[:window_title] || "Untitled"
@title_object = nil
#
@opacity = (the_options.delete(:opacity) || 0.8)
@background_color = (the_options.delete(:"background-color") || "rgba(0,0,0,#{@opacity})")
@layer = 2000
#
@tracking = {}
@responder = nil
@data = {}
#
super(the_name,the_class,the_options)
#
self.gxg_set_attribute(:draggable,false)
#
self
end
# Slight of hand:
alias :original_add_child :add_child
def add_child(name, element_class, options = {})
if @content
@content.add_child(name, element_class, options)
end
end
#
alias :original_children :children
def children()
if @content
@content.children
else
{}
end
end
#
def find_child(the_reference=nil, interior=false)
result = nil
if GxG::valid_uuid?(the_reference)
uuid_check = true
else
uuid_check = false
end
if interior == true
search_queue = self.original_children.values
else
search_queue = self.children.values
end
while search_queue.size > 0 do
entry = search_queue.shift
if entry
if uuid_check
if entry.uuid == the_reference
result = entry
break
end
else
if entry.title == the_reference
result = entry
break
end
end
unless result
entry.children.values.each do |the_child|
search_queue << the_child
end
end
end
end
result
end
#
# Construction
def cascade()
# Initialize Style Settings:
self.gxg_merge_style({:position => "absolute", :top => "0px", :left => "0px", :width => "100%", :height => "100%", :"background-color" => @background_color, :"z-index" => @layer})
mouse_event_capture = "
on(:mouseup,true) do |event|
end
on(:mousedown,true) do |event|
end
on(:mousemove,true) do |event|
end
on(:mouseenter,true) do |event|
end
on(:mouseleave,true) do |event|
end
"
self.set_script(mouse_event_capture)
# Prep build_list
build_list = []
#
modal_frame = {:component=>"block_table", :options=>{:style => {:opacity => 1.0, :clear => "both", :overflow => "hidden", :position => "absolute", :top => "#{@top}px", :left => "#{@left}px", :width => "#{self.width}px", :height=>"#{self.height}px", :padding => "0px", :margin => "0px", :"border-radius" => "5px 5px 5px 5px", :"z-index" => (@layer + 100)}, :states => {:"default-background-color" => true}}, :content=>[], :script=>""}
title_object_title = "Object #{::GxG::uuid_generate.to_s}"
the_title = {:component=>"label", :options=>{:title => title_object_title, :content => @window_title, :style => {:opacity => 1.0, :width => "100%", :"font-size" => "16px", :"text-align" => "center"}}, :content=>[], :script=>""}
title_cell = {:component=>"block_table_cell", :options=>{:style => {:opacity => 1.0, :width => "100%", :padding => "0px 0px 0px #{((self.width() / 2) - ((@window_title.size / 2) * 8))}px", :margin => "0px", :"vertical-align" => "middle", :"background-color" => "transparent"}}, :content=>[(the_title)], :script=>""}
title_row = {:component=>"block_table_row", :options=>{:style => {:opacity => 1.0, :clear => "both", :padding => "0px", :width => "100%", :height=>"24px", :margin => "0px", :"vertical-align" => "middle", :"background-color" => "#ffffff", :"border-bottom" => "1px solid #c2c2c2"}}, :content=>[(title_cell)], :script=>""}
#
interior_object_title = "Object #{::GxG::uuid_generate.to_s}"
interior_component = {:component=>"block_table_cell", :options=>{:title => "interior_component", :style => {:opacity => 1.0, :clear => "both", :padding => "0px", :margin => "0px", :border => "0px", :width => "#{self.width}px", :height => "#{self.height}px"}, :states => {:"default-background-color" => true}}, :content=>[], :script=>""}
content_row = {
:component => "block_table_row",
:options => {
:style => {
:opacity => 1.0,
:width => "#{self.width}px",
:height => "#{self.height - 24}px",
:padding => "0px",
:margin => "0px",
:border => "0px"
}
},
:content => [(interior_component)],
:script => ""
}
#
modal_frame[:content] = [(title_row),(content_row)]
build_list << modal_frame
#
self.build_interior_components(build_list)
#
# the_object = self.find_child(interior_object_title,true)
# if the_object
# @content = the_object
# end
#
the_object = self.find_child(title_object_title,true)
if the_object
@title_object = the_object
end
#
end
# Hooks for open/close events
def open(details={})
# override in object script
end
#
def close(details={})
# override in object script
end
end
# Menu Supports
class MenuItem < ::GxG::Gui::Block
def _after_create()
if @enabled == true
self.enable
else
self.disable
end
super()
end
#
# Structure:
def opened?()
false
end
#
def collapse()
self.unhighlight
if @the_parent.respond_to?(:collapse)
@the_parent.collapse()
end
end
#
def collapse_down()
self.unhighlight
end
# Visuals:
def enabled?()
@enabled
end
def enable()
@enabled = true
self.gxg_merge_style(:opacity => 1.0)
end
def disable()
@enabled = false
self.unhighlight
self.gxg_merge_style(:opacity => 0.5)
end
def highlight()
self.gxg_merge_style({:'background-color' => '#87acd5'})
end
def unhighlight()
self.gxg_merge_style({:'background-color' => '#f2f2f2'})
end
def update_appearance()
# override in object script
end
#
def initialize(the_name,the_class,the_options)
unless @title
@title = the_options.delete(:title)
end
@menu = the_options.delete(:menu)
@the_parent = the_options.delete(:parent)
@orientation = (the_options.delete(:orientation) || "vertical").to_s.to_sym
if @orientation == :horizontal
@zone = (the_options.delete(:zone) || "top").to_s.to_sym
else
@zone = (the_options.delete(:zone) || "left").to_s.to_sym
end
@enabled = the_options.delete(:enabled)
unless @enabled == false
@enabled = true
end
# presentation supports:
@icon = the_options.delete(:icon)
@icon_width = (the_options.delete(:icon_width) || 32)
@icon_height = (the_options.delete(:icon_height) || 32)
@label = the_options.delete(:content)
@key = the_options.delete(:key)
# Data payload:
@data = the_options.delete(:data)
#
super(the_name, the_class, the_options)
#
# Mouse Events:
on(:mouseup) do |event|
if self.enabled?
if @menu
self.collapse()
@menu.select_item(self)
end
end
end
on(:mouseenter) do |event|
if self.enabled?
self.highlight
end
end
on(:mouseleave) do |event|
if self.enabled?
self.unhighlight
end
end
self
end
# Payload:
def data()
@data
end
#
def cascade()
build_list = []
#
spacer = {:component=>"block", :options=>{:style => {:clear => "both", :width=>"32px", :height=>"32px"}}, :content=>[], :script=>""}
#
icon = nil
if @icon
icon = {:component=>"image", :options=>{:src=>@icon, :width=>@icon_width, :height=>@icon_height, :style => {:clear => "both"}}, :content=>[], :script=>""}
end
#
label = nil
if @label
label = {:component=>"label", :options=>{:content => @label, :style => {:padding => "0px 0px 2px 0px", :"font-size" => "16px"}}, :content=>[], :script=>""}
end
#
key = nil
if @key
key = {:component=>"label", :options=>{:content => @key, :style => {:"font-size" => "16px"}}, :content=>[], :script=>""}
end
# grid structure
left_spacer_cell = {:component=>"block_table_cell", :options=>{:style => {:float => "left", :padding => "0px", :margin => "0px"}}, :content=>[(spacer)], :script=>""}
right_spacer_cell = {:component=>"block_table_cell", :options=>{:style => {:float => "right", :padding => "0px", :margin => "0px"}}, :content=>[(spacer)], :script=>""}
#
icon_cell = {:component=>"block_table_cell", :options=>{:style => {:float => "left", :padding => "0px", :margin => "0px", :'vertical-align' => 'middle'}}, :content=>[(icon)], :script=>""}
label_cell = {:component=>"block_table_cell", :options=>{:style => {:float => "left", :padding => "0px 0px 0px 5px", :margin => "0px", :'vertical-align' => 'middle'}}, :content=>[(label)], :script=>""}
key_cell = {:component=>"block_table_cell", :options=>{:style => {:float => "right", :padding => "0px", :margin => "0px", :'vertical-align' => 'middle'}}, :content=>[(key)], :script=>""}
entry_row = {:component=>"block_table_row", :options=>{:style => {:clear => "both", :padding => "0px", :margin => "0px"}}, :content=>[], :script=>""}
#
if @zone == :right
entry_row[:content] << left_spacer_cell
end
#
if icon
entry_row[:content] << icon_cell
end
#
if label
entry_row[:content] << label_cell
end
#
if key
entry_row[:content] << key_cell
end
#
if @zone != :right
entry_row[:content] << right_spacer_cell
end
#
entry_table = {:component=>"block_table", :options=>{:style => {:padding => "0px", :margin => "0px"}}, :content=>[(entry_row)], :script=>""}
#
build_list << entry_table
#
self.build_interior_components(build_list)
end
#
end
#
class MenuEntry < ::GxG::Gui::Block
def _after_create()
if @enabled == true
self.enable
else
self.disable
end
super()
end
#
def initialize(the_name,the_class,the_options)
@opened = false
unless @title
@title = the_options.delete(:title)
end
@menu = the_options.delete(:menu)
@the_parent = the_options.delete(:parent)
@orientation = (the_options.delete(:orientation) || "vertical").to_s.to_sym
if @orientation == :horizontal
@zone = (the_options.delete(:zone) || "top").to_s.to_sym
else
@zone = (the_options.delete(:zone) || "left").to_s.to_sym
end
if @the_parent.is_a?(::GxG::Gui::MenuBar)
@submenu = false
else
@submenu = true
end
@enabled = the_options.delete(:enabled)
unless @enabled == false
@enabled = true
end
# presentation supports:
@icon = the_options.delete(:icon)
@icon_width = (the_options.delete(:icon_width) || 32)
@icon_height = (the_options.delete(:icon_height) || 32)
@label = the_options.delete(:content)
@label_size = (the_options.delete(:content_size) || 16).to_s.gsub("px","").to_i
@key = the_options.delete(:key)
@layer = (the_options.delete(:layer) || 1000)
@menu_item_width = [200,(@icon_width)].max
# Review: adjust for border, padding, and margin
@menu_item_height = 32
# if @icon
# @menu_item_height = [(@label_size),(@icon_height)].max
# else
# @menu_item_height = @label_size + 2
# end
#
super(the_name, the_class, the_options)
# Create off-entry block body to attach items/etc to.
the_uuid = GxG::uuid_generate.to_sym
the_title = "Untitled Component #{the_uuid}"
@body = GxG::DISPLAY_DETAILS[:object].add_child(the_uuid, ::GxG::Gui::Block, {:uuid => the_uuid})
@body.set_title(the_title)
GxG::DISPLAY_DETAILS[:object].register_object(the_title, @body)
# Set @body base-state
@body.gxg_set_state(:hidden, true)
# FIXME: menu background is errantly too short.
# @body.gxg_set_state(:"default-background-color", true)
@body.gxg_merge_style({:position => "absolute", :"background-color" => "#f2f2f2", :border => "1px solid #c2c2c2"})
@body.on(:mouseleave) do |event|
hold_open = false
search_queue = @body.children.values
while search_queue.size > 0 do
the_child = search_queue.shift
if the_child.is_any?(::GxG::Gui::MenuEntry, ::GxG::Gui::MenuItem)
if the_child.opened?
hold_open = true
break
end
else
the_child.children.values.each do |sub_child|
search_queue << sub_child
end
end
end
unless hold_open == true
@body.hide
@opened = false
end
end
#
the_uuid = GxG::uuid_generate.to_sym
the_title = "Untitled Component #{the_uuid}"
@body_content = @body.add_child(the_uuid, ::GxG::Gui::BlockTable, {:uuid => the_uuid})
@body_content.set_title(the_title)
GxG::DISPLAY_DETAILS[:object].register_object(the_title, @body_content)
# Mouse Events:
on(:mouseup) do |event|
if self.enabled?
if @body
if @body.gxg_get_state(:hidden) == true
self.update_appearance
@body.show
@opened = true
else
self.collapse_down
@body.hide
@opened = false
end
end
end
end
on(:mouseenter) do |event|
if self.enabled?
self.highlight
end
end
on(:mouseleave) do |event|
if self.enabled?
self.unhighlight
end
end
#
self
end
# Slight of hand:
alias :original_children :children
def children()
if @body
@body_content.children
else
{}
end
end
#
alias :original_add_child :add_child
def add_child(name, element_class, options = {})
the_object = nil
if element_class == GxG::Gui::MenuEntry || element_class == GxG::Gui::MenuItem
if @body
#
if @orientation == :horizontal
the_row = @body_content.find_child_type(::GxG::Gui::BlockTableRow)
unless the_row
the_uuid = GxG::uuid_generate.to_sym
the_title = "Untitled Component #{the_uuid}"
the_row = @body_content.add_child(the_uuid, ::GxG::Gui::BlockTableRow, {:uuid => the_uuid})
the_row.set_title(the_title)
GxG::DISPLAY_DETAILS[:object].register_object(the_title, the_row)
end
if the_row
the_uuid = GxG::uuid_generate.to_sym
the_title = "Untitled Component #{the_uuid}"
the_cell = the_row.add_child(the_uuid, ::GxG::Gui::BlockTableCell, {:uuid => the_uuid})
the_cell.set_title(the_title)
GxG::DISPLAY_DETAILS[:object].register_object(the_title, the_cell)
if the_cell
the_object = the_cell.add_child(name, element_class, {:orientation => @orientation, :zone => @zone, :menu => @menu, :parent => self}.merge(options))
# Review: MenuEntry too short issue
self.update_appearance()
else
# err - cannot proceed.
end
else
# err - cannot proceed.
end
else
the_uuid = GxG::uuid_generate.to_sym
the_title = "Untitled Component #{the_uuid}"
the_row = @body_content.add_child(the_uuid, ::GxG::Gui::BlockTableRow, {:uuid => the_uuid})
the_row.set_title(the_title)
GxG::DISPLAY_DETAILS[:object].register_object(the_title, the_row)
if the_row
the_uuid = GxG::uuid_generate.to_sym
the_title = "Untitled Component #{the_uuid}"
the_cell = the_row.add_child(the_uuid, ::GxG::Gui::BlockTableCell, {:uuid => the_uuid})
the_cell.set_title(the_title)
GxG::DISPLAY_DETAILS[:object].register_object(the_title, the_cell)
if the_cell
the_object = the_cell.add_child(name, element_class, {:orientation => @orientation, :zone => @zone, :menu => @menu, :parent => self}.merge(options))
# Review: MenuEntry too short issue
self.update_appearance()
else
# err - cannot proceed.
end
else
# err - cannot proceed.
end
end
else
# err - cannot proceed
end
else
# the_object = self.original_add_child(name, element_class, options)
log_warn("MenuEntry only accepts MenuEntry or MenuItem as children, not: #{element_class.inspect} --> Other errors will likely follow.")
end
if the_object && @menu
@menu.register_entry(the_object)
end
the_object
end
#
def entries()
results = []
search_pool = self.children.values
while search_pool.size > 0 do
entry = search_pool.shift
if entry
if entry.is_any?(::GxG::Gui::MenuEntry, ::GxG::Gui::MenuItem)
results << entry
else
entry.children.values.each do |the_child|
search_pool << the_child
end
end
end
end
results
end
# Structure:
def submenu()
@submenu
end
#
def opened?()
@opened
end
#
def set_opened(state=false)
@opened = state
end
#
def collapse()
if @body
@body.hide
end
self.unhighlight
if @the_parent.respond_to?(:collapse)
@the_parent.collapse()
end
end
#
def collapse_down()
self.entries.each do |the_child|
the_child.collapse_down
end
if @body
@body.hide
end
self.unhighlight
end
# Visuals:
def enabled?()
@enabled
end
def enable()
@enabled = true
self.entries.each do |the_child|
the_child.enable
end
self.gxg_merge_style(:opacity => 1.0)
end
def disable()
@enabled = false
self.entries.each do |the_child|
the_child.disable
end
@body.hide
self.unhighlight
self.gxg_merge_style(:opacity => 0.5)
end
def highlight()
self.gxg_merge_style({:'background-color' => '#87acd5'})
end
def unhighlight()
self.gxg_merge_style({:'background-color' => '#f2f2f2'})
end
def update_appearance()
# Set @body position according to @orientation and @zone
rectangle = {:top => 0, :left => 0, :right => 0, :bottom => 0}
rectangle[:top] = `#{self.element}.getBoundingClientRect().top`.to_i
rectangle[:left] = `#{self.element}.getBoundingClientRect().left`.to_i
rectangle[:right] = `#{self.element}.getBoundingClientRect().right`.to_i
rectangle[:bottom] = `#{self.element}.getBoundingClientRect().bottom`.to_i
# Review: @menu_item_height * self.entries.size : adjust for border, padding and margin (@menu_item_height * self.entries.size) + border_size ??
body_settings = {:top => "0px", :left => "0px", :width => "#{@menu_item_width}px", :height => "#{@menu_item_height * self.entries.size}px", :"z-index" => @layer}
if @submenu == true
case @zone
when :top, :"top-left", :"top-right"
if @orientation == :horizontal
body_settings[:top] = "#{(rectangle[:bottom] + 2)}px"
body_settings[:left] = "#{rectangle[:left] + @menu_item_width + 2}px"
else
body_settings[:top] = "#{(rectangle[:bottom] + 2)}px"
body_settings[:left] = "#{rectangle[:left] + @menu_item_width + 2}px"
end
when :left
if @orientation == :horizontal
body_settings[:top] = "#{rectangle[:top]}px"
body_settings[:left] = "#{(rectangle[:right] + 2)}px"
else
body_settings[:top] = "#{rectangle[:top]}px"
body_settings[:left] = "#{(rectangle[:right] + 2)}px"
end
when :right
if @orientation == :horizontal
body_settings[:top] = "#{rectangle[:top]}px"
body_settings[:left] = "#{(rectangle[:left] - 2 - body_settings[:width])}px"
else
body_settings[:top] = "#{rectangle[:top]}px"
body_settings[:left] = "#{(rectangle[:left] - 2 - body_settings[:width])}px"
end
when :bottom, :"bottom-left", :"bottom-right"
if @orientation == :horizontal
body_settings[:top] = "#{(rectangle[:top] - 2 - body_settings[:height])}px"
body_settings[:left] = "#{rectangle[:left]}px"
else
body_settings[:top] = "#{(rectangle[:top] - 2 - body_settings[:height])}px"
body_settings[:left] = "#{rectangle[:left]}px"
end
end
else
case @zone
when :top, :"top-left", :"top-right"
if @orientation == :horizontal
body_settings[:top] = "#{(rectangle[:bottom] + 2)}px"
body_settings[:left] = "#{rectangle[:left]}px"
else
body_settings[:top] = "#{(rectangle[:bottom] + 2)}px"
body_settings[:left] = "#{rectangle[:left]}px"
end
when :left
if @orientation == :horizontal
body_settings[:top] = "#{rectangle[:top]}px"
body_settings[:left] = "#{(rectangle[:right] + 2)}px"
else
body_settings[:top] = "#{rectangle[:top]}px"
body_settings[:left] = "#{(rectangle[:right] + 2)}px"
end
when :right
if @orientation == :horizontal
body_settings[:top] = "#{rectangle[:top]}px"
body_settings[:left] = "#{(rectangle[:left] - 2 - body_settings[:width])}px"
else
body_settings[:top] = "#{rectangle[:top]}px"
body_settings[:left] = "#{(rectangle[:left] - 2 - body_settings[:width])}px"
end
when :bottom, :"bottom-left", :"bottom-right"
if @orientation == :horizontal
body_settings[:top] = "#{(rectangle[:top] - 2 - body_settings[:height])}px"
body_settings[:left] = "#{rectangle[:left]}px"
else
body_settings[:top] = "#{(rectangle[:top] - 2 - body_settings[:height])}px"
body_settings[:left] = "#{rectangle[:left]}px"
end
end
end
@body.gxg_merge_style(body_settings)
#
self.entries.each do |the_child|
the_child.update_appearance
end
end
# Construction:
def cascade()
build_list = []
#
left_expander = nil
right_expander = nil
if @submenu == true
case @zone
when :top
if @orientation == :horizontal
right_expander = {:component=>"image", :options=>{:src => self.theme_widget("expand_bottom.svg"), :width=>32, :height=>32, :style => {:clear => "both"}}, :content=>[], :script=>""}
else
right_expander = {:component=>"image", :options=>{:src => self.theme_widget("expand_right.svg"), :width=>32, :height=>32, :style => {:clear => "both"}}, :content=>[], :script=>""}
end
when :left, :"top-left", :"bottom-left"
right_expander = {:component=>"image", :options=>{:src => self.theme_widget("expand_right.svg"), :width=>32, :height=>32, :style => {:clear => "both"}}, :content=>[], :script=>""}
when :right, :"top-right", :"bottom-right"
left_expander = {:component=>"image", :options=>{:src => self.theme_widget("expand_left.svg"), :width=>32, :height=>32, :style => {:clear => "both"}}, :content=>[], :script=>""}
when :bottom
if @orientation == :horizontal
right_expander = {:component=>"image", :options=>{:src => self.theme_widget("expand_top.svg"), :width=>32, :height=>32, :style => {:clear => "both"}}, :content=>[], :script=>""}
else
right_expander = {:component=>"image", :options=>{:src => self.theme_widget("expand_right.svg"), :width=>32, :height=>32, :style => {:clear => "both"}}, :content=>[], :script=>""}
end
end
end
#
the_icon = nil
if @icon
the_icon = {:component=>"image", :options=>{:src=>@icon, :width=>@icon_width, :height=>@icon_height, :style => {:clear => "both"}}, :content=>[], :script=>""}
end
#
the_label = nil
if @label
the_label = {:component=>"label", :options=>{:content => @label, :style => {:padding => "0px 0px 2px 0px", :"font-size" => "16px"}}, :content=>[], :script=>""}
end
#
the_key = nil
if @key
the_key = {:component=>"label", :options=>{:content => @key, :style => {:padding => "0px 0px 2px 0px", :"font-size" => "16px"}}, :content=>[], :script=>""}
end
# grid structure vertical-align:middle;
left_expander_cell = {:component=>"block_table_cell", :options=>{:style => {:float => "left", :padding => "0px", :margin => "0px"}}, :content=>[(left_expander)], :script=>""}
right_expander_cell = {:component=>"block_table_cell", :options=>{:style => {:float => "right", :padding => "0px", :margin => "0px"}}, :content=>[(right_expander)], :script=>""}
icon_cell = {:component=>"block_table_cell", :options=>{:style => {:float => "left", :padding => "0px", :margin => "0px", :'vertical-align' => 'middle'}}, :content=>[(the_icon)], :script=>""}
label_cell = {:component=>"block_table_cell", :options=>{:style => {:float => "left", :padding => "0px 0px 0px 5px", :margin => "0px", :'vertical-align' => 'middle'}}, :content=>[(the_label)], :script=>""}
key_cell = {:component=>"block_table_cell", :options=>{:style => {:float => "right", :padding => "0px", :margin => "0px", :'vertical-align' => 'middle'}}, :content=>[(the_key)], :script=>""}
entry_row = {:component=>"block_table_row", :options=>{:style => {:clear => "both", :padding => "0px", :margin => "0px"}}, :content=>[], :script=>""}
#
if left_expander
entry_row[:content] << left_expander_cell
end
#
if the_icon
entry_row[:content] << icon_cell
end
#
if the_label
entry_row[:content] << label_cell
end
#
if the_key
entry_row[:content] << key_cell
end
#
if right_expander
entry_row[:content] << right_expander_cell
end
#
entry_table = {:component=>"block_table", :options=>{:style => {:padding => "0px", :margin => "0px"}}, :content=>[(entry_row)], :script=>""}
#
build_list << entry_table
self.build_interior_components(build_list)
#
GxG::DISPATCHER.post_event do
self.update_appearance
end
#
true
end
end
#
class MenuBar < ::GxG::Gui::BlockTable
def initialize(the_name,the_class,the_options)
@zone = (the_options.delete(:zone) || "top").to_s.to_sym
case @zone.clone
when :top
@orientation = (the_options.delete(:orientation) || "horizontal").to_s.to_sym
when :bottom
@orientation = (the_options.delete(:orientation) || "horizontal").to_s.to_sym
when :left, :"top-left", :"bottom-left"
@orientation = (the_options.delete(:orientation) || "vertical").to_s.to_sym
when :right, :"top-right", :"bottom-right"
@orientation = (the_options.delete(:orientation) || "vertical").to_s.to_sym
else
@orientation = (the_options.delete(:orientation) || "horizontal").to_s.to_sym
end
@menu_registry = {}
super(the_name, the_class, the_options)
end
#
def register_entry(the_entry=nil)
if the_entry.is_any?(::GxG::Gui::MenuEntry, ::GxG::Gui::MenuItem)
@menu_registry[(the_entry.uuid)] = the_entry
true
else
false
end
end
#
def unregister_entry(the_reference=nil)
if the_reference.is_any?(::GxG::Gui::MenuEntry, ::GxG::Gui::MenuItem)
@menu_registry.delete(the_reference.uuid)
true
else
if the_reference.is_any?(::String, ::Symbol)
if GxG::valid_uuid?(the_reference)
@menu_registry.delete(the_reference.to_sym)
true
else
@menu_registry.values.each do |the_object|
if the_object.title == the_reference
@menu_registry.delete(the_object.uuid)
break
end
end
true
end
else
false
end
end
end
#
def find_entry(the_reference=nil)
result = nil
if the_reference.is_any?(::String, ::Symbol)
if GxG::valid_uuid?(the_reference)
result = @menu_registry[(the_reference.to_sym)]
else
@menu_registry.values.each do |the_object|
if the_object.title == the_reference
result = the_object
break
end
end
end
end
result
end
# Slight of hand:
alias :original_add_child :add_child
def add_child(name, element_class, options={})
the_entry = nil
if element_class == GxG::Gui::MenuEntry
if @orientation == :horizontal
#
the_row = self.find_child_type(::GxG::Gui::BlockTableRow)
unless the_row
the_uuid = GxG::uuid_generate.to_sym
the_title = "Untitled Component #{the_uuid}"
the_row = self.original_add_child(the_uuid, ::GxG::Gui::BlockTableRow, {:uuid => the_uuid})
the_row.set_title(the_title)
GxG::DISPLAY_DETAILS[:object].register_object(the_title, the_row)
end
if the_row
the_uuid = GxG::uuid_generate.to_sym
the_title = "Untitled Component #{the_uuid}"
the_cell = the_row.add_child(the_uuid, ::GxG::Gui::BlockTableCell, {:uuid => the_uuid})
the_cell.set_title(the_title)
GxG::DISPLAY_DETAILS[:object].register_object(the_title, the_cell)
if the_cell
the_entry = the_cell.add_child(name, element_class, {:orientation => @orientation, :zone => @zone, :parent => self, :menu => self}.merge(options))
# Review : MenuEntry too short issue
the_entry.update_appearance()
else
# err - cannot proceed.
end
else
# err - cannot proceed.
end
else
the_uuid = GxG::uuid_generate.to_sym
the_title = "Untitled Component #{the_uuid}"
the_row = self.original_add_child(the_uuid, ::GxG::Gui::BlockTableRow, {:uuid => the_uuid})
the_row.set_title(the_title)
GxG::DISPLAY_DETAILS[:object].register_object(the_title, the_row)
if the_row
the_uuid = GxG::uuid_generate.to_sym
the_title = "Untitled Component #{the_uuid}"
the_cell = the_row.add_child(the_uuid, ::GxG::Gui::BlockTableCell, {:uuid => the_uuid})
the_cell.set_title(the_title)
GxG::DISPLAY_DETAILS[:object].register_object(the_title, the_cell)
if the_cell
the_entry = the_cell.add_child(name, element_class, {:orientation => @orientation, :zone => @zone, :parent => self, :menu => self}.merge(options))
# Review : MenuEntry too short issue
the_entry.update_appearance()
else
# err - cannot proceed.
end
else
# err - cannot proceed.
end
end
else
# the_entry = self.original_add_child(name, element_class, options)
log_warn("MenuBar only accepts MenuEntry as children, not: #{element_class.inspect} --> Other errors will likely follow.")
end
if the_entry
self.register_entry(the_entry)
end
the_entry
end
#
def entries()
results = []
search_pool = self.children.values
while search_pool.size > 0 do
entry = search_pool.shift
if entry
if entry.is_a?(::GxG::Gui::MenuEntry)
results << entry
else
unless entry.is_a?(::GxG::Gui::MenuItem)
entry.children.values.each do |the_child|
search_pool << the_child
end
end
end
end
end
results
end
#
def collapse_down()
self.entries.each do |the_child|
the_child.collapse_down
end
end
#
def update_appearance()
self.entries.each do |the_child|
the_child.update_appearance
end
end
#
def select_item(the_menu_item=nil)
log_warn("Override this method in object script")
end
end
# Tabbed Regions Support
class TabSet < ::GxG::Gui::BlockTable
# OK - basic idea:
# Row0: tab labels in cells. :mouseup = hide others, show only designated Tab object as body.
# Row1: Tab content objects
def initialize(the_name,the_class,the_options)
# tab show/hide :transitions:
@transition_show = nil
@transition_hide = nil
the_transitions = the_options.delete(:transition)
if the_transitions.is_a?(::Hash)
if the_transitions[:show].is_a?(::Hash)
@transition_show = the_transitions[:show]
end
if the_transitions[:hide].is_a?(::Hash)
@transition_hide = the_transitions[:hide]
end
end
#
@tabs = {}
@tab_row = nil
@content_row = nil
#
super(the_name, the_class, the_options)
end
# Slight of hand:
alias :original_add_child :add_child
def add_child(name, element_class, options={})
result = nil
# Only accept ::GxG::Gui::Tab items as children
if element_class == ::GxG::Gui::Tab
if @tab_row && @content_row
# LabelCell
label_cell_uuid = ::GxG::uuid_generate.to_sym
label_cell_title = "Untitled Component #{label_cell_uuid.to_s}"
# Re-distribute :width of each label over total width:
label_cell = @tab_row.add_child(label_cell_uuid, ::GxG::Gui::BlockTableCell, {:uuid => label_cell_uuid})
label_cell.set_title(label_cell_title)
# GxG::DISPLAY_DETAILS[:object].register_object(label_cell_title, label_cell) ??
# ContentCell
content_cell_uuid = ::GxG::uuid_generate.to_sym
content_cell_title = "Untitled Component #{content_cell_uuid.to_s}"
content_cell = @content_row.add_child(content_cell_uuid, ::GxG::Gui::BlockTableCell, {:uuid => content_cell_uuid})
content_cell.set_title(content_cell_title)
# GxG::DISPLAY_DETAILS[:object].register_object(content_cell_title, content_cell) ??
content_cell.hide
# TabItem
# Needs: :tabset => obj, :label_cell => obj, :content_cell => obj
the_tab_item = label_cell.add_child(name, element_class, (options || {}).merge({:tabset => self, :label_cell => label_cell, :content_cell => content_cell}))
if the_tab_item
@tabs[(name.to_s.to_sym)] = the_tab_item
result = the_tab_item
# Review: where else to put this??
if @tabs.keys.size > 1
total_width = (`#{self.element}.getBoundingClientRect().right`.to_i - `#{self.element}.getBoundingClientRect().left`.to_i).to_f
the_width = "#{((total_width / @tabs.keys.size.to_f) / total_width) * 100.0}%"
else
the_width = "100.0%"
end
@tabs.values.each do |the_tab|
the_tab.width = the_width
end
# GxG::DISPLAY_DETAILS[:object].register_object(name, the_tab_item) (already done by build_components ??)
end
end
#
end
result
end
#
alias :original_children :children
def children()
if @tabs
@tabs
else
{}
end
end
#
def find_child(the_reference=nil, interior=false)
result = nil
if GxG::valid_uuid?(the_reference)
uuid_check = true
else
uuid_check = false
end
if interior == true
search_queue = self.original_children.values
else
search_queue = self.children.values
end
while search_queue.size > 0 do
entry = search_queue.shift
if entry
if uuid_check
if entry.uuid == the_reference
result = entry
break
end
else
if entry.title == the_reference
result = entry
break
end
end
unless result
entry.children.values.each do |the_child|
search_queue << the_child
end
end
end
end
result
end
#
def size()
@tabs.keys.size
end
#
def reference_tab(specifier=nil)
result = nil
check = nil
if GxG::valid_uuid?(specifier)
check = :uuid
else
if specifier.is_any?(::String, ::Symbol)
check = :reference
else
if specifier.is_a?(::GxG::Gui::Tab)
check = :instance
else
if specifier.is_a?(::Numeric)
check = :index
end
end
end
end
case check
when :uuid
@tabs.each_pair do |the_ref, the_instance|
if the_instance.uuid.to_s.to_sym == specifier.to_s.to_sym
result = @tabs[(the_ref)]
break
end
end
when :reference
result = @tabs[(specifier.to_s.to_sym)]
when :index
result = @tabs[(@tabs.keys[(specifier.to_i)])]
when :instance
@tabs.each_pair do |the_ref, the_instance|
if the_instance == specifier
result = @tabs[(the_ref)]
break
end
end
end
result
end
#
def detach_tab(specifier=nil)
# This should only be called from a Tab instance.
result = nil
check = nil
if GxG::valid_uuid?(specifier)
check = :uuid
else
if specifier.is_any?(::String, ::Symbol)
check = :reference
else
if specifier.is_a?(::GxG::Gui::Tab)
check = :instance
else
if specifier.is_a?(::Numeric)
check = :index
end
end
end
end
case check
when :uuid
@tabs.each_pair do |the_ref, the_instance|
if the_instance.uuid.to_s.to_sym == specifier.to_s.to_sym
result = @tabs.delete(the_ref)
break
end
end
when :reference
result = @tabs.delete(specifier.to_s.to_sym)
when :index
result = @tabs.delete(@tabs.keys[(specifier.to_i)])
when :instance
@tabs.each_pair do |the_ref, the_instance|
if the_instance == specifier
result = @tabs.delete(the_ref)
break
end
end
end
result
end
#
def cascade()
build_list = []
tab_row_id = "Tabs Row"
content_row_id = "Content Row"
#
tab_row = {:component=>"block_table_row", :options=>{:title => tab_row_id, :style => {:clear => "both", :padding => "0px", :margin => "0px", :width => "100%", :height => "32px", :"background-color" => "transparent"}}, :content=>[], :script=>""}
tab_table = {:component=>"block_table", :options=>{:style => {:clear => "both", :padding => "0px", :margin => "0px", :width => "100%", :height => "32px", :"background-color" => "transparent"}}, :content=>[(tab_row)], :script=>""}
tab_area_cell = {:component=>"block_table_cell", :options=>{:style => {:float => "left", :padding => "0px", :margin => "0px", :'vertical-align' => 'middle', :"background-color" => "transparent"}}, :content=>[(tab_table)], :script=>""}
tab_area_row = {:component=>"block_table_row", :options=>{:style => {:clear => "both", :padding => "0px", :margin => "0px", :width => "100%", :height => "32px", :"background-color" => "transparent"}}, :content=>[(tab_area_cell)], :script=>""}
#
content_row = {:component=>"block_table_row", :options=>{:title => content_row_id, :style => {:clear => "both", :padding => "0px", :margin => "0px", :width => "100%", :height => "100%"}}, :content=>[], :script=>""}
content_table = {:component=>"block_table", :options=>{:style => {:clear => "both", :padding => "0px", :margin => "0px", :width => "100%", :height => "100%"}}, :content=>[(content_row)], :script=>""}
content_area_cell = {:component=>"block_table_cell", :options=>{:style => {:float => "left", :padding => "0px", :margin => "0px", :'vertical-align' => 'middle'}}, :content=>[(content_table)], :script=>""}
content_area_row = {:component=>"block_table_row", :options=>{:style => {:clear => "both", :padding => "0px", :margin => "0px", :width => "100%", :height => "100%"}}, :content=>[(content_area_cell)], :script=>""}
#
build_list << tab_area_row
build_list << content_area_row
#
self.build_interior_components(build_list)
#
@tab_row = self.find_child(tab_row_id, true)
@content_row = self.find_child(content_row_id, true)
true
end
# Selection
def select_tab(specifier=nil,&block)
the_tab = self.reference_tab(specifier)
if the_tab
@tabs.values.each do |a_tab|
if a_tab.visible? && (a_tab != the_tab)
a_tab.hide(@transition_hide) do
unless the_tab.visible?
the_tab.show(@transition_show,&block)
end
end
else
if a_tab == the_tab
unless the_tab.visible?
the_tab.show(@transition_show,&block)
end
end
end
end
end
end
#
end
#
class Tab < ::GxG::Gui::BlockTable
# goes within TabSet only
def initialize(the_name,the_class,the_options)
# {:tabset => self, :label_object => label_cell, :content_object => content_cell}
@tabset = the_options.delete(:tabset)
@label_area = the_options.delete(:label_cell)
@content_area = the_options.delete(:content_cell)
unless (@tabset && @label_area && @content_area)
raise Exception, "Improperly initialized Tab object: create by adding to a TabSet."
end
if the_options.delete(:closeable) == true
@closeable = true
else
@closeable = false
end
if the_options.delete(:enabled) == false
@enabled = false
else
@enabled = true
end
if the_options[:content].is_a?(::String)
@label_text = the_options.delete(:content)
else
@label_text = "Untitled"
end
@label_object = nil
@tab_height = 24
@tab_width = "100%"
@visible = false
#
super(the_name, the_class, the_options)
end
#
def destroy()
if @tabset
if @tabset.detach_tab(self)
if @content_area
@content_area.hide()
@content_area.destroy()
@content_area = nil
end
if @label_area
@label_area.hide()
@label_area.destroy()
@label_area = nil
end
@tabset = nil
end
end
end
#
alias :original_children :children
def children()
if @content_area
@content_area.children()
else
{}
end
end
#
def find_child(the_reference=nil, interior=false)
result = nil
if GxG::valid_uuid?(the_reference)
uuid_check = true
else
uuid_check = false
end
if interior == true
search_queue = self.original_children.values
else
search_queue = self.children.values
end
while search_queue.size > 0 do
entry = search_queue.shift
if entry
if uuid_check
if entry.uuid == the_reference
result = entry
break
end
else
if entry.title == the_reference
result = entry
break
end
end
unless result
entry.children.values.each do |the_child|
search_queue << the_child
end
end
end
end
result
end
#
def cascade()
build_list = []
# Build up Label
tab_label_row = {:component=>"block_table_row", :options=>{:style => {:clear => "both", :padding => "0px", :margin => "0px", :width => "100%", :height => "100%"}}, :content=>[], :script=>""}
# Close Box
if @closeable == true
close_box = {:component=>"image", :options=>{:src => theme_widget("close.png"), :width=>20, :height=>20, :style => {:padding => "2px"}}, :content=>[], :script=>""}
close_box[:script] = "
on(:mouseup) do |event|
`alert('Closebox Selected')`
end
on(:mousedown) do |event|
unless self.gxg_get_state(:disabled) == true
self.gxg_set_attribute(:src,theme_widget('close_mousedown.png'))
end
end
on(:mouseenter) do |event|
unless self.gxg_get_state(:disabled) == true
self.gxg_set_attribute(:src,theme_widget('close_mouseenter.png'))
end
end
on(:mouseleave) do |event|
unless self.gxg_get_state(:disabled) == true
self.gxg_set_attribute(:src,theme_widget('close.png'))
end
end
"
tab_close_cell = {:component=>"block_table_cell", :options=>{:style => {:float => "left", :padding => "0px", :margin => "0px", :'vertical-align' => 'middle'}}, :content=>[(close_box)], :script=>""}
tab_label_row[:content] << tab_close_cell
end
# Label
# label_obj_id = ::GxG::uuid_generate()
the_label = {:component=>"label", :options=>{:title => "Tab Label Object", :content => @label_text, :style => {:width => "100%", :"font-size" => "16px", :"text-align" => "center"}}, :content=>[], :script=>""}
unless @enabled == true
the_label[:options][:style][:"font-color"] = "#c2c2c2"
end
the_label[:script] = "
on(:mouseup) do |event|
`alert('Label Selected')`
end
"
tab_label_cell = {:component=>"block_table_cell", :options=>{:style => {:float => "left", :padding => "0px", :margin => "0px", :'vertical-align' => 'middle', :overflow => "hidden"}}, :content=>[(the_label)], :script=>""}
tab_label_row[:content] << tab_label_cell
tab_label_row[:script] = "
on(:mouseup) do |event|
`alert('Row Selected')`
end
"
#
build_list << tab_label_row
#
self.gxg_merge_style({:clear => "both", :padding => "0px", :margin => "0px", :width => "100%", :height => "#{@tab_height}px", :"background-color" => "#f2f2f2", :"border-radius" => "5px 5px 0px 0px", :overflow => "hidden", :"border-bottom" => "1px solid #c2c2c2"})
#
self.build_interior_components(build_list)
#
@label_object = self.find_child("Tab Label Object",true)
true
end
# Visuals:
def width()
if @label_area
@tab_width
end
end
def width=(the_width=nil)
if @label_area && the_width
# set/get width of @label_area (a cell) to control tab width. ????
if the_width.is_a?(::Numeric)
the_width = "#{the_width}px"
end
@tab_width = the_width
@label_area.gxg_merge_style({:width => @tab_width})
end
end
#
def visible?()
@visible
end
#
def show(transition=nil, &block)
self.gxg_merge_style({:"border-bottom" => "none"})
@visible = true
@content_area.show(transition,&block)
end
#
def hide(transition=nil, &block)
@content_area.hide(transition) do
if block.respond_to?(:call)
block.call()
end
self.gxg_merge_style({:"border-bottom" => "1px solid #c2c2c2"})
@visible = false
end
end
# State:
def enabled?()
@enabled
end
def enable()
@enabled = true
@label_object.gxg_merge_style({:"font-color" => "#000000"})
end
def disable()
@enabled = false
@label_object.gxg_merge_style({:"font-color" => "#c2c2c2"})
end
# Selection
def selected?()
self.visible?
end
def select()
if @enabled == true
@tabset.select_tab(self)
end
end
# Methods to override:
def before_open(options={})
end
def open(options={})
end
def after_open(options={})
end
def before_close(options={})
end
def close(options={})
end
def after_close(options={})
end
#
end
#
class Variable < ::Ferro::Element::Var
#
def _before_create
if @options[:uuid]
@uuid = @options.delete(:uuid).to_s.to_sym
end
super()
end
#
def uuid()
@uuid
end
# Creates any kind of element depending on option :domtype.
end
# ### Compound Items
#
class SearchForm < ::Ferro::Combo::Search
#
def _before_create
if @options[:uuid]
@uuid = @options.delete(:uuid).to_s.to_sym
end
super()
end
#
def uuid()
@uuid
end
# # Override this method to specify what happens when
# submit button is clicked or enter key is pressed.
#
# @param [String] value The value of the text input.
# def submitted(value);end
include GxG::Gui::AnimationSupport
include GxG::Gui::ApplicationSupport
include GxG::Gui::ThemeSupport
end
#
class PopUpMenu < ::Ferro::Combo::PullDown
#
def _before_create
if @options[:uuid]
@uuid = @options.delete(:uuid).to_s.to_sym
end
super()
end
#
def uuid()
@uuid
end
# Creates a simple pull-down menu.
# Specify option :title to set the menu title text.
# Specify option :items as an Array of Ferro classes.
# Each class should be something clickable, for instance a
# FormBlock. These classes will be instanciated by
# this element.
include GxG::Gui::AnimationSupport
include GxG::Gui::ApplicationSupport
include GxG::Gui::ThemeSupport
end
# -----------------------------------------------------------------------------
COMPONENT_CLASSES = {
:header => ::GxG::Gui::Header,
:navigator => ::GxG::Gui::Navigator,
:section => ::GxG::Gui::Section,
:article => ::GxG::Gui::Article,
:aside => ::GxG::Gui::Aside,
:footer => ::GxG::Gui::Footer,
:form => ::GxG::Gui::Form,
:clickable => ::GxG::Gui::Clickable,
:fieldset => ::GxG::Gui::Fieldset,
:text_input => ::GxG::Gui::TextInput,
:password_input => ::GxG::Gui::PasswordInput,
:reset_input => ::GxG::Gui::ResetInput,
:radio_button => ::GxG::Gui::RadioButton,
:color_picker => ::GxG::Gui::ColorPicker,
:date_picker => ::GxG::Gui::DatePicker,
:datetime_local => ::GxG::Gui::DateTimeLocal,
:email_input => ::GxG::Gui::EmailInput,
:month_picker => ::GxG::Gui::MonthPicker,
:number_input =>::GxG::Gui::NumberInput,
:range_input => ::GxG::Gui::RangeInput,
:search_input => ::GxG::Gui::SearchInput,
:phone_input => ::GxG::Gui::PhoneInput,
:time_picker => ::GxG::Gui::TimePicker,
:url_input => ::GxG::Gui::UrlInput,
:week_picker => ::GxG::Gui::WeekPicker,
:label => ::GxG::Gui::Label,
:text_area => ::GxG::Gui::TextArea,
:output => ::GxG::Gui::Output,
:button_input => ::GxG::Gui::ButtonInput,
:submit_button => ::GxG::Gui::SubmitButton,
:click_block => ::GxG::Gui::ClickBlock,
:checkbox => ::GxG::Gui::CheckBox,
:selector => ::GxG::Gui::Selector,
:block => ::GxG::Gui::Block,
:text => ::GxG::Gui::Text,
:list => ::GxG::Gui::List,
:ordered_list => ::GxG::Gui::OrderedList,
:list_item => ::GxG::Gui::ListItem,
:anchor => ::GxG::Gui::Anchor,
:external_link => ::GxG::Gui::ExternalLink,
:button => ::GxG::Gui::Button,
:image => ::GxG::Gui::Image,
:video => ::GxG::Gui::Video,
:canvas => ::GxG::Gui::Canvas,
:script => ::GxG::Gui::Script,
:application_viewport => ::GxG::Gui::ApplicationViewport,
:search_form => ::GxG::Gui::SearchForm,
:popupmenu => ::GxG::Gui::PopUpMenu,
:table => ::GxG::Gui::Table,
:table_header => ::GxG::Gui::TableHeader,
:table_row => ::GxG::Gui::TableRow,
:table_cell => ::GxG::Gui::TableCell,
:block_table => ::GxG::Gui::BlockTable,
:block_table_header => ::GxG::Gui::BlockTableHeader,
:block_table_row => ::GxG::Gui::BlockTableRow,
:block_table_cell => ::GxG::Gui::BlockTableCell,
:window => ::GxG::Gui::Window,
:dialog_box => ::GxG::Gui::DialogBox,
:panel => ::GxG::Gui::Panel,
:tree => ::GxG::Gui::Tree,
:tree_node => ::GxG::Gui::TreeNode,
:menu_bar => ::GxG::Gui::MenuBar,
:menu_entry => ::GxG::Gui::MenuEntry,
:menu_item => ::GxG::Gui::MenuItem,
:tabset => ::GxG::Gui::TabSet,
:tab => ::GxG::Gui::Tab
}
#
def self.component_class(the_type_specifier=nil)
result = nil
if the_type_specifier
result = GxG::Gui::COMPONENT_CLASSES[(the_type_specifier.to_s.to_sym)]
end
result
end
#
def self.class_component(the_class_specifier=nil)
result = nil
if the_class_specifier
found_index = GxG::Gui::COMPONENT_CLASSES.values.index(the_class_specifier)
if found_index
result = GxG::Gui::COMPONENT_CLASSES.keys[(found_index)]
end
end
result
end
#
def self.component_registered?(the_specifier=nil)
if GxG::Gui::component_class(the_specifier)
true
else
false
end
end
#
def self.register_component_class(the_specifier=nil, the_constant=nil)
if the_specifier.is_any?(::String, ::Symbol) && the_constant.is_a?(::Class)
if GxG::Gui::component_registered(the_specifier.to_s.to_sym)
if GxG::Gui::component_class(the_specifier.to_s.to_sym) != the_constant
GxG::Gui::COMPONENT_CLASSES[(the_specifier.to_s.to_sym)] = the_constant
true
else
log_warn("Component Class #{the_specifier.to_s.to_sym.inspect} => #{the_constant.inspect} already defined.")
false
end
else
GxG::Gui::COMPONENT_CLASSES[(the_specifier.to_s.to_sym)] = the_constant
true
end
else
false
end
end
# Set the valid component classes for animations:
class Animation
# This is intentional - keep this modification (!)
def self.valid_components()
::GxG::Gui::COMPONENT_CLASSES.values
end
end
#
# --------------------------------------------------------------------------------------------------------------------------------------
#
class Page < Ferro::Document
#
# Override in your Page Script:
def run(data=nil)
end
#
include GxG::Gui::EventHandlers
#
def element()
`document.body`
end
#
def find_object_by_id(the_id=nil)
result = nil
if the_id.is_a?(::Numeric)
the_id = ("_" + the_id.to_i.to_s())
else
the_id = the_id.to_s
end
search_queue = [(self)]
while search_queue.size > 0 do
entry = search_queue.shift
attrib = `#{entry.element()}.attributes.getNamedItem('id') || 0`
unless `#{attrib} == 0`
attrib = `#{attrib}.value`
if `(#{attrib.to_s} == #{the_id.to_s})`
result = entry
break
end
end
if entry.respond_to?(:children)
entry.children.keys.each do |the_child_key|
attrib = `#{entry.children[(the_child_key)].element()}.attributes.getNamedItem('id') || 0`
unless `#{attrib} == 0`
attrib = `#{attrib}.value`
if `(#{attrib.to_s} == #{the_id.to_s})`
result = entry.children[(the_child_key)]
break
else
search_queue << entry.children[(the_child_key)]
end
end
end
end
end
result
end
#
def find_object(object_name=nil)
result = nil
search_queue = [(self)]
while search_queue.size > 0 do
entry = search_queue.shift
if entry.respond_to?(:children)
if entry.children[(object_name)]
result = entry.children[(object_name)]
break
else
entry.children.keys.each do |the_child|
search_queue << entry.children[(the_child)]
end
end
end
end
result
end
#
def find_object_by_title(the_title=nil)
result = nil
if GxG::valid_uuid?(the_title)
uuid_check = true
else
uuid_check = false
end
@title_registry.keys.each do |the_uuid|
if uuid_check == true
if @title_registry[(the_uuid)].to_s.include?(the_title.to_s)
result = self.find_object(the_uuid)
break
end
else
if the_title == @title_registry[(the_uuid)]
result = self.find_object(the_uuid)
break
end
end
end
result
end
#
def register_object(the_title=nil, the_object=nil)
if the_title && the_object
@title_registry[(the_object.uuid)] = the_title.to_s
true
else
false
end
end
#
def unregister_object(the_title=nil)
if the_title
the_object = self.find_object_by_title(the_title)
if the_object
@title_registry.delete(the_object.uuid)
end
true
else
false
end
end
#
def initialize(the_display_path=nil)
super()
@busy = false
@theme = "default"
# Review : prepend @theme_prefix with the page 'relative_url' value (see GxG::CONNECTION)
@theme_prefix = (::GxG::CONNECTION.relative_url() + "/themes/default")
@window_switcher = nil
@window_registry = {}
@dialog = nil
@dialog_queue = []
@title_registry = {}
@style_data = {}
@layout = {:"top-left" => {}, :top => {}, :"top-right" => {}, :right => {}, :"bottom-right" => {}, :bottom => {}, :"bottom-left" => {}, :left => {}}
@layout_content_area = {:top => 0, :left => 0, :bottom => `window.innerHeight`.to_i, :right => `window.innerWidth`.to_i}
@uuid = nil
@display_path = the_display_path
# Supports windowing:
on(:mousemove) do |event|
if event
GxG::DISPLAY_DETAILS[:mouse_x] = event[:pageX]
GxG::DISPLAY_DETAILS[:mouse_y] = event[:pageY]
GxG::DISPLAY_DETAILS[:mousedown] = ((event[:buttons] & 1) == 1)
end
end
#
::GxG::DISPATCHER.post_event(:display) do
if GxG::CONNECTION.open?
::GxG::DISPLAY_DETAILS[:object].start(`window.location.pathname`)
end
end
#
self
end
#
def uuid()
@uuid
end
# Busy/Unbusy Supports:
def busy()
@busy
end
def set_busy(state=false)
if state == true
unless @busy == true
busy_overlay = {:component => "block", :options => {:title => "gxg_wait_overlay", :style => {:cursor => "wait", :opacity => 0.01, :"background-color" => "#ffffff", :position => "absolute", :top => "0px", :left => "0px", :width => "100%", :height => "100%", :"z-index" => 9999}}, :content => [], :script => ""}
busy_overlay[:script] = "
on(:mouseup,true) do |event|
end
on(:mousedown,true) do |event|
end
on(:mousemove,true) do |event|
end
on(:mouseenter,true) do |event|
end
on(:mouseleave,true) do |event|
end
"
self.build_components([{:parent => nil, :record => {:content => [(busy_overlay)]}, :element => self}])
@busy = true
end
else
if @busy == true
busy_overlay = self.find_object_by_title("gxg_wait_overlay")
if busy_overlay
self.unregister_object("gxg_wait_overlay")
busy_overlay.destroy
end
@busy = false
end
end
@busy
end
# Theme Supports:
def site_asset(asset_path=nil)
if (`window.location['href']`).include?("https://")
host_prefix = ("https://" + `window.location['host']`)
else
host_prefix = ("http://" + `window.location['host']`)
end
if ::GxG::CONNECTION.relative_url()[0] != "/"
host_prefix = (host_prefix + "/")
end
if asset_path
(host_prefix + File.expand_path((::GxG::CONNECTION.relative_url() + "/" + asset_path.to_s)))
else
""
end
end
#
def theme_icon(image_name=nil)
if image_name
(@theme_prefix + "/icons/" + image_name.to_s)
else
""
end
end
#
def theme_background(image_name=nil)
if image_name
(@theme_prefix + "/backgrounds/" + image_name.to_s)
else
""
end
end
#
def theme_widget(image_name=nil)
if image_name
(@theme_prefix + "/widgets/" + image_name.to_s)
else
""
end
end
#
def theme_font(font_name=nil)
if font_name
(@theme_prefix + "/fonts/" + font_name.to_s)
else
""
end
end
#
def theme_window_title_layout()
{:left => [:close, :minimize, :maximize], :center => [:title], :right => []}
end
# Layout System Supports:
def 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|
@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)
@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|
@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
@layout_content_area = {:top => 0, :left => 0, :bottom => page_height, :right => page_width, :page_width => page_width, :page_height => page_height}
@layout_content_area[:top] = (top_bound * page_height.to_f).to_i
@layout_content_area[:top_percent] = top_bound * 100.0
@layout_content_area[:left] = (left_bound * page_width.to_f).to_i
@layout_content_area[:left_percent] = left_bound * 100.0
@layout_content_area[:right] = (right_bound * page_width.to_f).to_i
@layout_content_area[:right_percent] = right_bound * 100.0
@layout_content_area[:bottom] = (bottom_bound * page_height.to_f).to_i
@layout_content_area[:bottom_percent] = bottom_bound * 100.0
true
end
#
def 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)
#
@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
self.layout_refresh
end
#
true
else
false
end
end
#
def layout_remove_item(the_uuid=nil)
[:"top-left", :top, :"top-right", :right, :"bottom-right", :bottom, :"bottom-left", :left].each do |the_position|
if @layout[(the_position.to_s.to_sym)][(the_uuid.to_s.to_sym)]
@layout[(the_position.to_s.to_sym)].delete((the_uuid.to_s.to_sym))
break
end
end
self.layout_refresh
#
true
end
#
def layout_content_area()
@layout_content_area
end
# Window Manager:
# Dialog Box supports:
def dialog_open(build_source=nil, the_application=nil, details=nil, &block)
if build_source.is_any?(::Hash, GxG::Database::PersistedHash)
if build_source.is_a?(GxG::Database::PersistedHash)
the_uuid = build_source.uuid
the_options = build_source[:options].unpersist
if the_options[:title]
the_title = the_options.delete(:title)
else
the_title = build_source.title
end
else
the_uuid = GxG::uuid_generate.to_sym
the_options = build_source[:options].clone
the_title = the_options.delete(:title)
end
the_style = the_options.delete(:style)
the_states = (the_options.delete(:states) || {})
new_window = GxG::DISPLAY_DETAILS[:object].add_child(the_uuid, GxG::Gui::DialogBox, the_options)
if new_window
new_window.set_title(the_title)
# Set states:
unless the_states.keys.include?(:hidden)
the_states[:hidden] = false
end
new_window.gxg_set_states(the_states)
# process style info:
if the_style.is_a?(::Hash)
new_window.gxg_set_style(the_style)
end
if the_application.is_a?(::GxG::Application)
new_window.set_application(the_application)
unless the_application.get_window(the_uuid)
the_application.link_window(new_window)
end
end
# Set details if provided
if details
new_window.set_data(details)
end
# Set Script if any is provided
if build_source[:script].size > 0
new_window.set_script(build_source[:script].to_s)
end
# Set Callback
if block.respond_to?(:call)
new_window.set_responder(block)
end
#
if @dialog.is_a?(::GxG::Gui::DialogBox)
@dialog_queue << new_window
self.register_object(the_title, new_window)
self.build_components([{:parent => GxG::DISPLAY_DETAILS[:object], :record => build_source, :element => new_window}],the_application)
#
else
@dialog = new_window
self.register_object(the_title, new_window)
self.build_components([{:parent => GxG::DISPLAY_DETAILS[:object], :record => build_source, :element => new_window}],the_application)
new_window.before_open()
new_window.open()
new_window.show({:origin => {:opacity => 0.0}, :destination => {:opacity => 1.0}})
new_window.after_open()
end
true
else
false
end
else
false
end
end
#
def dialog_close(options={})
if @dialog.is_a?(::GxG::Gui::DialogBox)
@dialog.before_close()
close_procedure = Proc.new do
@dialog.close()
@dialog.after_close()
@dialog.gxg_every_child do |the_child|
if the_child.is_a?(::GxG::Gui::ApplicationViewport)
the_application = the_child.application
if the_application
the_application.unlink_viewport(the_child.title)
end
end
end
self.unregister_object(@dialog.title)
@dialog.destroy
@dialog = nil
if @dialog_queue.size > 0
@dialog = @dialog_queue.shift
@dialog.before_open
@dialog.open
@dialog.show({:origin => {:opacity => 0}, :destination => {:opacity => 1}, :options => {:duration => 500}})
@dialog.after_open
end
end
if options[:origin] && options[:destination]
@dialog.hide(options, &close_procedure)
else
@dialog.hide({:origin => {:opacity => 1}, :destination => {:opacity => 0}, :options => {:duration => 500}}, &close_procedure)
end
end
true
end
# Choice Dialog
def open_dialog(the_application=nil, details=nil, &block)
# details: {:type => :choose, :title => "Some Title", :banner => "Some Text Here"}
self.layout_refresh()
# Determine Dialog Details:
window_title = (details || {:title => "Choose"})[:title].to_s
banner_text = (details || {:banner => "(Please provide some text here)"})[:banner].to_s
one_text = (details || {:one => ""})[:one].to_s
two_text = (details || {:two => ""})[:two].to_s
three_text = (details || {:three => ""})[:three].to_s
default_text = (details || {:default => ""})[:default].to_s
dialog_type = ((details || {:type => :choose})[:type] || :choose)
# Define bounds: (in px)
total_width = (self.layout_content_area()[:page_width].to_f * 0.25).to_i
form_padding = 20
form_margin = 5
inner_width = (total_width - ((form_padding + form_margin) * 2))
total_height = ((form_padding + form_margin) * 2)
#
banner_font_size = 16
if banner_text.to_s.size > 40
banner_height = ((banner_text.size.to_f / 40.0) * banner_font_size.to_f).round.to_i
else
banner_height = banner_font_size
end
input_font_size = 16
# Define parts:
#
banner = {:component=>"text", :options=> {:content => banner_text, :style => {:"font-size" => "#{banner_font_size}px", :width => "100%"}}, :content => [], :script => ""}
banner_cell = {:component => "block_table_cell", :options => {:style => {:padding => "0px", :margin => "0px", :width => "100%", :height => (banner_height), :"vertical-align" => "middle"}}, :content => [(banner)], :script => ""}
#
text_input = {:component=>"text_input", :options=>{:title => "data", :content => default_text, :style => {:"background-color" => "#f2f2f2", :"font-size" => "#{input_font_size}px", :width => "100%"}}, :content=>[], :script=>""}
text_input_cell = {:component => "block_table_cell", :options => {:style => {:padding => "0px", :margin => "0px", :width => "100%", :"vertical-align" => "middle"}}, :content => [(text_input)], :script => ""}
# Buttons:
one_btn = {:component=>"button", :options=>{:content => one_text, :style => {:padding => "2px", :width => "80px", :height => "32px"}}, :content=>[], :script=>""}
one_btn[:script] = "
on(:mouseup) do |event|
the_window = self.find_parent_type(::GxG::Gui::DialogBox)
if the_window
the_window.respond({:action => :one})
end
end
"
one_btn_cell = {:component => "block_table_cell", :options => {:style => {:padding => "0px", :margin => "0px", :width => "40%", :"vertical-align" => "middle"}}, :content => [(one_btn)], :script => ""}
#
two_btn = {:component=>"button", :options=>{:content => two_text, :style => {:padding => "2px", :width => "80px", :height => "32px"}}, :content=>[], :script=>""}
two_btn[:script] = "
on(:mouseup) do |event|
the_window = self.find_parent_type(::GxG::Gui::DialogBox)
if the_window
the_window.respond({:action => :two})
end
end
"
two_btn_cell = {:component => "block_table_cell", :options => {:style => {:padding => "0px", :margin => "0px", :width => "40%", :"vertical-align" => "middle"}}, :content => [(two_btn)], :script => ""}
#
three_btn = {:component=>"button", :options=>{:content => three_text, :style => {:padding => "2px", :width => "80px", :height => "32px"}}, :content=>[], :script=>""}
three_btn[:script] = "
on(:mouseup) do |event|
the_window = self.find_parent_type(::GxG::Gui::DialogBox)
if the_window
the_window.respond({:action => :three})
end
end
"
three_btn_cell = {:component => "block_table_cell", :options => {:style => {:padding => "0px", :margin => "0px", :width => "40%", :"vertical-align" => "middle"}}, :content => [(three_btn)], :script => ""}
#
cancel_btn = {:component=>"button", :options=>{:content => "Cancel", :style => {:padding => "2px", :width => "80px", :height => "32px"}}, :content=>[], :script=>""}
cancel_btn[:script] = "
on(:mouseup) do |event|
the_window = self.find_parent_type(::GxG::Gui::DialogBox)
if the_window
the_window.respond({:action => :cancel})
end
end
"
cancel_cell = {:component => "block_table_cell", :options => {:style => {:padding => "0px", :margin => "0px", :"vertical-align" => "middle"}}, :content => [(cancel_btn)], :script => ""}
#
# Define Containers:
# Rows:
row_one = {:component=>"block_table_row", :options=>{:style => {:padding => "0px", :margin => "0px", :width => "100%"}}, :content=>[(banner_cell)], :script=>""}
row_two = {:component=>"block_table_row", :options=>{:style => {:padding => "0px", :margin => "0px", :width => "100%"}}, :content=>[(text_input_cell)], :script=>""}
row_three = {:component=>"block_table_row", :options=>{:style => {:padding => "0px", :margin => "0px", :width => "100%"}}, :content=>[], :script=>""}
# Tables:
table_one = {:component=>"block_table", :options=>{:style => {:padding => "5px", :margin => "0px", :width => "100%"}}, :content=>[(row_one)], :script=>""}
table_two = {:component=>"block_table", :options=>{:style => {:padding => "5px", :margin => "0px", :width => "100%"}}, :content=>[(row_two)], :script=>""}
table_three = {:component=>"block_table", :options=>{:style => {:padding => "5px", :margin => "0px", :width => "100%"}}, :content=>[(row_three)], :script=>""}
#
# Form:
form = {:component=>"form", :options=>{:style => {:"background-color" => "#f2f2f2", :padding => "#{form_padding}px", :margin => "#{form_margin}px"}}, :content=>[], :script=>""}
#
if banner_text.size > 0
form[:content] << table_one
end
# Build form:
case dialog_type
when :choose
# no :cancel; :one, :two, and :three buttons
if one_text.size > 0
row_three[:content] << one_btn_cell
end
if two_text.size > 0
row_three[:content] << two_btn_cell
end
if three_text.size > 0
row_three[:content] << three_btn_cell
end
when :alert
# no cancel button
action_btn = {:component=>"button", :options=>{:content => "OK", :style => {:padding => "2px", :width => "80px", :height => "32px"}}, :content=>[], :script=>""}
action_btn[:script] = "
on(:mouseup) do |event|
the_window = self.find_parent_type(::GxG::Gui::DialogBox)
if the_window
the_window.respond({:action => :ok})
end
end
"
action_btn_cell = {:component => "block_table_cell", :options => {:style => {:padding => "0px", :margin => "0px"}}, :content => [(action_btn)], :script => ""}
#
row_three[:content] << action_btn_cell
when :input
# text input, cancel and submit buttons
row_three[:content] << cancel_cell
action_btn = {:component=>"button", :options=>{:content => "OK", :style => {:padding => "2px", :width => "80px", :height => "32px"}}, :content=>[], :script=>""}
action_btn[:script] = "
on(:mouseup) do |event|
the_window = self.find_parent_type(::GxG::Gui::DialogBox)
form = self.find_parent_type(:form)
if the_window && form
the_window.respond({:action => :ok, :form => form.form_data()})
end
end
"
action_btn_cell = {:component => "block_table_cell", :options => {:style => {:padding => "0px", :margin => "0px"}}, :content => [(action_btn)], :script => ""}
#
row_three[:content] << action_btn_cell
#
form[:content] << table_two
end
form[:content] << table_three
# Build Dialog
### Calculate dialog width & height from content:
#
form[:options][:style][:width] = "#{total_width}px"
### Set banner height and other settings: ????
if banner_text.to_s.size > 0
form[:content][0][:options][:style][:width] = "#{inner_width}px"
form[:content][0][:options][:style][:height] = "#{banner_height}px"
form[:content][0][:options][:style][:padding] = "10px 0px 10px 0px"
total_height += (banner_height + 20)
end
if dialog_type == :input
form[:content][1][:options][:style][:width] = "#{inner_width}px"
form[:content][1][:options][:style][:height] = "#{input_font_size}px"
form[:content][1][:options][:style][:padding] = "10px 0px 10px 0px"
total_height += (input_font_size + 20)
form[:content][2][:options][:style][:width] = "#{inner_width}px"
form[:content][2][:options][:style][:height] = "32px"
form[:content][2][:options][:style][:padding] = "10px 0px 10px 0px"
total_height += 52
else
# just buttons
form[:content][1][:options][:style][:width] = "#{inner_width}px"
form[:content][1][:options][:style][:height] = "32px"
form[:content][1][:options][:style][:padding] = "10px 0px 10px 0px"
total_height += 52
end
# Review : cuts buttons off @ bottom on Firefox (shim-fix)
total_height += 32
#
form[:options][:style][:height] = "#{(total_height - ((form_padding + form_margin) * 2))}px"
### Calculate button horizontal spacing: inner_width
case row_three[:content].size
when 1
row_three[:content][0][:options][:style][:padding] = "5px 0px 5px #{((inner_width / 2) - 25)}px"
when 2
row_three[:content][0][:options][:style][:padding] = "5px 0px 5px #{((inner_width / 4) - 12)}px"
row_three[:content][1][:options][:style][:padding] = "5px 0px 5px #{((inner_width / 4) - 12)}px"
when 3
row_three[:content][0][:options][:style][:padding] = "5px 0px 5px #{((inner_width / 6) - 6)}px"
row_three[:content][1][:options][:style][:padding] = "5px 0px 5px #{((inner_width / 6) - 6)}px"
row_three[:content][2][:options][:style][:padding] = "5px 0px 5px #{((inner_width / 6) - 6)}px"
end
#
dialog_source = {:component => "dialog_box", :options => {:window_title => window_title, :states => {:hidden => true}, :width => total_width, :height => total_height}, :script => "", :content => [(form)]}
dialog_source[:script] = "
on(:keyup) do |event|
if event[:which] == 27
self.respond({:action => :cancel})
end
end
"
#
begin
# log_info("Opening dialog ...")
self.dialog_open(dialog_source, the_application, details, &block)
true
rescue Exception => the_error
log_error({:error => the_error, :parameters => {}})
false
end
end
# Mini Path Finder:
def vfs_dialog(the_application=nil, details=nil, &block)
# details: {:type => :folder, :path => "/path/to/somewhere"}
# Determine Dialog Type:
window_title = "Unsupported Dialog Type"
action_title = "Unsupported"
dialog_type = (details || {:type => :folder})[:type]
case dialog_type
when :save, "save"
window_title = "Save As ..."
action_title = "Save"
when :folder, "folder"
window_title = "Choose Folder"
action_title = "Choose"
when :object, "object"
window_title = "Select Object"
action_title = "Select"
end
# Path Menu: ????
# `#{@element}.textContent = 'The_String'` || set_text("The_String")
menu_bar = {:component => "menu_bar", :options => {:title => "path_menu_bar"}, :content => [], :script => ""}
menu_bar[:script] = "
def update_appearance()
the_window = self.find_parent_type(::GxG::Gui::DialogBox)
if the_window
this_layer = the_window.layer()
unless @current_path
@current_path = (the_window.data()[:path] || '/')
end
build_list = []
if @current_path == '/'
current = ['']
else
current = @current_path.split('/')
end
#
path_menu = {:component => 'menu_entry', :options => {:title => 'path_menu', :content => File.basename(@current_path), :layer => (this_layer + 100), :menu => self}, :content => [], :script => ''}
#
neg_index = (current.size * -1)
((neg_index)..-1).to_a.reverse.each_with_index do |the_offset, indexer|
subpath = current[(0..(the_offset))].join('/')
if subpath == '' || subpath == '/'
path_title = '/'
else
path_title = File.basename(subpath)
end
path_menu[:content] << {:component => 'menu_item', :options => {:content => path_title, :data => {:path => subpath}}, :content => [], :script => ''}
end
build_list << path_menu
self.children.values.each do |the_child|
the_child.destroy
end
self.build_interior_components(build_list)
end
end
def path()
@current_path
end
def path=(the_path=nil)
@current_path = (the_path || '/')
# self.set_text(File.basename(@current_path))
self.update_appearance
the_window = self.find_parent_type(::GxG::Gui::DialogBox)
if the_window
list = the_window.find_child('subitems')
if list
list.update_appearance()
end
end
end
#
def select_item(the_menu_item=nil)
if the_menu_item
self.path = the_menu_item.data()[:path]
end
end
"
menu_bar_cell = {:component => "block_table_cell", :options => {:style => {:border => "1px solid #c2c2c2", :padding => "0px", :margin => "0px", :width => "100%"}}, :content => [(menu_bar)], :script => ""}
# SubItem List:
subitems = {:component=>"list", :options=>{:title => "subitems", :style => {:clear => "both", :"list-style" => "none", :padding => "0px", :margin => "0px"}}, :content=>[], :script=>""}
# Various SubItems scripts (per dialog_type)
subitems[:script] = "
def manifest()
@manifest
end
#
def update_appearance()
the_window = self.find_parent_type(::GxG::Gui::DialogBox)
if the_window
dialog_type = (the_window.data()[:type] || :folder)
menu_bar = the_window.find_child('path_menu_bar')
if menu_bar
current_path = (menu_bar.path() || '/')
else
current_path = '/'
end
@selection = nil
@manifest = {}
self.children.values.each do |the_child|
the_child.destroy
end
GxG::CONNECTION.entries({:location => (current_path)}) do |response|
if response.is_a?(::Hash)
response[:result].each do |the_profile|
item_uuid = ::GxG::uuid_generate()
item_path = (current_path + '/' + the_profile[:title].to_s)
if dialog_type == :folder
if [:virtual_directory, :directory, :persisted_array].include?(the_profile[:type].to_s.to_sym)
@manifest[(item_uuid.to_s.to_sym)] = {:path => item_path, :profile => the_profile}
self.add_list_item({:icon => self.theme_icon('folder.svg'), :label => the_profile[:title].to_s, :uuid => item_uuid.to_s})
end
else
@manifest[(item_uuid.to_s.to_sym)] = {:path => item_path, :profile => the_profile}
case (the_profile[:type].to_s.to_sym)
when :virtual_directory, :directory, :persisted_array
the_icon_path = self.theme_icon('folder.svg')
when :persisted_hash
the_icon_path = self.theme_icon('object.svg')
else
# Review: further qualify file type icons ??
the_icon_path = self.theme_icon('file.svg')
end
self.add_list_item({:icon => the_icon_path, :label => the_profile[:title].to_s, :uuid => item_uuid.to_s})
end
end
end
end
#
end
end
"
case dialog_type
when :save
subitems[:script] = (subitems[:script] + "\n" + "
self.set_select_responder do |the_uuid|
if @manifest[(the_uuid.to_s.to_sym)]
# nothing
end
end
self.set_open_responder do |the_uuid|
if @manifest[(the_uuid.to_s.to_sym)]
if [:virtual_directory, :directory, :persisted_array].include?(@manifest[(the_uuid.to_s.to_sym)][:profile][:type].to_s.to_sym)
# open folder
the_window = self.find_parent_type(::GxG::Gui::DialogBox)
if the_window
menu_bar = the_window.find_child('path_menu_bar')
if menu_bar
GxG::DISPATCHER.post_event(:root) do
menu_bar.path = @manifest[(the_uuid.to_s.to_sym)][:path]
end
end
end
end
end
end
")
when :folder
subitems[:script] = (subitems[:script] + "\n" + "
self.set_select_responder do |the_uuid|
if @manifest[(the_uuid.to_s.to_sym)]
# nothing
end
end
self.set_open_responder do |the_uuid|
if @manifest[(the_uuid.to_s.to_sym)]
if [:virtual_directory, :directory, :persisted_array].include?(@manifest[(the_uuid.to_s.to_sym)][:profile][:type].to_s.to_sym)
# open folder
the_window = self.find_parent_type(::GxG::Gui::DialogBox)
if the_window
menu_bar = the_window.find_child('path_menu_bar')
if menu_bar
GxG::DISPATCHER.post_event(:root) do
menu_bar.path = @manifest[(the_uuid.to_s.to_sym)][:path]
end
end
end
end
end
end
")
when :object
subitems[:script] = (subitems[:script] + "\n" + "
self.set_select_responder do |the_uuid|
if @manifest[(the_uuid.to_s.to_sym)]
# if object selected enable Select button else disable it.
the_window = self.find_parent_type(::GxG::Gui::DialogBox)
if the_window
action_button = the_window.find_child('action_button')
if action_button
if [:file, :persisted_hash].include?(@manifest[(the_uuid.to_s.to_sym)][:profile][:type].to_s.to_sym)
action_button.enable()
else
action_button.disable()
end
end
end
end
end
self.set_open_responder do |the_uuid|
if @manifest[(the_uuid.to_s.to_sym)]
if [:virtual_directory, :directory, :persisted_array].include?(@manifest[(the_uuid.to_s.to_sym)][:profile][:type].to_s.to_sym)
# open folder
# first, disable Select button
the_window = self.find_parent_type(::GxG::Gui::DialogBox)
if the_window
menu_bar = the_window.find_child('path_menu_bar')
if menu_bar
GxG::DISPATCHER.post_event(:root) do
menu_bar.path = @manifest[(the_uuid.to_s.to_sym)][:profile][:path]
end
end
end
end
end
end
")
end
#
subitems_container = {:component=>"block", :options => {:style => {:width => "100%", :height => "192px", :"overflow-y" => "scroll"}}, :content => [(subitems)], :script => ""}
subitems_cell = {:component => "block_table_cell", :options => {:style => {:border => "1px solid #c2c2c2", :padding => "0px", :margin => "0px", :width => "100%"}}, :content => [(subitems_container)], :script => ""}
# Save As ... Field
save_name = {:component=>"text_input", :options=>{:title => "save_name", :style => {:"background-color" => "#f2f2f2", :"font-size" => "16px", :width => "100%"}}, :content=>[], :script=>""}
save_name_cell = {:component => "block_table_cell", :options => {:style => {:padding => "0px", :margin => "0px", :width => "100%"}}, :content => [(save_name)], :script => ""}
# Buttons:
action_btn = {:component=>"submit_button", :options=>{:title => "action_button", :content => action_title, :style => {:padding => "0px", :width => "80px", :height => "32px"}}, :content=>[], :script=>""}
action_btn[:script] = "
on(:mouseup) do |event|
the_window = self.find_parent_type(::GxG::Gui::DialogBox)
form = self.find_parent_type(:form)
if the_window && form
menu_bar = the_window.find_child('path_menu_bar')
list = the_window.find_child('subitems')
if menu_bar && list
if list.selection()
result_path = list.manifest()[(list.selection().to_s.to_sym)][:path]
else
result_path = menu_bar.path()
end
if the_window.data()[:type].to_s.to_sym == :save
result_path = (result_path + '/' + (form.form_data()[:save_name] || 'Untitled.data').to_s)
end
the_window.respond({:action => the_window.data()[:type].to_s.to_sym, :path => result_path})
end
end
end
".encode64
action_cell = {:component => "block_table_cell", :options => {:style => {:padding => "10px 0px 0px 35px", :margin => "0px", :width => "50%"}}, :content => [(action_btn)], :script => ""}
cancel_btn = {:component=>"button", :options=>{:content => "Cancel", :style => {:padding => "2px", :width => "80px", :height => "32px"}}, :content=>[], :script=>""}
cancel_btn[:script] = "
on(:mouseup) do |event|
the_window = self.find_parent_type(::GxG::Gui::DialogBox)
if the_window
the_window.respond({:action => :cancel})
end
end
".encode64
cancel_cell = {:component => "block_table_cell", :options => {:style => {:padding => "10px 0px 0px 35px", :margin => "0px", :width => "50%"}}, :content => [(cancel_btn)], :script => ""}
# Rows:
row_one = {:component=>"block_table_row", :options=>{:style => {:padding => "0px", :margin => "0px", :width => "100%"}}, :content=>[(menu_bar_cell)], :script=>""}
row_two = {:component=>"block_table_row", :options=>{:style => {:padding => "0px", :margin => "0px", :width => "100%"}}, :content=>[(subitems_cell)], :script=>""}
row_three = {:component=>"block_table_row", :options=>{:style => {:padding => "0px", :margin => "0px", :width => "100%"}}, :content=>[(save_name_cell)], :script=>""}
row_four = {:component=>"block_table_row", :options=>{:style => {:padding => "0px", :margin => "0px", :width => "100%"}}, :content=>[(cancel_cell),(action_cell)], :script=>""}
# Tables:
table_one = {:component=>"block_table", :options=>{:style => {:padding => "0px", :margin => "0px", :width => "300px", :height => "32px"}}, :content=>[(row_one)], :script=>""}
table_two = {:component=>"block_table", :options=>{:style => {:padding => "0px", :margin => "0px", :width => "300px", :height => "192px"}}, :content=>[(row_two)], :script=>""}
table_three = {:component=>"block_table", :options=>{:style => {:padding => "0px", :margin => "0px", :width => "300px", :height => "32px"}}, :content=>[(row_three)], :script=>""}
table_four = {:component=>"block_table", :options=>{:style => {:padding => "0px", :margin => "0px", :width => "300px", :height => "32px"}}, :content=>[(row_four)], :script=>""}
#
# Main Build:
form = {:component=>"form", :options=>{:style => {:"background-color" => "#f2f2f2", :padding => "20px", :margin => "5px", :width => "100%"}}, :content=>[], :script=>""}
#
total_height = 330
form[:content] << table_one
form[:content] << table_two
if dialog_type == :save || dialog_type == "save"
form[:content] << table_three
total_height += 32
end
form[:content] << table_four
viewport = {:component=>"application_viewport", :options=>{:title => "selection_viewport", :style => {:overflow => "hidden", :width => "100%", :height => "100%"}}, :content=>[(form)], :script=>""}
dialog_source = {:component => "dialog_box", :options => {:window_title => window_title, :states => {:hidden => true}, :width => 350, :height => total_height}, :script => "", :content => [(viewport)]}
dialog_source[:script] = "
def after_open()
menu_bar = self.find_child('path_menu_bar')
if menu_bar
menu_bar.path = (self.data()[:path])
end
end
"
#
begin
# log_info("Opening dialog ...")
self.dialog_open(dialog_source, the_application, details, &block)
true
rescue Exception => the_error
log_error({:error => the_error, :parameters => {}})
false
end
end
# internal windowing tools:
def get_window_registration(the_reference=nil)
if the_reference.is_any?(::String, ::Symbol)
# {:window => nil, :layer => 0, :restore => nil}
@window_registry[(the_reference)]
else
nil
end
end
#
def set_window_switcher(the_switcher=nil)
if the_switcher
@window_switcher = the_switcher
end
end
def get_window_switcher()
@window_switcher
end
# z-index management, restore/min/max etc.
# @window_registry = (@window_registry.sort_by {|uuid,entry| entry[:layer]}).to_h
# interfacing with others:
def get_window(the_reference=nil)
if the_reference.is_any?(::String, ::Symbol)
registration = self.get_window_registration(the_reference)
if registration
registration[:window]
else
nil
end
else
nil
end
end
#
def window_list()
result = []
@window_registry.keys.each do |reference|
result << {:uuid => reference, :title => @window_registry[(reference)][:window].title()}
end
result
end
#
def window_repack_layers()
@window_registry = (@window_registry.sort_by {|uuid,entry| entry[:layer]}).to_h
@window_registry.values.each_with_index do |entry, indexer|
entry[:window].gxg_merge_style(:"z-index",(200 + indexer))
entry[:layer] = indexer
end
true
end
#
def window_top_layer()
((@window_registry.collect {|uuid,entry| entry[:layer]}).max || 0)
end
#
def window_back_layer()
back_layer = (((@window_registry.collect {|uuid,entry| entry[:layer]}).min || 0) - 1)
if back_layer < 100
window_repack_layers
back_layer = (((@window_registry.collect {|uuid,entry| entry[:layer]}).min || 0) - 1)
end
back_layer
end
#
def window_next_layer()
next_layer = (((@window_registry.collect {|uuid,entry| entry[:layer]}).max || 0) + 1)
if next_layer > 99
window_repack_layers
next_layer = (((@window_registry.collect {|uuid,entry| entry[:layer]}).max || 0) + 1)
end
next_layer
end
#
def window_register(the_reference=nil, the_instance=nil)
if the_reference.is_any?(::String, ::Symbol)
if the_instance
# {:window => nil, :layer => 0, :restore => nil}
front_layer = self.window_next_layer()
the_instance.gxg_merge_style(:"z-index",(200 + front_layer))
@window_registry[(the_reference)] = {:window => the_instance, :layer => front_layer, :restore => nil}
true
else
false
end
else
false
end
end
#
def window_unregister(the_reference=nil)
if the_reference.is_any?(::String, ::Symbol)
@window_registry.delete(the_reference)
true
else
false
end
end
#
def window_open(build_source=nil, the_application=nil)
if build_source.is_any?(::Hash, GxG::Database::PersistedHash)
if build_source.is_a?(GxG::Database::PersistedHash)
the_uuid = build_source.uuid
the_options = build_source[:options].unpersist
if the_options[:title]
the_title = the_options.delete(:title)
else
the_title = build_source.title
end
the_options[:uuid] = the_uuid
else
the_uuid = GxG::uuid_generate.to_sym
the_options = build_source[:options].clone
the_title = the_options.delete(:title)
the_options[:uuid] = the_uuid
end
the_style = the_options.delete(:style)
the_states = (the_options.delete(:states) || {})
new_window = GxG::DISPLAY_DETAILS[:object].add_child(the_uuid, GxG::Gui::Window, the_options)
if new_window
new_window.set_title(the_title)
# Set states:
unless the_states.keys.include?(:hidden)
the_states[:hidden] = false
end
new_window.gxg_set_states(the_states)
# process style info:
if the_style.is_a?(::Hash)
new_window.gxg_set_style(the_style)
end
if the_application.is_a?(::GxG::Application)
new_window.set_application(the_application)
unless the_application.get_window(the_uuid)
the_application.link_window(new_window)
end
if new_window.menu_reference()
the_menu_source = the_application.search_content(new_window.menu_reference())
if the_menu_source.is_any?(::Hash, ::GxG::Database::PersistedHash)
new_window.set_menu(the_menu_source)
else
log_warn("Invalid Menu Resource referenced: #{new_window.menu_reference().inspect}")
end
end
end
# Set Script if any is provided
if build_source[:script].size > 0
new_window.set_script(build_source[:script].to_s)
end
#
self.register_object(the_title, new_window)
self.window_register(the_uuid,new_window)
self.build_components([{:parent => GxG::DISPLAY_DETAILS[:object], :record => build_source, :element => new_window}],the_application)
new_window.before_open
new_window.open
unless the_states[:hidden] == true
new_window.show
end
new_window.after_open
true
else
false
end
else
false
end
end
#
def window_close(the_reference=nil, options={})
if the_reference.is_any?(::String, ::Symbol)
registration = self.get_window_registration(the_reference)
if registration
registration[:window].before_close
registration[:window].close
registration[:window].hide
registration[:window].after_close
self.window_unregister(the_reference)
self.unregister_object(the_reference)
registration[:window].destroy
true
else
false
end
else
false
end
end
#
def window_is_in_back?(the_reference=nil)
the_record = self.get_window_registration(the_reference)
if the_record
bottom_layer = ((@window_registry.collect {|uuid,entry| entry[:layer]}).min || 0)
if the_record[:layer] == bottom_layer
true
else
false
end
else
false
end
end
#
def window_is_in_front?(the_reference=nil)
the_record = self.get_window_registration(the_reference)
if the_record
top_layer = ((@window_registry.collect {|uuid,entry| entry[:layer]}).max || 0)
if the_record[:layer] == top_layer
true
else
false
end
else
false
end
end
#
def window_send_to_back(the_reference=nil)
the_record = self.get_window_registration(the_reference)
if the_record
unless self.window_is_in_back?(the_reference)
back_layer = self.window_back_layer()
the_record[:window].gxg_merge_style(:"z-index",(200 + back_layer))
the_record[:layer] = back_layer
end
else
false
end
end
#
def window_bring_to_front(the_reference=nil, options={})
the_record = self.get_window_registration(the_reference)
if the_record
unless the_record[:layer] == self.window_top_layer()
front_layer = self.window_next_layer()
the_record[:window].gxg_merge_style(:"z-index",(200 + front_layer))
the_record[:layer] = front_layer
end
true
else
false
end
end
# Transition Effect Methods (override for custom effects)
def next_minimize_slot()
# slots are allocated from the top / left of the content area:
cell_width = 200
cell_height = 24
bounds = GxG::DISPLAY_DETAILS[:object].layout_content_area()
columns = (0..(bounds[:right] - bounds[:left]).step(cell_width).size)
rows = (0..(bounds[:bottom] - bounds[:top]).step(cell_height).size)
found = nil
(0..(rows - 1)).each do |the_row|
(0..(columns - 1)).each do |the_column|
is_in = false
test_rect = {:top => (bounds[:top] + (the_row * cell_height)), :left => (bounds[:left] + (the_column * cell_width)), :right => (bounds[:left] + (the_column * cell_width) + cell_width), :bottom => (bounds[:top] + (the_row * cell_height) + cell_height)}
@window_registry.values.each do |the_record|
#
this_rect = {:top => 0, :left => 0, :right => 0, :bottom => 0}
this_rect[:top] = `#{the_record[:window].element}.getBoundingClientRect().top`.to_i
this_rect[:left] = `#{the_record[:window].element}.getBoundingClientRect().left`.to_i
this_rect[:right] = `#{the_record[:window].element}.getBoundingClientRect().right`.to_i
this_rect[:bottom] = `#{the_record[:window].element}.getBoundingClientRect().bottom`.to_i
if (this_rect[:top] == test_rect[:top] && this_rect[:left] == test_rect[:left] && this_rect[:right] == test_rect[:right] && this_rect[:bottom] == test_rect[:bottom])
is_in = true
break
end
#
end
unless is_in
found = test_rect
break
end
end
if found
break
end
end
found
end
#
def minimize_effect(the_record=nil,&block)
switcher = self.get_window_switcher()
if switcher
origin = {:opacity => 1}
origin[:top] = `#{the_record[:window].element}.getBoundingClientRect().top`.to_i
origin[:left] = `#{the_record[:window].element}.getBoundingClientRect().left`.to_i
origin_right = `#{the_record[:window].element}.getBoundingClientRect().right`.to_i
origin_bottom = `#{the_record[:window].element}.getBoundingClientRect().bottom`.to_i
origin[:width] = (origin_right - origin[:left])
origin[:height] = (origin_bottom - origin[:top])
destination = {:opacity => 0}
destination[:top] = `#{switcher.element}.getBoundingClientRect().top`.to_i
destination[:left] = `#{switcher.element}.getBoundingClientRect().right`.to_i
destination_right = `#{switcher.element}.getBoundingClientRect().right`.to_i
destination_bottom = `#{switcher.element}.getBoundingClientRect().top`.to_i
destination[:width] = (destination_right - destination[:left])
destination[:height] = (destination_bottom - destination[:top])
conclude = Proc.new {
if block.respond_to?(:call)
block.call(true)
end
}
else
# find a tile spot to land on:
the_slot = self.next_minimize_slot()
origin = {:opacity => 1}
origin[:top] = `#{the_record[:window].element}.getBoundingClientRect().top`.to_i
origin[:left] = `#{the_record[:window].element}.getBoundingClientRect().left`.to_i
origin_right = `#{the_record[:window].element}.getBoundingClientRect().right`.to_i
origin_bottom = `#{the_record[:window].element}.getBoundingClientRect().bottom`.to_i
origin[:width] = (origin_right - origin[:left])
origin[:height] = (origin_bottom - origin[:top])
destination = {:opacity => 0.5}
destination[:top] = the_slot[:top]
destination[:left] = the_slot[:left]
destination_right = the_slot[:right]
destination_bottom = the_slot[:bottom]
destination[:width] = (destination_right - destination[:left])
destination[:height] = (destination_bottom - destination[:top])
conclude = Proc.new {
self.window_send_to_back(the_record[:window].uuid)
if block.respond_to?(:call)
block.call(true)
end
}
end
animate_content = Proc.new {
top = `#{the_record[:window].element}.getBoundingClientRect().top`.to_i
left = `#{the_record[:window].element}.getBoundingClientRect().left`.to_i
right = `#{the_record[:window].element}.getBoundingClientRect().right`.to_i
bottom = `#{the_record[:window].element}.getBoundingClientRect().bottom`.to_i
the_record[:window].set_frame({:top => top, :left => left, :right => right, :bottom => bottom})
}
the_record[:window].animate({:origin => origin, :destination => destination, :options => {:duration => 500, :animationiteration => animate_content, :animationend => conclude}})
#
settings = {:action => :minimize, :top => 0, :left => 0, :right => 0, :bottom => 0}
settings[:top] = `#{the_record[:window].element}.getBoundingClientRect().top`.to_i
settings[:left] = `#{the_record[:window].element}.getBoundingClientRect().left`.to_i
settings[:right] = `#{the_record[:window].element}.getBoundingClientRect().right`.to_i
settings[:bottom] = `#{the_record[:window].element}.getBoundingClientRect().bottom`.to_i
settings
end
#
def unminimize_effect(the_record=nil,&block)
switcher = self.get_window_switcher()
if switcher
origin = {:opacity => 0}
origin[:top] = `#{the_record[:window].element}.getBoundingClientRect().top`.to_i
origin[:left] = `#{the_record[:window].element}.getBoundingClientRect().left`.to_i
origin_right = `#{the_record[:window].element}.getBoundingClientRect().right`.to_i
origin_bottom = `#{the_record[:window].element}.getBoundingClientRect().bottom`.to_i
origin[:width] = (origin_right - origin[:left])
origin[:height] = (origin_bottom - origin[:top])
destination = {:opacity => 1}
destination[:top] = the_record[:restore][:settings][:top]
destination[:left] = the_record[:restore][:settings][:left]
destination_right = the_record[:restore][:settings][:right]
destination_bottom = the_record[:restore][:settings][:bottom]
destination[:width] = (destination_right - destination[:left])
destination[:height] = (destination_bottom - destination[:top])
else
#
origin = {:opacity => 0.5}
origin[:top] = `#{the_record[:window].element}.getBoundingClientRect().top`.to_i
origin[:left] = `#{the_record[:window].element}.getBoundingClientRect().left`.to_i
origin_right = `#{the_record[:window].element}.getBoundingClientRect().right`.to_i
origin_bottom = `#{the_record[:window].element}.getBoundingClientRect().bottom`.to_i
origin[:width] = (origin_right - origin[:left])
origin[:height] = (origin_bottom - origin[:top])
destination = {:opacity => 1}
destination[:top] = the_record[:restore][:settings][:top]
destination[:left] = the_record[:restore][:settings][:left]
destination_right = the_record[:restore][:settings][:right]
destination_bottom = the_record[:restore][:settings][:bottom]
destination[:width] = (destination_right - destination[:left])
destination[:height] = (destination_bottom - destination[:top])
end
animate_content = Proc.new {
top = `#{the_record[:window].element}.getBoundingClientRect().top`.to_i
left = `#{the_record[:window].element}.getBoundingClientRect().left`.to_i
right = `#{the_record[:window].element}.getBoundingClientRect().right`.to_i
bottom = `#{the_record[:window].element}.getBoundingClientRect().bottom`.to_i
the_record[:window].set_frame({:top => top, :left => left, :right => right, :bottom => bottom})
}
conclude = Proc.new {
if block.respond_to?(:call)
block.call(true)
end
}
the_record[:window].animate({:origin => origin, :destination => destination, :options => {:duration => 500, :animationiteration => animate_content, :animationend => conclude}})
#
true
end
#
def maximize_effect(the_record=nil,&block)
bounds = GxG::DISPLAY_DETAILS[:object].layout_content_area()
origin = {}
origin[:top] = `#{the_record[:window].element}.getBoundingClientRect().top`.to_i
origin[:left] = `#{the_record[:window].element}.getBoundingClientRect().left`.to_i
origin_right = `#{the_record[:window].element}.getBoundingClientRect().right`.to_i
origin_bottom = `#{the_record[:window].element}.getBoundingClientRect().bottom`.to_i
origin[:width] = (origin_right - origin[:left])
origin[:height] = (origin_bottom - origin[:top])
destination = {}
destination[:top] = bounds[:top]
destination[:left] = bounds[:left]
destination[:width] = (bounds[:right] - destination[:left])
destination[:height] = (bounds[:bottom] - destination[:top])
# run animation (which alters the state)
# animationiteration set_frame
animate_content = Proc.new {
top = `#{the_record[:window].element}.getBoundingClientRect().top`.to_i
left = `#{the_record[:window].element}.getBoundingClientRect().left`.to_i
right = `#{the_record[:window].element}.getBoundingClientRect().right`.to_i
bottom = `#{the_record[:window].element}.getBoundingClientRect().bottom`.to_i
the_record[:window].set_frame({:top => top, :left => left, :right => right, :bottom => bottom})
}
conclude = Proc.new {
if block.respond_to?(:call)
block.call(true)
end
}
the_record[:window].animate({:origin => origin, :destination => destination, :options => {:duration => 500, :animationiteration => animate_content, :animationend => conclude}})
#
settings = {:action => :maximize, :top => 0, :left => 0, :right => 0, :bottom => 0}
settings[:top] = bounds[:top]
settings[:left] = bounds[:left]
settings[:right] = bounds[:right]
settings[:bottom] = bounds[:bottom]
settings
end
#
def unmaximize_effect(the_record=nil,&block)
origin = {}
origin[:top] = the_record[:window].top()
origin[:left] = the_record[:window].left()
origin_right = the_record[:window].right()
origin_bottom = the_record[:window].bottom()
origin[:width] = (origin_right - origin[:left])
origin[:height] = (origin_bottom - origin[:top])
destination = {}
destination[:top] = the_record[:restore][:settings][:top]
destination[:left] = the_record[:restore][:settings][:left]
destination_right = the_record[:restore][:settings][:right]
destination_bottom = the_record[:restore][:settings][:bottom]
destination[:width] = (destination_right - destination[:left])
destination[:height] = (destination_bottom - destination[:top])
# run animation (which alters the state)
animate_content = Proc.new {
top = `#{the_record[:window].element}.getBoundingClientRect().top`.to_i
left = `#{the_record[:window].element}.getBoundingClientRect().left`.to_i
right = `#{the_record[:window].element}.getBoundingClientRect().right`.to_i
bottom = `#{the_record[:window].element}.getBoundingClientRect().bottom`.to_i
the_record[:window].set_frame({:top => top, :left => left, :right => right, :bottom => bottom})
}
conclude = Proc.new {
if block.respond_to?(:call)
block.call(true)
end
}
the_record[:window].animate({:origin => origin, :destination => destination, :options => {:duration => 500, :animationiteration => animate_content, :animationend => conclude}})
true
end
#
def rollup_effect(the_record=nil,&block)
origin = {}
the_top = the_record[:window].top()
origin[:height] = the_record[:window].height()
destination = {}
destination[:height] = 24
# run animation (which alters the state)
window_body = the_record[:window].find_child("window_body",true)
if window_body
window_body.hide({:origin => {:opacity => 1.0}, :destination => {:opacity => 0.0}}) do
the_record[:window].animate({:origin => origin, :destination => destination})
if block.respond_to?(:call)
block.call(true)
end
end
end
#
settings = {:action => :rollup, :top => 0, :left => 0, :right => 0, :bottom => 0}
settings[:top] = `#{the_record[:window].element}.getBoundingClientRect().top`.to_i
settings[:left] = `#{the_record[:window].element}.getBoundingClientRect().left`.to_i
settings[:right] = `#{the_record[:window].element}.getBoundingClientRect().right`.to_i
settings[:bottom] = `#{the_record[:window].element}.getBoundingClientRect().bottom`.to_i
settings
end
#
def rolldown_effect(the_record=nil,&block)
origin = {}
origin[:height] = 24
destination = {}
destination[:height] = (the_record[:restore][:settings][:bottom] - the_record[:restore][:settings][:top])
# run animation (which alters the state)
conclude = Proc.new {
window_body = the_record[:window].find_child("window_body",true)
if window_body
window_body.show({:origin => {:opacity => 0.0}, :destination => {:opacity => 1.0}}) do
the_record[:window].commit_settings
if block.respond_to?(:call)
block.call(true)
end
end
end
}
the_record[:window].animate({:origin => origin, :destination => destination, :options => {:duration => 500, :animationend => conclude}})
true
end
# set/restore window state:
def window_restoreable?(the_reference=nil)
the_record = self.get_window_registration(the_reference)
if the_record
if the_record[:restore].is_a?(::Hash)
true
else
false
end
else
false
end
end
#
def window_restore(the_reference=nil, options={},&block)
the_record = self.get_window_registration(the_reference)
if the_record
if the_record[:restore].is_a?(::Hash)
# restore from last :action
case the_record[:restore][:action]
when :minimize
self.unminimize_effect(the_record) do
the_record[:window].clear_special_state(the_record[:restore][:settings])
# clear the restore record
the_record[:restore] = nil
if block.respond_to?(:call)
block.call(true)
end
end
when :maximize
self.unmaximize_effect(the_record) do
the_record[:window].clear_special_state(the_record[:restore][:settings])
# clear the restore record
the_record[:restore] = nil
if block.respond_to?(:call)
block.call(true)
end
end
when :rollup
self.rolldown_effect(the_record) do
the_record[:window].clear_special_state(the_record[:restore][:settings])
# clear the restore record
the_record[:restore] = nil
if block.respond_to?(:call)
block.call(true)
end
end
end
true
else
false
end
else
false
end
end
#
def window_minimize(the_reference=nil, settings={})
the_record = self.get_window_registration(the_reference)
if the_record
if self.window_restoreable?(the_reference)
self.window_restore(the_reference) do
settings = {:top => the_record[:window].top, :left => the_record[:window].left, :right => the_record[:window].right, :bottom => the_record[:window].bottom}
the_record[:restore] = {:action => :minimize, :settings => settings}
the_record[:window].set_special_state(self.minimize_effect(the_record))
end
else
the_record[:restore] = {:action => :minimize, :settings => settings}
the_record[:window].set_special_state(self.minimize_effect(the_record))
end
true
else
false
end
end
#
def window_maximize(the_reference=nil, settings={})
the_record = self.get_window_registration(the_reference)
if the_record
if self.window_restoreable?(the_reference)
self.window_restore(the_reference) do
settings = {:top => the_record[:window].top, :left => the_record[:window].left, :right => the_record[:window].right, :bottom => the_record[:window].bottom}
the_record[:restore] = {:action => :maximize, :settings => settings}
the_record[:window].set_special_state(self.maximize_effect(the_record))
self.window_bring_to_front(the_reference)
end
else
the_record[:restore] = {:action => :maximize, :settings => settings}
the_record[:window].set_special_state(self.maximize_effect(the_record))
self.window_bring_to_front(the_reference)
end
true
else
false
end
end
#
def window_rollup(the_reference=nil, settings={})
the_record = self.get_window_registration(the_reference)
if the_record
if self.window_restoreable?(the_reference)
self.window_restore(the_reference) do
settings = {:top => the_record[:window].top, :left => the_record[:window].left, :right => the_record[:window].right, :bottom => the_record[:window].bottom}
the_record[:restore] = {:action => :rollup, :settings => settings}
the_record[:window].set_special_state(self.rollup_effect(the_record))
end
else
the_record[:restore] = {:action => :rollup, :settings => settings}
the_record[:window].set_special_state(self.rollup_effect(the_record))
end
true
else
false
end
end
#
# Loading and Building:
def get_library(the_reference=nil)
result = nil
if the_reference.is_any?(::Hash, ::GxG::Database::PersistedHash)
GxG::APPLICATIONS[:libraries].values.each do |the_library|
if the_library.title == the_reference[:library].to_s && the_library.type == (the_reference[:type] || the_library.type)
if the_reference[:maximum]
if ((the_reference[:minimum].to_f)..(the_reference[:maximum].to_f)).include?(the_library.version)
result = the_library
break
end
else
if the_library.version >= the_reference[:minimum].to_f
result = the_library
break
end
end
end
end
found
else
if GxG::APPLICATIONS[:libraries][(the_reference.to_s.to_sym)]
result = GxG::APPLICATIONS[:libraries][(the_reference.to_s.to_sym)]
else
GxG::APPLICATIONS[:libraries].values.each do |the_library|
if the_library.title == the_reference
result = the_library
break
end
end
end
end
result
end
#
def library_loaded?(the_reference=nil)
if the_reference.is_any?(::Hash, ::GxG::Database::PersistedHash)
found = false
GxG::APPLICATIONS[:libraries].values.each do |the_library|
if the_library.title == the_reference[:library].to_s && the_library.type == (the_reference[:type] || the_library.type).to_s
if the_reference[:maximum]
if ((the_reference[:minimum].to_f)..(the_reference[:maximum].to_f)).include?(the_library.version)
found = the_library.loaded?()
break
end
else
if the_library.version >= the_reference[:minimum].to_f
found = the_library.loaded?()
break
end
end
end
end
found
else
if GxG::APPLICATIONS[:libraries][(the_reference.to_s.to_sym)]
true
else
found = false
GxG::APPLICATIONS[:libraries].values.each do |the_library|
if the_library.title == the_reference
found = the_library.loaded?()
break
end
end
found
end
end
end
#
def load_library(criteria=nil, &block)
if criteria.is_any?(::Hash, ::GxG::Database::PersistedHash)
# {:library => "lib-name", :manifest => true/false, :dependencies => true/false, :minimum => 0.0, :maximum => 0.0, :loaded => [<UUID>...]}
if criteria[:maximum]
maximum = criteria[:maximum].to_f
else
maximum = nil
end
GxG::CONNECTION.library_pull({:library => criteria[:library].to_s, :type => criteria[:type].to_s, :minimum => (criteria[:minimum] || 0.0).to_f, :maximum => maximum,:dependencies => true, :loaded => GxG::APPLICATIONS[:libraries].keys}) do |response|
# process response[:result]
if response.is_a?(::Hash)
if response[:result].is_a?(::Array)
response[:result].each do |the_record|
unless GxG::APPLICATIONS[:libraries].keys.include?(the_record[:uuid].to_s.to_sym)
definition = the_record[:data]
if definition.is_a?(::String)
if definition.base64?
definition = definition.decode64
end
if definition.json?
definition = ::JSON::parse(definition,{:symbolize_names => true})
end
if definition.is_a?(::Hash)
definition = ::GxG::Database::process_import(definition)
end
if definition.is_a?(::GxG::Database::PersistedHash)
GxG::APPLICATIONS[:libraries][(the_record[:uuid].to_s.to_sym)] = ::GxG::Library.new(definition)
if block.respond_to?(:call)
block.call(GxG::APPLICATIONS[:libraries][(the_record[:uuid].to_s.to_sym)])
end
end
end
end
end
end
end
end
#
end
end
#
def load_libraries(load_queue=[], &block)
result = true
if load_queue.is_any?(::Array, ::GxG::Database::PersistedArray)
load_queue.each do |the_requirement|
GxG::DISPLAY_DETAILS[:object].load_library(the_requirement) do |the_library|
unless the_library.loaded?
log_warn("Unable to load library: #{the_library.title}")
result = false
break
end
end
end
#
end
if block.respond_to?(:call)
block.call(result)
end
result
end
# Application Supports:
def application_open(specifier={}, parameters={})
result = false
if specifier.is_a?(::Hash)
error_hander = Proc.new {
self.set_busy(false)
}
self.set_busy(true)
GxG::CONNECTION.application_open(specifier,error_hander) do |app_stub|
if app_stub.is_a?(::Hash)
if app_stub[:result].is_a?(::Hash)
GxG::CONNECTION.pull_object(app_stub[:result][:location]) do |source_object|
GxG::APPLICATIONS[:processes][(app_stub[:result][:application].to_s.to_sym)] = GxG::Application.new(app_stub[:result].merge({:source => source_object}))
#
result = GxG::APPLICATIONS[:processes][(app_stub[:result][:application].to_s.to_sym)].run(parameters)
#
self.set_busy(false)
end
else
log_warn('Error opening Application: ' + app_stub.inspect)
end
else
log_warn("Malformed response: #{app_stub.inspect}")
end
end
else
log_warn("Invalid Argument: #{specifier.inspect}")
end
result
end
# Theme Supports:
def load_theme(the_theme="")
@theme = the_theme
@theme_prefix = (::GxG::CONNECTION.relative_url() + "/themes/#{the_theme}")
#
::GxG::CONNECTION.pull_object("/content/themes/#{the_theme}") do |response|
GxG::Gui::Css.refresh_css_cache()
# Set CSS Rules:
if response.is_any?(::Hash, ::GxG::Database::PersistedHash)
if response.is_a?(::GxG::Database::PersistedHash)
response = response.unpersist()
end
if response[:resources].is_a?(::Array)
# Load Font and other Resources: response[:resources] (Array of Hashes)
# Font Resource Profile: {
# :resource_type => "font-face",
# :font_family => "Varela",
# :font_style => "normal",
# :font_weight => 400,
# :format => "truetype",
# :source => "http://fonts.gstatic.com/s/varela/v10/DPEtYwqExx0AWHX5Ax4B.ttf"
# }
response[:resources].each do |the_resource_profile|
if the_resource_profile.is_a?(::Hash)
if the_resource_profile[:resource_type] == "font-face"
::GxG::Gui::Css::load_font(the_resource_profile)
end
# expand for other resource types
end
end
end
if response[:rules].is_a?(::Hash)
response[:rules].keys.each do |the_css_key|
GxG::Gui::Css.set_rule(the_css_key, response[:rules][(the_css_key)])
end
end
end
#
end
end
# Component Building:
def build_components(build_queue=[], the_application=nil)
result = false
if build_queue.is_any?(::Array, ::GxG::Database::PersistedArray)
while build_queue.size > 0 do
entry = build_queue.shift
if entry.is_any?(::Hash, ::GxG::Database::PersistedHash)
# Build out object based upon the record
if entry[:element].is_a?(::GxG::Gui::Page)
if entry[:record][:page_title]
`document.title = #{entry[:record][:page_title].to_s}`
# TODO: set meta data
# Set classes for page:
if entry[:record][:class].to_s.size > 0
`document.body.setAttribute("class",#{entry[:record][:class].to_s})`
end
# Set Script if any is provided
if entry[:record][:script].size > 0
entry[:element].set_script(entry[:record][:script].to_s)
end
end
else
# All other components / elements
end
# Queue up sub-components for construction
if entry[:record][:content].size > 0
entry[:record][:content].each do |sub_component|
# puts "Building: #{sub_component.inspect}"
case sub_component[:component].to_s.downcase.to_sym
when :animation
# store the animation in GxG::ANIMATION[:animations] hash (by uuid)
if sub_component.is_a?(::GxG::Database::PersistedHash)
the_uuid = sub_component.uuid.to_s.to_sym
the_options = sub_component[:options].unpersist()
unless the_options[:title].is_a?(::String)
the_options[:title] = sub_component.title.to_s
end
else
the_uuid = ::GxG::uuid_generate.to_s.to_sym
the_options = sub_component[:options].clone
unless the_options[:title].is_a?(::String)
the_options[:title] = "Untitled Animation #{the_uuid.to_s}"
end
end
GxG::ANIMATION[:animations][(the_uuid)] = GxG::Gui::Animation.new(the_options,the_uuid)
if sub_component[:script].size > 0
GxG::ANIMATION[:animations][(the_uuid)].set_script(sub_component[:script].to_s)
end
# store the animation assets in GxG::ANIMATION[:assets] hash (by uuid) (later)
if sub_component[:content].size > 0
sub_component[:content].each do |the_asset|
# Expansion ??
end
end
else
#
element_class = GxG::Gui::component_class(sub_component[:component].to_s.to_sym)
if element_class
if sub_component.is_a?(::GxG::Database::PersistedHash)
the_options = sub_component[:options].unpersist()
the_uuid = sub_component.uuid.to_s.to_sym
if the_options[:title]
the_title = the_options.delete(:title)
else
the_title = sub_component.title.to_s
end
else
the_options = sub_component[:options].clone
the_uuid = ::GxG::uuid_generate.to_sym
if the_options[:title]
the_title = the_options.delete(:title)
else
the_title = "Untitled Component #{the_uuid.to_s}"
end
end
#
the_style = the_options.delete(:style)
the_states = (the_options.delete(:states) || {})
app_location = the_options.delete(:location)
app_name = the_options.delete(:name)
if the_options[:track]
tracking = the_options.delete(:track)
else
tracking = nil
end
#
new_entry = {:parent => entry[:element], :record => sub_component, :element => entry[:element].add_child((the_uuid), element_class, the_options.merge!({:uuid => the_uuid}))}
new_entry[:element].set_title(the_title)
#
self.register_object(the_title, new_entry[:element])
# Window component tracking:
if tracking
the_window = new_entry[:element].window()
if the_window
the_window.track_deltas(the_uuid,new_entry[:element],tracking)
end
end
#
# Set Layout info if any:
if the_options[:zone]
self.layout_add_item(the_uuid, the_options[:zone].to_s.to_sym, false)
end
# Set states:
unless the_states.keys.include?(:hidden)
the_states[:hidden] = false
end
new_entry[:element].gxg_set_states(the_states)
# process style info:
if the_style.is_a?(::Hash)
new_entry[:element].gxg_set_style(the_style)
end
# linking
if sub_component[:component].to_s.to_sym == :window
if the_application.is_a?(::GxG::Application)
new_entry[:element].set_application(the_application)
unless the_application.get_window(the_uuid)
the_application.link_window(new_entry[:element])
end
if new_entry[:element].menu_reference()
the_menu_source = the_application.search_content(new_entry[:element].menu_reference())
if the_menu_source.is_any?(::Hash, ::GxG::Database::PersistedHash)
new_entry[:element].set_menu(the_menu_source)
else
log_warn("Invalid Menu Resource referenced: #{new_entry[:element].menu_reference().inspect}")
end
end
end
self.window_register(the_uuid, new_entry[:element])
end
if sub_component[:component].to_s.to_sym == :dialog_box
if the_application.is_a?(::GxG::Application)
new_entry[:element].set_application(the_application)
unless the_application.get_window(the_uuid)
the_application.link_window(new_entry[:element])
end
end
# DialogBoxes are NOT registered with the windowing system!
end
if sub_component[:component].to_s.to_sym == :application_viewport
#
if the_application.is_a?(::GxG::Application)
new_entry[:element].set_application(the_application)
unless the_application.get_viewport(the_uuid)
the_application.link_viewport(new_entry[:element])
end
else
if app_location.is_a?(::String) || app_name.is_a?(::String)
#
allocated = false
GxG::APPLICATIONS[:processes].keys.each do |the_app_key|
# FIXME: if an app is not :unique there is no garanteeing the load order will
# match the page's :application_viewport load order. So mismatched page viewports can result.
# That is: they may end up in reversed page/screen positions due to app assignment ordinal.
if GxG::APPLICATIONS[:processes][(the_app_key)].location == app_location || GxG::APPLICATIONS[:processes][(the_app_key)].title == app_name
#
unless GxG::APPLICATIONS[:processes][(the_app_key)].get_viewport(the_uuid)
#
new_entry[:element].set_application(GxG::APPLICATIONS[:processes][(the_app_key)])
GxG::APPLICATIONS[:processes][(the_app_key)].link_viewport(new_entry[:element])
allocated = true
break
end
end
end
unless allocated == true
if app_location
specifier = {:location => app_location, :restore => true}
end
if app_name
specifier = {:name => app_name, :restore => true}
end
# Look for existing / loaded app to link viewport to
# if exists, link, if not: app_open and link
link_app = nil
GxG::APPLICATIONS[:processes].values.each do |application|
if specifier[:location]
if specifier[:location] == application.location
link_app = application
break
end
else
if specifier[:name]
if specifier[:name] == File.basename(application.location)
link_app = application
break
end
end
end
end
if link_app
new_entry[:element].set_application(link_app)
link_app.link_viewport(new_entry[:element])
else
GxG::CONNECTION.application_open(specifier) do |app_stub|
if app_stub[:result].is_a?(::Hash)
GxG::CONNECTION.pull_object(app_stub[:result][:location]) do |source_object|
GxG::APPLICATIONS[:processes][(app_stub[:result][:application].to_s.to_sym)] = GxG::Application.new(app_stub[:result].merge({:source => source_object, :viewport => new_entry[:element]}))
new_entry[:element].set_application(GxG::APPLICATIONS[:processes][(app_stub[:result][:application].to_s.to_sym)])
GxG::APPLICATIONS[:processes][(app_stub[:result][:application].to_s.to_sym)].link_viewport(new_entry[:element])
#
end
else
log_warn("Failure retrieving: #{specifier.inspect} --> #{app_stub.inspect}")
end
end
end
end
else
log_warn("Neither :location nor :name are specified!")
end
#
end
end
# Set Script if any is provided
if sub_component[:script].size > 0
new_entry[:element].set_script(sub_component[:script].to_s)
end
#
build_queue << new_entry
end
#
end
end
end
#
end
end
result = true
end
result
end
#
def load(page_profile=nil)
# Build out page with page_profile
if page_profile.is_any?(::Hash, ::GxG::Database::PersistedHash)
@title_registry = {}
@title_registry[(page_profile.uuid().to_s.to_sym)] = page_profile.title()
# Load CSS Theme
if page_profile[:theme].to_s.size > 0
self.load_theme(page_profile[:theme].to_s)
else
self.load_theme("default")
end
# Build a list of viewport application references
viewport_apps = []
page_profile[:content].search do |value, selector, container|
if selector == :location
viewport_apps << container[(selector)].to_s
end
end
# Restore prior-session applications: state-only, run @ tail end.
GxG::APPLICATIONS[:prefetch] = true
app_prefetched = []
skipped_apps = 0
list_failure = Proc.new do |response|
log_warn("Application List Failure: #{response.inspect}")
GxG::APPLICATIONS[:prefetch] = false
end
GxG::CONNECTION.application_list(list_failure) do |list_response|
if list_response[:result].is_a?(::Array)
if list_response[:result].size > 0
GxG::APPLICATIONS[:prefetch] = true
list_response[:result].each do |entry|
# entry: {:application => <ProcessUUID>, :credentialed => false, :unique => true, :location => "/Path/to/app"}
GxG::CONNECTION.pull_object(entry[:location]) do |source_object|
if GxG::DISPLAY_DETAILS[:logged_in] == true
app_prefetched << entry[:application].to_s.to_sym
GxG::APPLICATIONS[:processes][(entry[:application].to_s.to_sym)] = GxG::Application.new(entry.merge({:source => source_object}))
else
if source_object[:credentialed] == true
skipped_apps += 1
else
app_prefetched << entry[:application].to_s.to_sym
GxG::APPLICATIONS[:processes][(entry[:application].to_s.to_sym)] = GxG::Application.new(entry.merge({:source => source_object}))
end
end
if (GxG::APPLICATIONS[:processes].size + skipped_apps) == list_response[:result].size
self.build_components([{:parent => nil, :record => page_profile, :element => self}])
else
GxG::APPLICATIONS[:prefetch] = true
end
end
end
else
# Load Page and Components
self.build_components([{:parent => nil, :record => page_profile, :element => self}])
end
end
end
prefetch_timer = nil
prefetch_timer = GxG::DISPATCHER.add_periodic_timer({:interval => 0.5}) do
if GxG::APPLICATIONS[:prefetch] == true
loaded_apps = 0
GxG::APPLICATIONS[:processes].values.each do |the_app|
if viewport_apps.include?(the_app.location.to_s)
loaded_apps += 1
end
end
if loaded_apps == viewport_apps.size
GxG::APPLICATIONS[:prefetch] = false
end
else
self.set_busy true
#
GxG::APPLICATIONS[:processes].values.each do |the_app|
if app_prefetched.include?(the_app.process_uuid)
the_app.run({:restore => true})
else
the_app.run()
end
end
GxG::DISPATCHER.cancel_timer(prefetch_timer)
self.set_busy false
end
end
#
end
end
#
def start(navigate_to="")
request_path = navigate_to[(::GxG::CONNECTION.relative_url().size)..-1]
if request_path == "/"
request_path = "/index"
end
::GxG::DISPLAY_DETAILS[:host_path] = request_path
# Load Page Content
::GxG::CONNECTION.pull_object("/content/pages#{request_path}") do |response|
self.load(response)
self.layout_refresh
end
end
#
def stop()
#
end
#
end
#
end
end
#