rsence 2.0.9.23 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. data/INSTALL.rdoc +61 -49
  2. data/README.rdoc +20 -4
  3. data/VERSION +1 -1
  4. data/conf/default_conf.yaml +8 -0
  5. data/conf/rsence_command_strings.yaml +31 -20
  6. data/docs/ExampleGuiPlugin.rdoc +2 -2
  7. data/js/comm/comm.js +27 -5
  8. data/js/comm/transporter/transporter.js +1 -1
  9. data/js/comm/values/values.js +12 -5
  10. data/js/controls/button/button.js +12 -2
  11. data/js/controls/dialogs/alert_sheet/alert_sheet.js +13 -1
  12. data/js/controls/dialogs/confirm_sheet/confirm_sheet.js +13 -2
  13. data/js/controls/dialogs/sheet/sheet.js +35 -28
  14. data/js/controls/imageview/imageview.js +13 -13
  15. data/js/controls/progress/progressindicator/progressindicator.js +5 -5
  16. data/js/controls/sliders/slider/slider.js +4 -31
  17. data/js/controls/stepper/stepper.js +12 -19
  18. data/js/controls/textcontrol/textcontrol.js +0 -50
  19. data/js/controls/textcontrol/themes/default/textcontrol.html +1 -1
  20. data/js/controls/window/window.js +1 -1
  21. data/js/core/elem/elem.js +146 -160
  22. data/js/core/rsence_ns/rsence_ns.js +7 -0
  23. data/js/foundation/control/eventresponder/eventresponder.js +8 -7
  24. data/js/foundation/eventmanager/eventmanager.js +81 -48
  25. data/js/foundation/geom/rect/rect.js +1 -1
  26. data/js/foundation/json_renderer/json_renderer.js +4 -1
  27. data/js/foundation/system/system.js +37 -34
  28. data/js/foundation/view/morphanimation/morphanimation.js +53 -43
  29. data/js/foundation/view/view.js +119 -118
  30. data/js/lists/listitems/listitems.js +10 -10
  31. data/js/lists/propertylist/js.inc +0 -0
  32. data/js/lists/propertylist/propertylist.js +574 -0
  33. data/js/lists/propertylist/propertylisteditor/js.inc +0 -0
  34. data/js/lists/propertylist/propertylisteditor/propertylisteditor.js +233 -0
  35. data/js/lists/radiobuttonlist/radiobuttonlist.js +15 -8
  36. data/js/menus/minimenu/js.inc +0 -0
  37. data/js/menus/minimenu/minimenu.js +139 -0
  38. data/js/menus/minimenu/minimenuitem/js.inc +0 -0
  39. data/js/menus/minimenu/minimenuitem/minimenuitem.js +33 -0
  40. data/js/menus/minimenu/minimenuitem/themes/default/minimenuitem.css +45 -0
  41. data/js/menus/minimenu/minimenuitem/themes/default/minimenuitem.html +4 -0
  42. data/js/menus/minimenu/minimenuitem/themes/default/minimenuitem_checkmark.png +0 -0
  43. data/js/menus/minimenu/themes/default/minimenu.css +63 -0
  44. data/js/menus/minimenu/themes/default/minimenu.html +7 -0
  45. data/js/menus/minimenu/themes/default/minimenu.png +0 -0
  46. data/js/util/reloadapp/reloadapp.js +1 -1
  47. data/lib/conf/argv.rb +40 -11
  48. data/lib/daemon/daemon.rb +63 -22
  49. data/lib/plugins/gui_plugin.rb +28 -31
  50. data/lib/plugins/guiparser.rb +37 -7
  51. data/lib/plugins/plugin.rb +260 -28
  52. data/lib/plugins/plugin_base.rb +14 -0
  53. data/lib/plugins/plugin_plugins.rb +11 -1
  54. data/lib/plugins/pluginmanager.rb +127 -44
  55. data/lib/plugins/plugins.rb +10 -1
  56. data/lib/session/msg.rb +25 -1
  57. data/lib/session/sessionmanager.rb +11 -2
  58. data/lib/session/sessionstorage.rb +14 -14
  59. data/lib/transporter/transporter.rb +29 -13
  60. data/lib/values/hvalue.rb +30 -0
  61. data/plugins/client_pkg/info.yaml +2 -2
  62. data/plugins/{index_html → main}/img/loading.gif +0 -0
  63. data/plugins/{index_html → main}/img/riassence.gif +0 -0
  64. data/plugins/main/info.yaml +5 -4
  65. data/plugins/main/main.rb +180 -24
  66. data/plugins/{index_html → main}/tmpl/index.html +4 -2
  67. data/plugins/ticket/info.yaml +2 -2
  68. data/plugins/ticket/lib/upload.rb +57 -5
  69. data/plugins/ticket/ticket.rb +10 -4
  70. data/setup/welcome/info.yaml +2 -2
  71. data/setup/welcome/text/welcome.html +1 -1
  72. metadata +22 -11
  73. data/plugins/index_html/index_html.rb +0 -120
  74. data/plugins/index_html/info.yaml +0 -18
