glimmer-dsl-opal 0.6.0 → 0.7.3

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 (49) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +51 -0
  3. data/README.md +428 -18
  4. data/VERSION +1 -1
  5. data/app/assets/stylesheets/{glimmer.css → glimmer/glimmer.css} +1 -1
  6. data/lib/display.rb +31 -0
  7. data/lib/file.rb +29 -0
  8. data/lib/glimmer-dsl-opal.rb +33 -5
  9. data/lib/glimmer-dsl-opal/ext/date.rb +11 -0
  10. data/lib/glimmer-dsl-opal/ext/struct.rb +37 -0
  11. data/lib/glimmer-dsl-opal/samples/elaborate/tic_tac_toe.rb +23 -0
  12. data/lib/glimmer-dsl-opal/samples/hello/hello_table.rb +283 -0
  13. data/lib/glimmer-dsl-swt.rb +20 -35
  14. data/lib/glimmer/data_binding/table_items_binding.rb +32 -19
  15. data/lib/glimmer/dsl/opal/block_property_expression.rb +41 -0
  16. data/lib/glimmer/dsl/opal/custom_widget_expression.rb +1 -1
  17. data/lib/glimmer/dsl/opal/dsl.rb +2 -0
  18. data/lib/glimmer/dsl/opal/widget_expression.rb +7 -3
  19. data/lib/glimmer/engine.rb +1 -1
  20. data/lib/glimmer/swt/button_proxy.rb +5 -5
  21. data/lib/glimmer/swt/color_proxy.rb +45 -45
  22. data/lib/glimmer/swt/combo_proxy.rb +42 -3
  23. data/lib/glimmer/swt/composite_proxy.rb +7 -3
  24. data/lib/glimmer/swt/control_editor.rb +54 -0
  25. data/lib/glimmer/swt/date_time_proxy.rb +71 -5
  26. data/lib/glimmer/swt/display_proxy.rb +6 -2
  27. data/lib/glimmer/swt/fill_layout_proxy.rb +1 -1
  28. data/lib/glimmer/swt/font_proxy.rb +4 -4
  29. data/lib/glimmer/swt/label_proxy.rb +2 -2
  30. data/lib/glimmer/swt/layout_data_proxy.rb +13 -10
  31. data/lib/glimmer/swt/layout_proxy.rb +5 -5
  32. data/lib/glimmer/swt/list_proxy.rb +2 -2
  33. data/lib/glimmer/swt/message_box_proxy.rb +4 -2
  34. data/lib/glimmer/swt/property_owner.rb +2 -2
  35. data/lib/glimmer/swt/shell_proxy.rb +8 -0
  36. data/lib/glimmer/swt/tab_folder_proxy.rb +2 -2
  37. data/lib/glimmer/swt/tab_item_proxy.rb +7 -7
  38. data/lib/glimmer/swt/table_column_proxy.rb +71 -12
  39. data/lib/glimmer/swt/table_editor.rb +65 -0
  40. data/lib/glimmer/swt/table_item_proxy.rb +50 -7
  41. data/lib/glimmer/swt/table_proxy.rb +581 -14
  42. data/lib/glimmer/swt/text_proxy.rb +49 -1
  43. data/lib/glimmer/swt/widget_proxy.rb +120 -22
  44. data/lib/glimmer/ui/custom_widget.rb +8 -8
  45. data/lib/net/http.rb +1 -5
  46. data/lib/os.rb +36 -0
  47. data/lib/uri.rb +3 -3
  48. metadata +31 -10
  49. data/lib/glimmer/data_binding/ext/observable_model.rb +0 -40
@@ -20,11 +20,59 @@ module Glimmer
20
20
  event: 'keyup',
