# #require 'gxg_uri' # module GxG module Networking # WebSocket Connector: class SocketMonitor # def initialize() @event_handlers = {} self end # def on(the_operation=nil, &block) if the_operation.is_any?(::String, ::Symbol) if block.respond_to?(:call) @event_handlers[(the_operation.to_s.downcase.to_sym)] = block end end end # def follow_up(options={}) operation_frame = GxG::DISPLAY_DETAILS[:socket].next_message() if operation_frame.is_a?(::Hash) the_operation = (operation_frame.keys[0]) if @event_handlers[(the_operation)].respond_to?(:call) @event_handlers[(the_operation)].call(operation_frame[(the_operation)]) end end end # end # class Socket # def inbox() @inbox end # def add_to_inbox(payload="") begin @inbox << ::JSON.parse(payload.decode64,{:symbolize_names => true}) rescue Exception => the_error log_error({:error => the_error, :parameters => payload.decode64}) end end # def error_message(the_message) log_error({:error => Exception.new(the_message)}) end # def next_message() @inbox.shift end # def write(the_data="") # Expects: String or Hash if the_data.is_any?(::Hash, ::GxG::Database::DetachedHash) if the_data.is_a?(::Hash) the_data = JSON.generate(the_data).encode64 else # Discouraged: no context of an operation this way. Not good. (deprecate?) the_data = JSON.generate(the_data.export).encode64 end end thesocket = @socket if thesocket `thesocket.send(the_data)` true else false end end # def close() thesocket = @socket if thesocket `thesocket.close` @socket = nil true else false end end # def open?() if @socket true else false end end # def open(the_location) @socket = nil if (`window.location['href']`).include?("https://") the_url = "wss://#{the_location}" else the_url = "ws://#{the_location}" end begin greeting = {:attach_display => true}.to_json.encode64 the_socket = `new WebSocket(the_url)` %x{ the_socket.onopen = function(e) { the_socket.send(greeting); }; the_socket.onmessage = function(event) { Opal.eval("GxG::DISPLAY_DETAILS[:socket].add_to_inbox('" + event.data + "')"); }; the_socket.onclose = function(event) { Opal.eval("GxG::DISPLAY_DETAILS[:socket].close"); }; the_socket.onerror = function(error) { Opal.eval("GxG::DISPLAY_DETAILS[:socket].error_message('" + error.message + "')"); }; } @socket = the_socket rescue Exception => the_error log_error(:error => the_error, :parameters => {:url => the_url.to_s}) end if @socket ::GxG::DISPLAY_DETAILS[:socket] = self end end # def set_remote_uuid(the_uuid=nil) if GxG::valid_uuid?(the_uuid) @remote_uuid = the_uuid.to_sym true else false end end # def initialize() @uuid = GxG::uuid_generate().to_sym @remote_uuid = nil @inbox = [] self end end end # XHR Connector: module Networking class Channel # end # class Connector # def remote_uuid() @remote_uuid end # def open?() @active end # def close() the_message = new_message({:close => true}) the_message[:sender] = @uuid the_message[:to] = @remote_uuid self.pull(the_message,{:synchronous => true}) @active = false @csrf = nil if @heart_beat_timer.is_a?(::Hash) GxG::DISPATCHER.cancel_timer(@heart_beat_timer) end true end # def push(the_message=nil, options={}) if @active # Uses PUT method, with data payload if the_message.is_a?(::GxG::Events::Message) if @relative_url.to_s.size > 0 the_url = "#{@host_prefix}#{@relative_url.to_s}?display=#{@display_path.to_s}" else the_url = "#{@host_prefix}?display=#{@display_path.to_s}" end payload = the_message.body.to_json.to_s the_xhr = `new XMLHttpRequest` `#{the_xhr}.open('PUT',#{the_url},true)` `#{the_xhr}.timeout = #{(options[:timeout] || 5000)}` `#{the_xhr}.setRequestHeader('X-Requested-With','XMLHttpRequest')` `#{the_xhr}.setRequestHeader('Accept','application/json')` `#{the_xhr}.setRequestHeader('X-CSRF-Token', #{@csrf})` `#{the_xhr}.setRequestHeader('Content-Type', 'application/json')` # unless (`navigator.userAgent`).include?("Firefox") || (`navigator.userAgent`).include?("Chrome") # `#{the_xhr}.setRequestHeader('Content-Length', #{payload.size})` # end failed = Proc.new do |reason=nil| the_message.fail(reason) end succeeded = Proc.new do response_code = (`#{the_xhr}.status`).to_i if response_code < 300 begin the_message.succeed(::JSON.parse(`#{the_xhr}.response`,{:symbolize_names => true})) rescue Exception => the_error log_error({:error => the_error, :parameters => `#{the_xhr}.response`}) end else failed.call(response_code) end end `#{the_xhr}.addEventListener('load', #{succeeded})` `#{the_xhr}.addEventListener('error', #{failed})` `#{the_xhr}.addEventListener('timeout', #{failed})` `#{the_xhr}.addEventListener('abort', #{failed})` `#{the_xhr}.send(#{payload})` # true else # error? false end end # end # def pull(the_message=nil, options={:synchronous => false}) if @active # Uses GET method, with in-URL query specifiers (no data payload) # @display_path if the_message.is_a?(::GxG::Events::Message) the_query = "display=#{@display_path.to_s}&details=#{the_message.body.to_json.encode64}" if @relative_url.to_s.size > 0 the_url = (@host_prefix.to_s + @relative_url.to_s + "?" + the_query) else the_url = (@host_prefix.to_s + "?" + the_query) end the_xhr = `new XMLHttpRequest` if options[:synchronous] == true `#{the_xhr}.open('GET',#{the_url.to_s},false)` else `#{the_xhr}.open('GET',#{the_url.to_s},true)` `#{the_xhr}.timeout = #{(options[:timeout] || 600000)}` end `#{the_xhr}.setRequestHeader('X-Requested-With','XMLHttpRequest')` `#{the_xhr}.setRequestHeader('Accept','application/json')` failed = Proc.new do |reason=nil| the_message.fail(reason) end succeeded = Proc.new do response_code = (`#{the_xhr}.status`).to_i if response_code < 300 begin the_message.succeed(::JSON.parse(`#{the_xhr}.response`,{:symbolize_names => true})) rescue Exception => the_error log_error({:error => the_error, :parameters => `#{the_xhr}.response`}) end else failed.call(response_code) end end `#{the_xhr}.addEventListener('load', #{succeeded})` `#{the_xhr}.addEventListener('error', #{failed})` `#{the_xhr}.addEventListener('timeout', #{failed})` `#{the_xhr}.addEventListener('abort', #{failed})` `#{the_xhr}.send(null)` # true else # error? false end end # end # def push_message(the_message=nil) # TODO: establish a sensible routing scheme and make better use of message :sender and :to in that idea. end # def pull_message(options={}) # Use to pull a message portion or look for application messages on the server. end # def process_pending(options={}) # end # Support-level methods for pulling/pushing objects or object-deltas. def push_object_portions(details={}, error_handler=nil, &the_handler) # supply thread and index to send, if successful, will post event to keep chain going. # Details: {:thread => "", :index => 0, :portions => 100} end # Search The Database(s) def search_database(details={}, error_handler=nil, &the_handler) # FOR NOW: limit 100 records returned at a time. # Use: {:limit => 100, :offset => 0} || {:page => 1} if details.is_a?(::Hash) && the_handler self.pull_data({:search_database => {:criteria => details}},error_handler) do |data| if data.is_a?(::Hash) the_handler.call(data) end end true else false end end # Search The Database(s) & PULL a batch of records and formats def search_pull(details={}, error_handler=nil, &the_handler) # FOR NOW: limit 100 records returned at a time. # Use: {:limit => 100, :offset => 0} || {:page => 1} if details.is_a?(::Hash) && the_handler self.pull_data({:search_pull => {:criteria => details}},error_handler) do |data| if data.is_a?(::Hash) if data[:result] if data[:result].is_a?(::String) if data[:result].to_s.base64? data = data[:result].decode64 end if data.to_s.json? begin data = ::JSON::parse(data.to_s,{:symbolize_names => true}) the_handler.call(::GxG::Database::process_detached_import(data)) rescue Exception => the_error log_error({:error => the_error, :parameters => {:data => data}}) end end # else # error log_error({:error => Exception.new("Malformed Response Data"), :parameters => {:data => data[:result]}}) end else if data[:error] if error_handler.respond_to?(:call) error_handler.call(data) end end end # end end true else false end end # High-level methods for pulling/pushing objects or object-deltas. def destroy_object(the_specifier=nil,error_handler=nil,&the_handler) if the_specifier && the_handler self.pull_data({:destroy_object => {:path => the_specifier}},error_handler) do |data| if data.is_a?(::Hash) the_handler.call(data) end end true else false end end # def push_data(details={}, error_handler=nil,&the_handler) if details.is_a?(::Hash) && the_handler # the_message = new_message(details) the_message[:sender] = @uuid the_message[:to] = @remote_uuid if error_handler.respond_to?(:call) the_message.on(:fail,&error_handler) end the_message.on(:success) do |response| the_handler.call(response) end GxG::CONNECTION.push(the_message) # true else false end end # def push_object(details={}, error_handler=nil,&the_handler) if details.is_a?(::Hash) && the_handler # ???? # Details: {:path => "/path/to/object", :object => (BinaryArray/String/PersistedHash)} exception = nil payload = {} if details[:object].is_any?(::GxG::BinaryArray, ::String) # Binary payload if details[:path].to_s.size > 0 payload[:path] = details[:path] payload[:object] = details[:object].to_s.encode64 else exception = "You MUST specify a :path to store the data into." end else if details[:object].is_a?(::GxG::Database::DetachedHash) # DB Object / VFS payload if details[:path].to_s.size > 0 payload[:path] = details[:path] end payload[:uuid] = details[:object].uuid.to_s # Generate Importation Records format_list = [] importation_record = {:formats => {}, :records => []} # if details[:object].format.to_s.size > 0 format_list << details[:object].format end details[:object].search do |value, selector, container| if value.is_a?(::GxG::Database::DetachedHash) if value.format.to_s.size > 0 format_list << value.format end end if value.is_a?(::GxG::Database::PersistedArray) if value.constraint.to_s.size > 0 format_list << value.constraint end end end format_list.each do |the_specifier| format_record = GxG::DB[:formats][(the_specifier.to_s.to_sym)].clone if format_record.is_a?(::Hash) format_record[:content] = format_record[:content].gxg_export importation_record[:formats][(the_specifier.to_s.to_sym)] = format_record end end importation_record[:records] << details[:object].export # payload[:object] = importation_record else exception = "Invalid Data Type" end end if exception if error_handler.respond_to?(:call) error_handler.call({:result => nil, :error => exception}) end false else self.push_data({:put_object => payload},error_handler) do |data| if data.is_a?(::Hash) the_handler.call(data) end end true end else false end end # def pull_data(the_specifier={},error_handler=nil,&the_handler) if the_specifier && the_handler # accepts entire command frame as specifier: the_message = new_message(the_specifier) the_message[:sender] = @uuid the_message[:to] = @remote_uuid if error_handler.respond_to?(:call) the_message.on(:fail,&error_handler) end the_message.on(:success) do |response| if response.is_a?(::Hash) data = nil if response[:result].is_a?(::String) if response[:result].to_s.base64? data = response[:result].decode64 end if data.to_s.json? begin data = ::JSON::parse(data.to_s,{:symbolize_names => true}) rescue Exception => the_error log_error({:error => the_error, :parameters => {:data => data}}) end end the_handler.call(data) else # error log_error({:error => Exception.new("Malformed Response Data"), :parameters => {:data => response[:result]}}) end end end GxG::CONNECTION.pull(the_message) end end # def pull_object(the_specifier=nil,error_handler=nil,&the_handler) if the_specifier && the_handler # GxG::DISPLAY_DETAILS[:object].set_busy(true) if GxG::valid_uuid?(the_specifier.to_s) op_frame = {:get_object => {:uuid => the_specifier}} else op_frame = {:get_object => {:path => the_specifier}} end self.pull_data(op_frame,error_handler) do |data| if data.is_a?(::Hash) # puts "Got: #{data.inspect}" the_handler.call(::GxG::Database::process_detached_import(data)) # xxx # if data[:result] # if data[:result].is_a?(::String) # if data[:result].to_s.base64? # data = data[:result].decode64 # end # if data.to_s.json? # begin # data = ::JSON::parse(data.to_s,{:symbolize_names => true}) # the_handler.call(::GxG::Database::process_detached_import(data)) # rescue Exception => the_error # log_error({:error => the_error, :parameters => {:data => data}}) # end # end # # # else # # error # log_error({:error => Exception.new("Malformed Response Data"), :parameters => {:data => data[:result]}}) # end # else # if data[:error] # if error_handler.respond_to?(:call) # error_handler.call(data) # end # end # end # GxG::DISPLAY_DETAILS[:object].set_busy(false) end end true else false end end # def load_format(details={}, error_handler=nil, &the_handler) if details && the_handler self.pull_data({:get_format => details},error_handler) do |data| if data.is_a?(::Hash) if data[:result] if data[:result].is_a?(::String) if data[:result].to_s.base64? data = data[:result].decode64 end if data.to_s.json? begin data = ::JSON::parse(data.to_s,{:symbolize_names => true}) ::GxG::Database::process_detached_import(data) the_handler.call({:result => true}) rescue Exception => the_error log_error({:error => the_error, :parameters => {:data => data}}) end end # else # error log_error({:error => Exception.new("Malformed Response Data"), :parameters => {:data => data[:result]}}) end else if data[:error] if error_handler.respond_to?(:call) error_handler.call(data) end end end # end end true else false end end # def store_format(details={}, error_handler=nil, &the_handler) if details && the_handler if details.keys.size == 0 format_list = ::GxG::DB[:formats].keys else format_list = [] ::GxG::DB[:formats].keys.each do |the_uuid| if details[:uuid].to_s.size > 0 if the_uuid == details[:uuid] unless format_list.include?(the_uuid) format_list << the_uuid end end end if details[:type].to_s.size > 0 if ::GxG::DB[:formats][(the_uuid)][:type] == details[:type] unless format_list.include?(the_uuid) format_list << the_uuid end end end if details[:ufs].to_s.size > 0 if ::GxG::DB[:formats][(the_uuid)][:ufs] == details[:ufs] unless format_list.include?(the_uuid) format_list << the_uuid end end end if details[:title].to_s.size > 0 if ::GxG::DB[:formats][(the_uuid)][:title] == details[:title] unless format_list.include?(the_uuid) format_list << the_uuid end end end if details[:version].to_s.size > 0 if ::GxG::DB[:formats][(the_uuid)][:version] == details[:version] unless format_list.include?(the_uuid) format_list << the_uuid end end end end end importation_record = {:formats => {}, :records => []} format_list.each do |the_uuid| format_record = ::GxG::DB[:formats][(the_uuid.to_s.to_sym)].clone format_record[:content] = format_record[:content].gxg_export importation_record[:formats][(the_uuid.to_s.to_sym)] = format_record end self.push_data({:put_format => importation_record},error_handler) do |data| if data.is_a?(::Hash) the_handler.call(data) end end true else false end end # def get_permissions(the_specifier=nil,error_handler=nil,&the_handler) if the_specifier && the_handler # GxG::DISPLAY_DETAILS[:object].set_busy(true) self.pull_data({:get_permissions => {:path => the_specifier}},error_handler) do |data| if data.is_a?(::Hash) the_handler.call(data) # GxG::DISPLAY_DETAILS[:object].set_busy(false) end end true else false end end # # Libarary Loading Support: def library_pull(details={}, error_handler=nil, &the_handler) if self.open? if details.is_a?(::Hash) && the_handler self.pull_data({:get_library => details},error_handler) do |data| the_handler.call(data) end true else false end end end # Format Support: def format_pull(details={}, error_handler=nil, &the_handler) if self.open? if details.is_any?(::Array, ::Hash) && the_handler self.pull_data({:format_pull => details},error_handler) do |data| the_handler.call(data) end true else false end end end # TODO: format_push(data={}) def format_push(details={}, error_handler=nil, &the_handler) if self.open? if details.is_a?(::Hash) && the_handler self.push_data({:format_push => details},error_handler) do |data| the_handler.call(data) end true else false end end end # def format_list(details={}, error_handler=nil, &the_handler) if self.open? if details.is_a?(::Hash) && the_handler self.pull_data({:format_list => details},error_handler) do |data| the_handler.call(data) end true else false end end end # Admin: def admin(details={}, error_handler=nil, &the_handler) if self.open? if details.is_a?(::Hash) && the_handler self.pull_data({:admin => details},error_handler) do |data| the_handler.call(data) end true else false end end end # # VFS: def vfs(details={}, error_handler=nil, &the_handler) if self.open? if details.is_a?(::Hash) && the_handler if [:vfs_mkfile, :vfs_mkdir, :vfs_rename, :vfs_destroy, :vfs_copy, :vfs_move, :set_permissions].include?(details.keys[0]) self.pull_data(details, error_handler) do |data| # Review : further decoding needed? base64?? the_handler.call(data) end true else false end else false end else false end end # # Application Support: def app_state_push(details={}, error_handler=nil, &the_handler) if details.is_a?(::Hash) && the_handler self.push_data({:app_state_push => {:application => details[:application], :data => details[:data]}},error_handler) do |data| if data.is_a?(::Hash) the_handler.call(data) end end true else false end end # def app_state_pull(details={}, error_handler=nil, &the_handler) if self.open? if details.is_a?(::Hash) && the_handler self.pull_data({:app_state_pull => {:application => details[:application]}},error_handler) do |data| the_handler.call(data) end true else false end end end # def application_open(details={}, error_handler=nil, &the_handler) # Specify either a :path or :name if self.open? if details.is_a?(::Hash) && the_handler if details[:name] self.pull_data({:application_open => {:restore => (details[:restore] || false), :name => details[:name]}},error_handler) do |data| the_handler.call(data) end else # Review : remember to call using 'path' - NOT 'location' anymore: self.pull_data({:application_open => {:restore => (details[:restore] || false), :path => details[:path]}},error_handler) do |data| the_handler.call(data) end end true else false end end end # def application_close(details={}, error_handler=nil, &the_handler) if self.open? if details.is_a?(::Hash) && the_handler self.pull_data({:application_close => {:application => details[:application]}},error_handler) do |data| the_handler.call(data) end true else false end end end # def application_list(error_handler=nil, &the_handler) if self.open? if the_handler self.pull_data({:application_list => true},error_handler) do |data| the_handler.call(data) end true else false end end end # def application_menu(error_handler=nil, &the_handler) if self.open? if the_handler self.pull_data({:application_menu => true},error_handler) do |data| the_handler.call(data) end true else false end end end # def entries(details={}, error_handler=nil, &the_handler) if self.open? if details.is_a?(::Hash) && the_handler GxG::DISPLAY_DETAILS[:object].set_busy(true) # Review : remember to use 'path' - NOT 'location' anymore: self.pull_data({:entries => {:path => details[:path]}},error_handler) do |data| the_handler.call(data) GxG::DISPLAY_DETAILS[:object].set_busy(false) end true else false end end end # def heartbeat() if @active heartbeat_message = new_message({:heartbeat => true}) heartbeat_message[:sender] = @uuid.to_s heartbeat_message[:to] = @remote_uuid.to_s heartbeat_message.on(:success) do |response| if response.is_a?(::Hash) # FIXME: possible conflict with proper credential setting. if response[:result][:status] == "credentialed" # GxG::DISPLAY_DETAILS[:logged_in] = true else # GxG::DISPLAY_DETAILS[:logged_in] = false end GxG::DISPLAY_DETAILS[:server_status] = response[:result][:server].to_s.to_sym end end GxG::CONNECTION.pull(heartbeat_message) end end # # Set Credential: def update_credential(details={}, error_handler=nil, &the_handler) if self.open? if details.is_a?(::Hash) && the_handler content = details.to_json.to_s.encrypt(@csrf.to_s).encode64 the_message = new_message({:update_credential => content}) the_message[:sender] = @uuid the_message[:to] = @remote_uuid the_message[:body][:thread] = the_message.id().to_s the_message.on(:success) do |the_response| if the_response.is_a?(::Hash) if the_response[:status] == "credentialed" GxG::DISPLAY_DETAILS[:logged_in] = true end end the_handler.call(the_response) end if error_handler the_message.on(:fail) do |the_response| error_handler.call(the_response) end end GxG::CONNECTION.push(the_message) end end end # def downgrade_credential(&the_handler) # ???? if @active downgrade_message = new_message({:downgrade_credential => true}) downgrade_message[:sender] = @uuid.to_s downgrade_message[:to] = @remote_uuid.to_s downgrade_message.on(:success) do |response| # var evt = new CustomEvent('credentialchanged', { detail: "state" }); ?? # window.dispatchEvent(evt); if response.is_a?(::Hash) # FIXME: possible conflict with proper credential setting. if response[:status] == "uncredentialed" GxG::DISPLAY_DETAILS[:logged_in] = false if the_handler.respond_to?(:call) the_handler.call(response) end new_event = `new CustomEvent('credentialchanged', { detail: #{response[:status].to_s} })` `window.dispatchEvent(#{new_event})` else # GxG::DISPLAY_DETAILS[:logged_in] = true end # GxG::DISPLAY_DETAILS[:server_status] = response[:server].to_s.to_sym end end GxG::CONNECTION.pull(downgrade_message) end end # def action(details={}, error_handler=nil, &the_handler) # ???? end # def relative_url() @relative_url end # def query() @query end # def article() @article end # def display_path() @display_path end # def initialize() if (`window.location['href']`).include?("https://") @host_prefix = ("https://" + `window.location['host']`) else @host_prefix = ("http://" + `window.location['host']`) end @relative_url = "" # Find Relative URL (Note: now provided by host in introduction) page_name = File::basename(`window.location['pathname']`) subpath_array = (`window.location['pathname']`.split("/")) found_at = subpath_array.index(page_name) if found_at.is_a?(::Numeric) if found_at > 1 @relative_url = subpath_array[(0..(found_at - 1))].join("/") end end # Process Query if found @article = nil @query = {} query_string = `window.location.href`.split("?")[1] if query_string @article = query_string.split("#")[1] query_string = query_string.split("#")[0] if query_string.include?("&") query_array = query_string.split("&") else query_array = query_string.split("&") end query_array.each do |entry| buffer = entry.split("=") @query[(buffer[0].to_s.to_sym)] = buffer[1] end else @article = `window.location.href`.split("#")[1] end # @uuid = GxG.uuid_generate().to_sym @remote_uuid = nil @active = true @display_path = nil @in_busy = false @out_busy = false @pending = {} @channels = {} @buffers = {} @csrf = nil @the_connector = self # Make introduction to host and aquire csrf token the_introduction = new_message({:introduction => @uuid.to_s}) the_introduction[:sender] = @uuid.to_s the_introduction[:to] = @host_prefix.to_s the_introduction.on(:success) do |response| if response[:result] == "OK" if response[:csrf] @csrf = response[:csrf] end if response[:status] == "credentialed" GxG::DISPLAY_DETAILS[:logged_in] = true else GxG::DISPLAY_DETAILS[:logged_in] = false end if response[:relative_url] @relative_url = response[:relative_url] end # if response[:display] == "unavailable" `alert('Display Resource Unavailable')` else @display_path = response[:display] @active = true # Are we logged in at the server and recovering from a crash ?? GxG::DISPATCHER.post_event(:display) do until (`document.readyState`.to_s == "complete") do sleep 0.5 end ::GxG::DISPLAY_DETAILS[:query] = @query ::GxG::DISPLAY_DETAILS[:article] = @article %x{ var do_unload = function (the_event) { Opal.GxG.$eval("GxG::DISPLAY_DETAILS[:socket].close"); if (the_event) { the_event.returnValue = "closing..."; } for (var i = 0; i < 500000000; i++) { } return "closing..."; }; window.addEventListener('unload', do_unload,false); window.addEventListener('onunload', do_unload,false); window.addEventListener('beforeunload', do_unload,false); window.addEventListener('onbeforeunload', do_unload,false); window.onbeforeunload = do_unload; window.beforeunload = do_unload; } # `window.addEventListener('unload', do_unload,false);function do_unload() { Opal.GxG.$eval("GxG::CONNECTION.close"); };` # `window.addEventListener('beforeunload', do_unload,false);function do_unload() { Opal.GxG.$eval("GxG::CONNECTION.close"); };` # `window.addEventListener('onbeforeunload', do_unload,false);function do_unload() { Opal.GxG.$eval("GxG::CONNECTION.close"); };` # Load Page GxG::DISPATCHER.post_event(:display) do # sleep 0.5 ::GxG::Gui::Vdom::navigate_to(`window.location['pathname']`) end # Establish WebSocket Channel: ::GxG::DISPLAY_DETAILS[:socket] = ::GxG::Networking::Socket.new ::GxG::DISPLAY_DETAILS[:socket].open((`window.location['host']` + "/" + @relative_url + "/ws").gsub("//","/")) ::GxG::SOCKET_MONITOR.on(:attach_socket) do |the_uuid| if ::GxG::valid_uuid?(the_uuid) the_message = new_message({:attach_socket => the_uuid.to_s}) the_message[:sender] = @uuid the_message[:to] = @remote_uuid # Review : add fail/succeed code blocks?? GxG::CONNECTION.push(the_message) end end ::GxG::DISPATCHER.add_periodic_timer({:interval => 0.333}) do ::GxG::SOCKET_MONITOR.follow_up end # end # end # end end the_introduction.on(:fail) do |response| @active = false log_error({:error => Exception.new("Communication Error"), :parameters => {:response => response}}) end GxG::DISPATCHER.post_event(:communications) do GxG::CONNECTION.pull(the_introduction) @heart_beat_timer = GxG::DISPATCHER.add_periodic_timer({:interval => 60.0}) do GxG::CONNECTION.heartbeat() end # ???? end # # # `window.addEventListener('unload', do_unload,false);function do_unload() { Opal.GxG.$eval("GxG::CONNECTION.close"); };` # `window.addEventListener('beforeunload', do_unload,false);function do_unload() { Opal.GxG.$eval("GxG::CONNECTION.close"); };` # `window.addEventListener('onbeforeunload', do_unload,false);function do_unload() { Opal.GxG.$eval("GxG::CONNECTION.close"); };` # `window.onbeforeunload = function do_reload (the_event) { # Opal.eval("GxG::CONNECTION.close"); # the_event.returnValue = "closing..."; # return "closing..."; # }; # ` # self end # end end end