@@ -38,19 +38,26 @@ module RSence
38
38
  def initialize
39
39
  @config = RSence.config[:transporter_conf]
40
40
  @accept_req = false
41
- @valuemanager = ValueManager.new
42
- @sessions = SessionManager.new( self )
43
41
  core_pkgs = {
44
42
  :core => [:transporter, :session_storage, :session_manager, :value_manager]
45
43
  }
46
- @plugins = PluginManager.new(
47
- RSence.config[:plugin_paths],
48
- self,
49
- RSence.args[:autoupdate],
50
- false,
51
- core_pkgs[:core],
52
- core_pkgs
53
- )
44
+ @plugins = PluginManager.new( {
45
+ :plugin_paths => RSence.config[:plugin_paths],
46
+ :transporter => self,
47
+ :autoreload => RSence.args[:autoupdate],
48
+ :name_prefix => false,
49
+ :resolved_deps => core_pkgs[:core],
50
+ :resolved_categories => core_pkgs
51
+ })
52
+ @valuemanager = ValueManager.new
53
+ @sessions = SessionManager.new( self )
54
+ if RSence.config[:session_conf][:reset_sessions]
55
+ puts "Resetting all sessions..."
56
+ @sessions.reset_sessions()
57
+ else
58
+ @sessions.restore_sessions()
59
+ end
60
+
54
61
  if RSence.launch_pid != Process.pid
55
62
  Process.kill( 'TERM', RSence.launch_pid )
56
63
  end
@@ -180,7 +187,7 @@ module RSence
180
187
  ## Calls the restore_ses of plugins, when a session is restored (page reload with previously active session)
181
188
  if msg.restored_session
182
189
  msg.session[:deps] = []
183
-
190
+
184
191
  if msg.cloned_source
185
192
  begin
186
193
  @plugins.delegate( :cloned_target, msg, msg.cloned_source )
@@ -190,24 +197,29 @@ module RSence
190
197
  xhr_traceback_handler( e, "Transporter::PluginDelegateClonedTargetError: @plugins.delegate 'cloned_target' failed." )
191
198
  end
192
199
  end
193
-
200
+
194
201
  begin
195
202
  @plugins.delegate( :restore_ses, msg )
203
+ msg.session[:plugin_incr] == @plugins.incr
196
204
  rescue => e
197
205
  response_success = false
198
206
  xhr_error_handler( msg, :plugin_delegate_restore_ses_error, e.message )
199
207
  xhr_traceback_handler( e, "Transporter::PluginDelegateRestoreSesError: @plugins.delegate 'restore_ses' failed." )
200
208
  end
201
-
209
+
202
210
  elsif msg.new_session
211
+
203
212
  begin
204
213
  @plugins.delegate( :init_ses, msg )
214
+ msg.session[:plugin_incr] == @plugins.incr
205
215
  rescue => e
206
216
  response_success = false
207
217
  xhr_error_handler( msg, :plugin_delegate_init_ses_error, e.message )
208
218
  xhr_traceback_handler( e, "Transporter::PluginDelegateInitSesError: @plugins.delegate 'init_ses' failed." )
209
219
  end
220
+
210
221
  elsif msg.cloned_targets
222
+
211
223
  begin
212
224
  @plugins.delegate( :cloned_source, msg, msg.cloned_targets )