21
21
  event_handler: -> (event_listener) {
22
22
  -> (event) {
23
+ # TODO consider unifying this event handler with on_key_pressed by relying on its result instead of hooking another keyup event
24
+ if @last_key_pressed_event.nil? || @last_key_pressed_event.doit
25
+ @text = event.target.value
26
+ event_listener.call(event)
27
+ else
28
+ # TODO Fix doit false, it's not stopping input
29
+ event.prevent
30
+ event.prevent_default
31
+ event.stop_propagation
32
+ event.stop_immediate_propagation
33
+ end
34
+ }
35
+ }
36
+ },
37
+ 'on_key_pressed' => {
38
+ event: 'keydown',
39
+ event_handler: -> (event_listener) {
40
+ -> (event) {
41
+ @last_key_pressed_event = event
23
42
  @text = event.target.value
43
+ # TODO generalize this solution to all widgets that support key presses
44
+ # TODO support event.location once DOM3 is supported by opal-jquery
45
+ event.define_singleton_method(:keyCode) {event.which}
46
+ event.define_singleton_method(:key_code, &event.method(:keyCode))
47
+ event.define_singleton_method(:character) {event.which.chr}
48
+ event.define_singleton_method(:stateMask) do
49
+ state_mask = 0
50
+ state_mask |= SWTProxy[:alt] if event.alt_key
51
+ state_mask |= SWTProxy[:ctrl] if event.ctrl_key
52
+ state_mask |= SWTProxy[:shift] if event.shift_key
53
+ state_mask |= SWTProxy[:command] if event.meta_key
54
+ state_mask
55
+ end
56
+ event.define_singleton_method(:state_mask, &event.method(:stateMask))
57
+ doit = true
58
+ event.define_singleton_method(:doit=) do |value|
59
+ doit = value
60
+ end
61
+ event.define_singleton_method(:doit) { doit }
24
62
  event_listener.call(event)
63
+
64
+ # TODO Fix doit false, it's not stopping input
65
+ unless doit
66
+ event.prevent
67
+ event.prevent_default
68
+ event.stop_propagation
69
+ event.stop_immediate_propagation
70
+ end
71
+
72
+ doit
25
73
  }
26
74
  }
27
- }
75
+ },
28
76
  }
29
77
  end
30
78
 
@@ -29,13 +29,16 @@ module Glimmer
29
29
  include Glimmer
30
30
  include PropertyOwner
31
31
 
32
- attr_reader :parent, :args, :path, :children, :enabled, :foreground, :background, :font, :focus
32
+ attr_reader :parent, :args, :path, :children, :enabled, :foreground, :background, :font, :focus, :disposed?, :rendered
33
+ alias isDisposed disposed?
34
+ alias is_disposed disposed?
35
+ alias rendered? rendered
33
36
 
34
37
  class << self
35
38
  # Factory Method that translates a Glimmer DSL keyword into a WidgetProxy object
36
- def for(keyword, parent, args)
39
+ def for(keyword, parent, args, block)
37
40
  the_widget_class = widget_class(keyword)
38
- the_widget_class.respond_to?(:create) ? the_widget_class.create(keyword, parent, args) : the_widget_class.new(parent, args)
41
+ the_widget_class.respond_to?(:create) ? the_widget_class.create(keyword, parent, args, block) : the_widget_class.new(parent, args, block)
39
42
  end
40
43
 
41
44
  def widget_class(keyword)
@@ -97,11 +100,13 @@ module Glimmer
97
100
  # end,
98
101
  }
99
102
 
100
- def initialize(parent, args)
103
+ def initialize(parent, args, block)
101
104
  @parent = parent
102
105
  @args = args
106
+ @block = block
103
107
  @children = Set.new # TODO consider moving to composite
104
108
  @enabled = true
109
+ @data = {}
105
110
  DEFAULT_INITIALIZERS[self.class.underscored_widget_name(self)]&.call(self)
106
111
  @parent.post_initialize_child(self) # TODO rename to post_initialize_child to be closer to glimmer-dsl-swt terminology
107
112
  end
@@ -112,17 +117,52 @@ module Glimmer
112
117
  child.render
113
118
  end
114
119
 
120
+ # Executes for the parent of a child that just got disposed
121
+ def post_dispose_child(child)
122
+ @children&.delete(child)
123
+ end
124
+
115
125
  # Executes at the closing of a parent widget curly braces after all children/properties have been added/set
116
126
  def post_add_content
117
127
  # No Op by default
118
128
  end
119
129
 
130
+ def set_data(key=nil, value)
131
+ @data[key] = value
132
+ end
133
+ alias setData set_data
134
+ alias data= set_data
135
+
136
+ def get_data(key=nil)
137
+ @data[key]
138
+ end
139
+ alias getData get_data
140
+ alias data get_data
141
+
120
142
  def css_classes
