rsence 2.0.9.23 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
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 )