213
225
  rescue => e
@@ -215,6 +227,10 @@ module RSence
215
227
  xhr_error_handler( msg, :plugin_delegate_cloned_source_error, e.message )
216
228
  xhr_traceback_handler( e, "Transporter::PluginDelegateClonedSourceError: @plugins.delegate 'cloned_source' failed." )
217
229
  end
230
+
231
+ elsif msg.refresh_page?( @plugins.incr ) and @config[:client_autoreload]
232
+ # Forces the client to reload, if plugins are incremented
233
+ msg.reply("window.location.reload( true );")
218
234
  end
219
235
 
220
236
  ## Calls validators for changed values
data/lib/values/hvalue.rb CHANGED
@@ -122,6 +122,14 @@ module RSence
122
122
  return true
123
123
  end
124
124
 
125
+ # Checks, if the plugin_name and method_name pairing is already bound with the bind method. Returns true or false.
126
+ def bound?( plugin_name, method_name )
127
+ plugin_name = plugin_name.to_sym unless plugin_name.class == Symbol
128
+ method_name = method_name.to_sym unless method_name.class == Symbol
129
+ return false unless @members.has_key?(plugin_name)
130
+ return @members[plugin_name].include?(method_name)
131
+ end
132
+
125
133
  # Releases the responder of the value, both params as in bind, but optional +method_name+ can be omitted, matching all methods bound to the +plugin_name+.
126
134
  # @param [Symbol] plugin_name The name of the plugin acting as a responder to the value.
127
135
  # @param [Symbol] method_name The name of the method of the plugin acting as a responder to the value.
@@ -135,6 +143,9 @@ module RSence
135
143
  @members.delete( plugin_name )
136
144
  else
137
145
  @members[plugin_name].slice!(@members[plugin_name].index( method_name )) if @members[plugin_name].include?(method_name)
146
+ if @members[plugin_name].empty?
147
+ @members.delete( plugin_name )
148
+ end
138
149
  end
139
150
  return true
140
151
  end
@@ -211,6 +222,25 @@ module RSence
211
222
  end
212
223
  end
213
224
 
225
+ # Sets the key of the hash data of the value, the change will be synced with the client.
226
+ # @param [Message] msg The {Message} instance.
227
+ # @param [String] key The key of data to change
228
+ # @param [#to_json] data Any data that can be mapped to JSON and handled by the client.
229
+ # @param [Boolean] dont_tell_client Doesn't notify the client about the change, if true.
230
+ def set_key( msg, key, data, dont_tell_client=false )
231
+
232
+ @data[key] = data
233
+
234
+ # won't tell the client about the change, usually not needed
235
+ unless dont_tell_client
236
+ ## update the flags
237
+ @sync = false
238
+ @is_valid = true
239
+
240
+ add_to_sync( msg )
241
+ end
242
+ end
243
+
214
244
  # @private Tell the client that the value changed.
215
245
  def to_client( msg )
216
246
  if @is_new_to_client
@@ -3,7 +3,7 @@
3
3
  title: Client Resource Package
4
4
 
5
5
  # The human-readable version of the package
6
- version: 2.0.0
6
+ version: 2.1.0
7
7
 
8
8
  # A brief description of the package (rdoc formatting supported)
9
9
  description: |
@@ -17,7 +17,7 @@ description: |
17
17
  reloadable: false
18
18
 
19
19
  # System version requirement.
20
- sys_version: '>= 2.0.0'
20
+ sys_version: '>= 2.1.0'
21
21
 
22
22
  category: :system
23
23
 
File without changes
File without changes
@@ -3,16 +3,17 @@
3
3
  title: Main Plugin
4
4
 
5
5
  # The human-readable version of the package
6
- version: 2.0.0
6
+ version: 2.1.0
7
7
 
8
8
  # A brief description of the package (rdoc formatting supported)
9
9
  description: |
10
- The main plugin manages the flow of communication between client and server.
10
+ The main plugin manages the flow of communication between client and server
11
+ as well as initializes a startup web page, if no other plugin does so.
11
12
 
12
13
  # System version requirement.
13
- sys_version: '>= 2.0.0'
14
+ sys_version: '>= 2.1.0'
14
15
 