121
143
  dom_element.attr('class').to_s.split
122
144
  end
123
145
 
124
146
  def dispose
147
+ remove_all_listeners
125
148
  Document.find(path).remove
149
+ parent&.post_dispose_child(self)
150
+ # TODO fire on_widget_disposed listener
151
+ @disposed = true
152
+ end
153
+
154
+ def remove_all_listeners
155
+ effective_observation_request_to_event_mapping.keys.each do |keyword|
156
+ effective_observation_request_to_event_mapping[keyword].to_collection.each do |mapping|
157
+ observation_requests[keyword].to_a.each do |event_listener|
158
+ event = mapping[:event]
159
+ event_handler = mapping[:event_handler]
160
+ event_element_css_selector = mapping[:event_element_css_selector]
161
+ the_listener_dom_element = event_element_css_selector ? Element[event_element_css_selector] : listener_dom_element
162
+ the_listener_dom_element.off(event)
163
+ end
164
+ end
165
+ end
126
166
  end
127
167
 
128
168
  def path
@@ -171,31 +211,52 @@ module Glimmer
171
211
  @parent.path
172
212
  end
173
213
 
174
- def render
214
+ def parent_dom_element
215
+ Document.find(parent_path)
216
+ end
217
+
218
+ def render(custom_parent_dom_element = nil)
219
+ the_parent_dom_element = custom_parent_dom_element || parent_dom_element
175
220
  old_element = dom_element
176
221
  brand_new = @dom.nil? || old_element.empty?
177
- build_dom
222
+ build_dom(!custom_parent_dom_element) # TODO handle custom parent layout by passing parent instead of parent dom element
178
223
  if brand_new
179
- Document.find(parent_path).append(@dom)
224
+ the_parent_dom_element.append(@dom)
180
225
  else
181
226
  old_element.replace_with(@dom)
182
227
  end
183
- @observation_requests&.clone&.each do |keyword, event_listener_set|
228
+ observation_requests&.clone&.each do |keyword, event_listener_set|
184
229
  event_listener_set.each do |event_listener|
185
- @observation_requests[keyword].delete(event_listener)
230
+ observation_requests[keyword].delete(event_listener) # TODO look into the implications of this and if it's needed.
186
231
  handle_observation_request(keyword, &event_listener)
187
232
  end
188
233
  end
189
234
  children.each do |child|
190
235
  child.render
191
236
  end
237
+ @rendered = true
238
+ content_on_render_blocks.each { |content_block| content(&content_block) }
192
239
  end
193
240
  alias redraw render
194
241
 
195
- def build_dom
242
+ def content_on_render_blocks
243
+ @content_on_render_blocks ||= []
244
+ end
245
+
246
+ def add_content_on_render(&content_block)
247
+ if rendered?
248
+ content_block.call
249
+ else
250
+ content_on_render_blocks << content_block
251
+ end
252
+ end
253
+
254
+ def build_dom(layout=true)
255
+ # TODO consider passing parent element instead and having table item include a table cell widget only for opal
196
256
  @dom = nil
197
257
  @dom = dom
198
258
  @dom = @parent.layout.dom(@dom) if @parent.respond_to?(:layout) && @parent.layout
259
+ @dom
199
260
  end
200
261
 
201
262
  def content(&block)
@@ -207,6 +268,21 @@ module Glimmer
207
268
  {}
208
269
  end
209
270
 
271
+ def effective_observation_request_to_event_mapping
272
+ default_observation_request_to_event_mapping.merge(observation_request_to_event_mapping)
273
+ end
274
+
275
+ def default_observation_request_to_event_mapping
276
+ {
277
+ 'on_focus_gained' => {
278
+ event: 'focus',
279
+ },
280
+ 'on_focus_lost' => {
281
+ event: 'blur',
282
+ },
283
+ }
284
+ end
285
+
210
286
  def name
211
287
  self.class.name.split('::').last.underscore.sub(/_proxy$/, '').gsub('_', '-')
212
288
  end
@@ -270,10 +346,6 @@ module Glimmer
270
346
  element
271
347
  end
272
348
 
273
- def parent_dom_element
274
- Document.find(parent_path)
275
- end
276
-
277
349
  def listener_path
278
350
  path
279
351
  end
@@ -295,26 +367,36 @@ module Glimmer
295
367
  end
296
368
  end
297
369
 
298
- def handle_observation_request(keyword, &event_listener)
299
- return unless observation_request_to_event_mapping.keys.include?(keyword)
370
+ def observation_requests
300
371
  @observation_requests ||= {}
301
- @observation_requests[keyword] ||= Set.new
372
+ end
373
+
374
+ def handle_observation_request(keyword, &event_listener)
375
+ return unless effective_observation_request_to_event_mapping.keys.include?(keyword)
302
376
  event = nil
303
377
  delegate = nil
304
- [observation_request_to_event_mapping[keyword]].flatten.each do |mapping|
305
- @observation_requests[keyword] << event_listener
378
+ effective_observation_request_to_event_mapping[keyword].to_collection.each do |mapping|
379
+ observation_requests[keyword] ||= Set.new
380
+ observation_requests[keyword] << event_listener
306
381
  event = mapping[:event]
307
382
  event_handler = mapping[:event_handler]
383
+ event_element_css_selector = mapping[:event_element_css_selector]
308
384
  potential_event_listener = event_handler&.call(event_listener)
309
385
  event_listener = potential_event_listener || event_listener
310
- delegate = listener_dom_element.on(event, &event_listener)
386
+ async_event_listener = lambda do |event|
387
+ Async::Task.new do
388
+ event_listener.call(event)
389
+ end
390
+ end
391
+ the_listener_dom_element = event_element_css_selector ? Element[event_element_css_selector] : listener_dom_element
392
+ delegate = the_listener_dom_element.on(event, &async_event_listener)
311
393
  end
312
394
  # TODO update code below for new WidgetProxy API
313
395
  EventListenerProxy.new(element_proxy: self, event: event, selector: selector, delegate: delegate)
314
396
  end
315
397
 
316
398
  def add_observer(observer, property_name)
317
- property_listener_installers = self.class.ancestors.map {|ancestor| widget_property_listener_installers[ancestor]}.compact
399
+ property_listener_installers = self.class&.ancestors&.to_a.map {|ancestor| widget_property_listener_installers[ancestor]}.compact
318
400
  widget_listener_installers = property_listener_installers.map{|installer| installer[property_name.to_s.to_sym]}.compact if !property_listener_installers.empty?
319
401
  widget_listener_installers.to_a.each do |widget_listener_installer|
320
402
  widget_listener_installer.call(observer)
@@ -326,6 +408,14 @@ module Glimmer
326
408
  super(attribute_name, *args) # PropertyOwner
327
409
  end
328
410
 
411
+ def method_missing(method, *args, &block)
412
+ if method.to_s.start_with?('on_')
413
+ handle_observation_request(method, &block)
414
+ else
415
+ super(method, *args, &block)
416
+ end
417
+ end
418
+
329
419
  def apply_property_type_converters(attribute_name, args)
330
420
  if args.count == 1
331
421
  value = args.first
@@ -466,7 +556,7 @@ module Glimmer
466
556
  # }
467
557
  # end,
468
558
  # },