15
16
  category: :system
16
17
 
17
- depends_on: :index_html
18
+ depends_on: :client_pkg
18
19
 
data/plugins/main/main.rb CHANGED
@@ -7,9 +7,10 @@
7
7
  ##
8
8
 
9
9
 
10
- # The MainPlugin is accessible as +@plugins.main+ from other plugins.
10
+ # The MainPlugin is accessible as +@plugins.main+ and just +main+ from other plugins.
11
11
  #
12
12
  # = MainPlugin provides mainly client setup and the following services:
13
+ # * The root html page, which includes the scripts and sets up client startup variables.
13
14
  # * The url of the client as a HValue, including the anchor.
14
15
  # * Accessible via +msg.session[:main][:location_href]+
15
16
  # * The local time of the client's web browser as a HValue, as seconds since epoch.
@@ -18,38 +19,165 @@
18
19
  # * Provides the +#init_ui+ event for plugins that respond to it.
19
20
  class MainPlugin < Plugin
20
21
 
21
- # @private Internal structures, binds configuration data as instance variables
22
+ # # Session-specific index page renderer (unused)
23
+ # def session_index_html( request, response )
24
+ #
25
+ # ses_key = @randgen.gen
26
+ # sha_key = ''
27
+ #
28
+ # buffer = [
29
+ # "var qP=function(cmd){COMM.Queue.push(cmd);};"
30
+ # ]
31
+ #
32
+ # req_num = 0
33
+ #
34
+ # 3.times do |req_num|
35
+ # sha_key = Digest::SHA1.hexdigest( ses_key + sha_key )
36
+ # msg = @plugins.transporter.xhr(
37
+ # request, response, {
38
+ # :servlet => true,
39
+ # :cookie => (req_num==0),
40
+ # :query => {
41
+ # 'ses_key' => "#{req_num}:.o.:#{sha_key}"
42
+ # }
43
+ # }
44
+ # )
45
+ # buffer += msg.value_buffer
46
+ # msg.buffer.each do |buffer_item|
47
+ # buffer.push( "qP(function(){#{buffer_item};});")
48
+ # end
49
+ # ses_key = msg.ses_key
50
+ # end
51
+ #
52
+ # buffer.unshift( "COMM.Session.newKey(#{ses_key.to_json});" )
53
+ # buffer.unshift( "COMM.Session.sha_key=#{sha_key.to_json};" )
54
+ # buffer.unshift( "COMM.Session.req_num=#{req_num};" )
55
+ #
56
+ # index_html = render_index_html
57
+ #
58
+ # return index_html.gsub('__STARTUP_SEQUENCE__', buffer.join("\n") )
59
+ # end
60
+
61
+
62
+ # Index page renderer
63
+ def render_index_html
64
+
65
+ index_html = @index_html_src.clone
66
+
67
+ client_rev = client_pkg.client_cache.client_rev
68
+ deps_src = ''
69
+ @conf[:deps].each do |dep|
70
+ deps_src += %{<script src="#{dep}" type="text/javascript"></script>}
71
+ end
72
+ client_base = File.join(@bconf[:h],client_rev)
73
+
74
+ index_html.gsub!( '__CLIENT_BASE__', client_base )
75
+ index_html.gsub!( '__DEFAULT_TITLE__', @conf[:title] )
76
+ index_html.gsub!( '__CLIENT_REV__', client_rev )
77
+ index_html.gsub!( '__CLIENT_HELLO__', @bconf[:hello] )
78
+ index_html.gsub!( '__NOSCRIPT__', @conf[:noscript] )
79
+ index_html.gsub!( '__SCRIPT_DEPS__', deps_src )
80
+
81
+ return index_html
82
+ end
83
+
84
+
85
+
86
+ ### Top-level plugin events:
87
+
88
+
89
+ # Binds configuration data as instance variables
22
90
  def init
23
91
  super
92
+ @plugins.register_alias( :main, :index_html )
93
+ @randgen = RandGen.new( 40 )
94
+ ::RSence.config[:index_html][:instance] = self
24
95
  @conf = ::RSence.config[:index_html]
25
96
  @bconf = ::RSence.config[:broker_urls]