469
- DateTimeProxy => { #radio?
559
+ DateTimeProxy => {
470
560
  :date_time => lambda do |observer|
471
561
  on_widget_selected { |selection_event|
472
562
  observer.call(date_time)
@@ -480,6 +570,13 @@ module Glimmer
480
570
  }
481
571
  end
482
572
  },
573
+ TableProxy => {
574
+ :selection => lambda do |observer|
575
+ on_widget_selected { |selection_event|
576
+ observer.call(selection_event.table_item.get_data) # TODO ensure selection doesn't conflict with editing
577
+ }
578
+ end,
579
+ },
483
580
  # Java::OrgEclipseSwtWidgets::MenuItem => {
484
581
  # :selection => lambda do |observer|
485
582
  # on_widget_selected { |selection_event|
@@ -501,6 +598,7 @@ module Glimmer
501
598
  end
502
599
  end
503
600
 
601
+ require 'glimmer/swt/display_proxy'
504
602
  require 'glimmer/swt/browser_proxy'
505
603
  require 'glimmer/swt/button_proxy'
506
604
  require 'glimmer/swt/combo_proxy'
@@ -1,5 +1,5 @@
1
1
  # Copyright (c) 2020 Andy Maleh
2
- #
2
+ #
3
3
  # Permission is hereby granted, free of charge, to any person obtaining
4
4
  # a copy of this software and associated documentation files (the
5
5
  # "Software"), to deal in the Software without restriction, including
@@ -7,10 +7,10 @@
7
7
  # distribute, sublicense, and/or sell copies of the Software, and to
8
8
  # permit persons to whom the Software is furnished to do so, subject to
9
9
  # the following conditions:
10
- #
10
+ #
11
11
  # The above copyright notice and this permission notice shall be
12
12
  # included in all copies or substantial portions of the Software.
13
- #
13
+ #
14
14
  # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
15
  # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
16
  # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
@@ -89,7 +89,7 @@ module Glimmer
89
89
  def included(klass)
90
90
  klass.extend(ClassMethods)
91
91
  unless klass.name.include?('Glimmer::UI::CustomShell')
92
- klass.include(Glimmer)
92
+ klass.include(Glimmer)
93
93
  Glimmer::UI::CustomWidget.add_custom_widget_namespaces_for(klass)
94
94
  end
95
95
  end
@@ -107,7 +107,7 @@ module Glimmer
107
107
  end
108
108
  begin
109
109
  constant = result.const_get(namespace)
110
- return constant if constant.ancestors.include?(Glimmer::UI::CustomWidget)
110
+ return constant if constant&.respond_to?(:ancestors) && constant&.ancestors&.to_a.include?(Glimmer::UI::CustomWidget)
111
111
  constant
112
112
  rescue => e
113
113
  # Glimmer::Config.logger.debug {"#{e.message}\n#{e.backtrace.join("\n")}"}
@@ -158,7 +158,7 @@ module Glimmer
158
158
  args = []
159
159
  end
160
160
  options ||= {}
161
- args = options.delete('swt_style').split(',').map(&:to_sym) if options['swt_style']
161
+ args = options.delete('swt_style').split(',').map(&:to_sym) if options['swt_style']
162
162
  @args = args
163
163
  @swt_style = SWT::SWTProxy[*@args]
164
164
  options ||= {}
@@ -244,7 +244,7 @@ module Glimmer
244
244
 
245
245
  def has_style?(symbol)
246
246
  @args.include?(symbol) # not a very solid implementation. Bring SWT constants eventually
247
- end
247
+ end
248
248
 
249
249
  def async_exec(&block)
250
250
  SWT::DisplayProxy.instance.async_exec(&block)
@@ -273,7 +273,7 @@ module Glimmer
273
273
  end
274
274
  end
275
275
 
276
- alias local_respond_to? respond_to?
276
+ alias local_respond_to? respond_to?
277
277
  def respond_to?(method, *args, &block)
278
278
  super or
279
279
  can_handle_observation_request?(method) or
@@ -7,11 +7,7 @@ module Net
7
7
  # Note: ignore Protocol superclass for now
8
8
  class HTTP
9
9
  def post_form(uri, params)
10
- # pd uri.scheme
11
- # pd uri.host
12
- # pd uri.path
13
- # pd uri.query
14
- # pd params
10
+ # TODO
15
11
  end
16
12
  end
17
13
  end
@@ -0,0 +1,36 @@
1
+ # Copyright (c) 2020 Andy Maleh
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining
4
+ # a copy of this software and associated documentation files (the
5
+ # "Software"), to deal in the Software without restriction, including
6
+ # without limitation the rights to use, copy, modify, merge, publish,
7
+ # distribute, sublicense, and/or sell copies of the Software, and to
8
+ # permit persons to whom the Software is furnished to do so, subject to
9
+ # the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be
12
+ # included in all copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+
22
+ class OS
23
+ class << self
24
+ def windows?
25
+ # No Op in Opal
26
+ end
27
+
28
+ def mac?
29
+ # No Op in Opal
30
+ end
31
+
32
+ def linux?
33
+ # No Op in Opal
34
+ end
35
+ end
36
+ end
data/lib/uri.rb CHANGED
@@ -6,7 +6,7 @@ module URI
6
6
 
7
7
  def initialize(url)
8
8
  @url = url
9
- uri_match = url.match(REGEX)
9
+ uri_match = url.match(REGEX).to_a
10
10
  @scheme = uri_match[1]
11
11
  @host = uri_match[2]
12
12
  @path = uri_match[3]
@@ -16,7 +16,7 @@ module URI
16
16
 
17
17
  def to_s
18
18
  url
19
- end
19
+ end
20
20
  end
21
21
 
22
22
  TBLENCWWWCOMP_ = {"\u0000"=>"%00", "\u0001"=>"%01", "\u0002"=>"%02", "\u0003"=>"%03", "\u0004"=>"%04", "\u0005"=>"%05", "\u0006"=>"%06", "\a"=>"%07", "\b"=>"%08", "\t"=>"%09", "\n"=>"%0A", "\v"=>"%0B", "\f"=>"%0C", "\r"=>"%0D", "\u000E"=>"%0E", "\u000F"=>"%0F", "\u0010"=>"%10", "\u0011"=>"%11", "\u0012"=>"%12", "\u0013"=>"%13", "\u0014"=>"%14", "\u0015"=>"%15", "\u0016"=>"%16", "\u0017"=>"%17", "\u0018"=>"%18", "\u0019"=>"%19", "\u001A"=>"%1A", "\e"=>"%1B", "\u001C"=>"%1C", "\u001D"=>"%1D", "\u001E"=>"%1E", "\u001F"=>"%1F", " "=>"+", "!"=>"%21", "\""=>"%22", "#"=>"%23", "$"=>"%24", "%"=>"%25", "&"=>"%26", "'"=>"%27", "("=>"%28", ")"=>"%29", "*"=>"%2A", "+"=>"%2B", ","=>"%2C", "-"=>"%2D", "."=>"%2E", "/"=>"%2F", "0"=>"%30", "1"=>"%31", "2"=>"%32", "3"=>"%33", "4"=>"%34", "5"=>"%35", "6"=>"%36", "7"=>"%37", "8"=>"%38", "9"=>"%39", ":"=>"%3A", ";"=>"%3B", "<"=>"%3C", "="=>"%3D", ">"=>"%3E", "?"=>"%3F", "@"=>"%40", "A"=>"%41", "B"=>"%42", "C"=>"%43", "D"=>"%44", "E"=>"%45", "F"=>"%46", "G"=>"%47", "H"=>"%48", "I"=>"%49", "J"=>"%4A", "K"=>"%4B", "L"=>"%4C", "M"=>"%4D", "N"=>"%4E", "O"=>"%4F", "P"=>"%50", "Q"=>"%51", "R"=>"%52", "S"=>"%53", "T"=>"%54", "U"=>"%55", "V"=>"%56", "W"=>"%57", "X"=>"%58", "Y"=>"%59", "Z"=>"%5A", "["=>"%5B", "\\"=>"%5C", "]"=>"%5D", "^"=>"%5E", "_"=>"%5F", "`"=>"%60", "a"=>"%61", "b"=>"%62", "c"=>"%63", "d"=>"%64", "e"=>"%65", "f"=>"%66", "g"=>"%67", "h"=>"%68", "i"=>"%69", "j"=>"%6A", "k"=>"%6B", "l"=>"%6C", "m"=>"%6D", "n"=>"%6E", "o"=>"%6F", "p"=>"%70", "q"=>"%71", "r"=>"%72", "s"=>"%73", "t"=>"%74", "u"=>"%75", "v"=>"%76", "w"=>"%77", "x"=>"%78", "y"=>"%79", "z"=>"%7A", "{"=>"%7B", "|"=>"%7C", "}"=>"%7D", "~"=>"%7E", "\u007F"=>"%7F", "\u0080"=>"%80", "\u0081"=>"%81", "\u0082"=>"%82", "\u0083"=>"%83", "\u0084"=>"%84", "\u0085"=>"%85", "\u0086"=>"%86", "\u0087"=>"%87", "\u0088"=>"%88", "\u0089"=>"%89", "\u008A"=>"%8A", "\u008B"=>"%8B", "\u008C"=>"%8C", "\u008D"=>"%8D", "\u008E"=>"%8E", "\u008F"=>"%8F", "\u0090"=>"%90", "\u0091"=>"%91", "\u0092"=>"%92", "\u0093"=>"%93", "\u0094"=>"%94", "\u0095"=>"%95", "\u0096"=>"%96", "\u0097"=>"%97", "\u0098"=>"%98", "\u0099"=>"%99", "\u009A"=>"%9A", "\u009B"=>"%9B", "\u009C"=>"%9C", "\u009D"=>"%9D", "\u009E"=>"%9E", "\u009F"=>"%9F", "\u00A0"=>"%A0", "\u00A1"=>"%A1", "\u00A2"=>"%A2", "\u00A3"=>"%A3", "\u00A4"=>"%A4", "\u00A5"=>"%A5", "\u00A6"=>"%A6", "\u00A7"=>"%A7", "\u00A8"=>"%A8", "\u00A9"=>"%A9", "\u00AA"=>"%AA", "\u00AB"=>"%AB", "\u00AC"=>"%AC", "\u00AD"=>"%AD", "\u00AE"=>"%AE", "\u00AF"=>"%AF", "\u00B0"=>"%B0", "\u00B1"=>"%B1", "\u00B2"=>"%B2", "\u00B3"=>"%B3", "\u00B4"=>"%B4", "\u00B5"=>"%B5", "\u00B6"=>"%B6", "\u00B7"=>"%B7", "\u00B8"=>"%B8", "\u00B9"=>"%B9", "\u00BA"=>"%BA", "\u00BB"=>"%BB", "\u00BC"=>"%BC", "\u00BD"=>"%BD", "\u00BE"=>"%BE", "\u00BF"=>"%BF", "\u00C0"=>"%C0", "\u00C1"=>"%C1", "\u00C2"=>"%C2", "\u00C3"=>"%C3", "\u00C4"=>"%C4", "\u00C5"=>"%C5", "\u00C6"=>"%C6", "\u00C7"=>"%C7", "\u00C8"=>"%C8", "\u00C9"=>"%C9", "\u00CA"=>"%CA", "\u00CB"=>"%CB", "\u00CC"=>"%CC", "\u00CD"=>"%CD", "\u00CE"=>"%CE", "\u00CF"=>"%CF", "\u00D0"=>"%D0", "\u00D1"=>"%D1", "\u00D2"=>"%D2", "\u00D3"=>"%D3", "\u00D4"=>"%D4", "\u00D5"=>"%D5", "\u00D6"=>"%D6", "\u00D7"=>"%D7", "\u00D8"=>"%D8", "\u00D9"=>"%D9", "\u00DA"=>"%DA", "\u00DB"=>"%DB", "\u00DC"=>"%DC", "\u00DD"=>"%DD", "\u00DE"=>"%DE", "\u00DF"=>"%DF", "\u00E0"=>"%E0", "\u00E1"=>"%E1", "\u00E2"=>"%E2", "\u00E3"=>"%E3", "\u00E4"=>"%E4", "\u00E5"=>"%E5", "\u00E6"=>"%E6", "\u00E7"=>"%E7", "\u00E8"=>"%E8", "\u00E9"=>"%E9", "\u00EA"=>"%EA", "\u00EB"=>"%EB", "\u00EC"=>"%EC", "\u00ED"=>"%ED", "\u00EE"=>"%EE", "\u00EF"=>"%EF", "\u00F0"=>"%F0", "\u00F1"=>"%F1", "\u00F2"=>"%F2", "\u00F3"=>"%F3", "\u00F4"=>"%F4", "\u00F5"=>"%F5", "\u00F6"=>"%F6", "\u00F7"=>"%F7", "\u00F8"=>"%F8", "\u00F9"=>"%F9", "\u00FA"=>"%FA", "\u00FB"=>"%FB", "\u00FC"=>"%FC", "\u00FD"=>"%FD", "\u00FE"=>"%FE", "\u00FF"=>"%FF"}
@@ -54,7 +54,7 @@ module URI
54
54
  def self.decode_www_form_component(str, enc=Encoding::UTF_8)
55
55
  raise ArgumentError, "invalid %-encoding (#{str})" if /%(?![0-9a-fA-F][0-9a-fA-F])/ =~ str
56
56
  str.b.gsub(/\+|%[0-9a-fA-F][0-9a-fA-F]/, TBLDECWWWCOMP_).force_encoding(enc)
57
- end
57
+ end
58
58
  end
59
59
 
60
60
  module Kernel