26
97
  @goodbye_uri = File.join(@bconf[:hello],'goodbye')
27
98
  end
28
99
 
29
- # @private Internal structures, matches the "hello/goodbye" session termination request
30
- def match( uri, request_type )
31
- if request_type == :post and uri == @goodbye_uri
100
+ # Opens and renders the index page template
101
+ def open
102
+ super
103
+ @index_html_src = file_read( ::RSence.config[:index_html][:index_tmpl] )
104
+ render_index_html
105
+ end
106
+
107
+ # Frees the ticket resource id of the "loading" gif image.
108
+ def close
109
+ super
110
+ @plugins[:ticket].del_rsrc( @loading_gif_id )
111
+ end
112
+
113
+
114
+
115
+ ### Servlet features; responds to GET / as well as POST /hello/goodbye
116
+
117
+
118
+ # @private Internal structures, matches the "hello/goodbye" session termination POST request and the "/" index html page GET request
119
+ def match( uri, method )
120
+ if uri == ::RSence.config[:index_html][:respond_address] and method == :get
32
121
  return true
122
+ elsif req_type == :post and uri == @goodbye_uri
123
+ return true
124
+ else
125
+ return false
33
126
  end
34
- return false
35
127
  end
36
128
 
37
- # @private Internal structures, score for the "hello/goodbye" session termination request
38
- def score; 100; end
129
+ # @private Internal structures, score for the "hello/goodbye" session termination request and the default index html page
130
+ def score
131
+ return 1000 # allows overriding with anything with a score below 1000
132
+ end
133
+
134
+ # Inspects the http request header to decide if the browser supports gzip compressed responses.
135
+ def support_gzip( header )
136
+ return false if not ::RSence.config[:no_gzip]
137
+ return false if not header.has_key?('accept-encoding')
138
+ return header['accept-encoding'].include?('gzip')
139
+ end
140
+
141
+ # Outputs the startup web page.
142
+ def get( req, response, ses )
143
+ index_html = render_index_html
144
+
145
+ response.status = 200
146
+
147
+ response['Content-Type'] = 'text/html; charset=UTF-8'
148
+ response['Date'] = httime( Time.now )
149
+ response['Server'] = 'RSence'
150
+ response['Cache-Control'] = 'no-cache'
151
+
152
+ if support_gzip( req.header )
153
+ index_gzip = GZString.new('')
154
+ gzwriter = Zlib::GzipWriter.new( index_gzip, 9 )
155
+ gzwriter.write( index_html )
156
+ gzwriter.close
157
+ response['Content-Length'] = index_gzip.length
158
+ response['Content-Encoding'] = 'gzip'
159
+ response.body = index_gzip
160
+ else
161
+ response['Content-Length'] = index_html.length
162
+ response.body = index_html
163
+ end
164
+ end
39
165
 
40
- # @private Internal structures, handler for the "hello/goodbye" session termination request
166
+ # Returns the "hello/goodbye" session termination request
41
167
  def post( req, res, ses )
42
168
  @plugins.sessions.expire_ses_by_req( req, res )
43
169
  end
44
170
 
45
- # @private url_responder gets called whenever the
46
- # page location.href changes, enabled virtual uris
47
- # to enable back/forward/bookmarking in browsers,
48
- # when software is coded to support it.
171
+
172
+
173
+ ### Features accessible from other plugins:
174
+
175
+ # The +#url_responder+ gets called whenever the anchor (pound) of location.href changes.
176
+ # It enables virtual url events for back/forward buttons and bookmarking in browsers whenever utilized.
49
177
  #
50
178
  # Client-side support is included in js/url_responder.js
51
179
  #
52
- # Also allows virtual-host -like behavior, if software is coded to support it.
180
+ # Also allows virtual-host -like behavior if utilized.
53
181
  def url_responder(msg,location_href)
54
182
 
55
183
  ses = get_ses( msg )
@@ -84,13 +212,30 @@ class MainPlugin < Plugin
84
212
  end
85
213
 
86
214
 
87
- # @private new session initialization, called just once per session.
215
+ # Returns base url of browser (before the '#' sign)
216
+ def url( msg )
217
+ get_ses( msg )[:url][0]
218
+ end
219
+
220
+
221
+ # Returns pound url of browser (after the '#' sign)
222
+ def pound( msg )
223
+ get_ses( msg )[:url][1]
224
+ end
225
+
226
+
227
+
228
+ ### Session events:
229
+
230
+
231
+ # New session initialization, called just once per session.
88
232
  def init_ses(msg)
89
233
  super
90
234
  restore_ses( msg )
91
235
  end
92
236
 
93
- # @private called once when a session is restored using the cookie's ses_key
237
+
238
+ # Called once when a session is restored or cloned using the cookie's ses_key
94
239
  def restore_ses(msg)
95
240
  super
96
241
  ## Resets session data to defaults
@@ -101,6 +246,7 @@ class MainPlugin < Plugin
101
246
  ses[:poll_mode] = true
102
247
  end
103
248
 
249
+
104
250
  # Interface for adding delayed calls
105
251
  #
106
252
  # When adding a delayed call, use an Array to define a plugin/method with optional arguments that will be called on the next request. The client will call back immediately when a delayed call is pending. The first param of the method is a +msg+. Don't include the +msg+ of the current request in params, it will be inserted automatically for the delayed call.
@@ -125,6 +271,7 @@ class MainPlugin < Plugin
125
271
  get_ses( msg )[:delayed_calls].push( params )
126
272
  end
127
273
 
274
+
128
275
  # @private Initializes the client-side COMM.urlResponder and sesWatcher
129
276
  def boot0( msg, ses )
130
277
 
@@ -148,18 +295,27 @@ class MainPlugin < Plugin
148
295
 
149
296
  end
150
297
 
298
+
151
299
  # @private Calls the init_ui method of each loaded plugin and removes the loading -message
152
300
  def boot1( msg, ses )
153
301
  # Delegates the init_ui method to each plugin to signal bootstrap completion.
154
302
  msg.plugins.delegate( 'init_ui', msg ) unless ses[:dont_init_ui]
155
303
  end
156
304
 
157
- # @private
305
+
306
+ # Disables the init_ui event.
158
307
  def dont_init_ui( msg )
159
308
  get_ses( msg )[:dont_init_ui] = true
160
309
  end
161
310
 
162
- # @private Flushes commands in the :delayed_calls array
311
+
312
+ # Enables the init_ui event.
313
+ def do_init_ui( msg )
314
+ get_ses( msg )[:dont_init_ui] = false
315
+ end
316
+
317
+
318
+ # Flushes commands in the :delayed_calls array
163
319
  def flush_delayed( msg, ses )
164
320
  ## Limits the amount of delayed calls to process to 4.
165
321
  ## Prevents the client from choking even when the server
@@ -213,10 +369,10 @@ class MainPlugin < Plugin
213
369
  end
214
370
  end
215
371
 
216
- # @private When nothing is delayed and the second poll has been made (init_ui called),
217
- # sets the client to non-polling-mode, having only HValue
218
- # changes trigger new requests. SesWatcher makes this happen
219
- # regularly.
372
+ # When nothing is delayed and the second poll has been made (init_ui called),
373
+ # sets the client to non-polling-mode, having only value synchronization trigger
374
+ # new requests. On the client, SesWatcher forces the change by sending the
375
+ # client time periodically.
220
376
  def end_polling( msg, ses )
221
377
  if ses[:poll_mode] == true
222
378
  msg.reply "COMM.Transporter.poll(0);"
@@ -224,7 +380,7 @@ class MainPlugin < Plugin
224
380
  end
225
381
  end
226
382
 
227
- # @private Starts polling.
383
+ # Starts polling mode.
228
384
  def start_polling( msg, ses )
229
385
  if ses[:poll_mode] == false
230
386
  msg.reply( "COMM.Transporter.poll(#{::RSence.config[:transporter_conf][:client_poll_priority]});" )
@@ -232,7 +388,7 @@ class MainPlugin < Plugin
232
388
  end
233
389
  end
234
390
 
235
- # @private called on every request of an active, valid session
391
+ # Called on every request of an active, valid session
236
392
  def idle(msg)
237
393
 
238
394
  ses = get_ses( msg )