superchris-rubyjs 0.8.2

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 (86) hide show
  1. data/README +131 -0
  2. data/Rakefile +65 -0
  3. data/bin/rubyjs +144 -0
  4. data/rubyjs.gemspec +112 -0
  5. data/src/rubyjs.rb +3 -0
  6. data/src/rubyjs/code_generator.rb +474 -0
  7. data/src/rubyjs/compiler.rb +2061 -0
  8. data/src/rubyjs/debug_name_generator.rb +95 -0
  9. data/src/rubyjs/encoder.rb +171 -0
  10. data/src/rubyjs/eval_into.rb +59 -0
  11. data/src/rubyjs/lib/core.rb +1016 -0
  12. data/src/rubyjs/lib/dom_element.rb +66 -0
  13. data/src/rubyjs/lib/json.rb +101 -0
  14. data/src/rubyjs/lib/microunit.rb +188 -0
  15. data/src/rubyjs/model.rb +293 -0
  16. data/src/rubyjs/name_generator.rb +71 -0
  17. data/src/rwt/AbsolutePanel.rb +161 -0
  18. data/src/rwt/DOM.Konqueror.rb +89 -0
  19. data/src/rwt/DOM.Opera.rb +65 -0
  20. data/src/rwt/DOM.rb +1044 -0
  21. data/src/rwt/Event.Opera.rb +35 -0
  22. data/src/rwt/Event.rb +429 -0
  23. data/src/rwt/HTTPRequest.IE6.rb +5 -0
  24. data/src/rwt/HTTPRequest.rb +74 -0
  25. data/src/rwt/Label.rb +164 -0
  26. data/src/rwt/Panel.rb +90 -0
  27. data/src/rwt/RootPanel.rb +16 -0
  28. data/src/rwt/UIObject.rb +495 -0
  29. data/src/rwt/Widget.rb +193 -0
  30. data/src/rwt/ported-from/AbsolutePanel.java +158 -0
  31. data/src/rwt/ported-from/DOM.java +571 -0
  32. data/src/rwt/ported-from/DOMImpl.java +426 -0
  33. data/src/rwt/ported-from/DOMImplOpera.java +82 -0
  34. data/src/rwt/ported-from/DOMImplStandard.java +234 -0
  35. data/src/rwt/ported-from/HTTPRequest.java +81 -0
  36. data/src/rwt/ported-from/HTTPRequestImpl.java +103 -0
  37. data/src/rwt/ported-from/Label.java +163 -0
  38. data/src/rwt/ported-from/Panel.java +99 -0
  39. data/src/rwt/ported-from/UIObject.java +614 -0
  40. data/src/rwt/ported-from/Widget.java +221 -0
  41. data/test/benchmark/bm_vm1_block.rb +15 -0
  42. data/test/benchmark/bm_vm1_const.rb +13 -0
  43. data/test/benchmark/bm_vm1_ensure.rb +15 -0
  44. data/test/benchmark/common.rb +5 -0
  45. data/test/benchmark/params.yaml +7 -0
  46. data/test/common.Browser.rb +13 -0
  47. data/test/common.rb +8 -0
  48. data/test/gen_browser_test_suite.rb +129 -0
  49. data/test/gen_test_suite.rb +41 -0
  50. data/test/run_benchs.rb +58 -0
  51. data/test/run_tests.rb +22 -0
  52. data/test/test_args.rb +24 -0
  53. data/test/test_array.rb +22 -0
  54. data/test/test_case.rb +35 -0
  55. data/test/test_class.rb +55 -0
  56. data/test/test_eql.rb +9 -0
  57. data/test/test_exception.rb +61 -0
  58. data/test/test_expr.rb +12 -0
  59. data/test/test_hash.rb +29 -0
  60. data/test/test_hot_ruby.rb +146 -0
  61. data/test/test_if.rb +28 -0
  62. data/test/test_insertion_sort.rb +25 -0
  63. data/test/test_inspect.rb +10 -0
  64. data/test/test_lebewesen.rb +39 -0
  65. data/test/test_massign.rb +66 -0
  66. data/test/test_new.rb +12 -0
  67. data/test/test_range.rb +70 -0
  68. data/test/test_regexp.rb +22 -0
  69. data/test/test_send.rb +65 -0
  70. data/test/test_simple_output.rb +5 -0
  71. data/test/test_splat.rb +21 -0
  72. data/test/test_string.rb +51 -0
  73. data/test/test_test.rb +17 -0
  74. data/test/test_yield.rb +154 -0
  75. data/utils/js/Makefile +9 -0
  76. data/utils/js/RunScript.class +0 -0
  77. data/utils/js/RunScript.java +73 -0
  78. data/utils/js/js.jar +0 -0
  79. data/utils/js/run.sh +3 -0
  80. data/utils/jsc/Makefile +7 -0
  81. data/utils/jsc/README +3 -0
  82. data/utils/jsc/RunScript.c +93 -0
  83. data/utils/jsc/run.sh +15 -0
  84. data/utils/yuicompressor/README +1 -0
  85. data/utils/yuicompressor/yuicompressor-2.2.5.jar +0 -0
  86. metadata +157 -0
@@ -0,0 +1,5 @@
1
+ class HTTPRequest
2
+ def self.createXmlHTTPRequest()
3
+ `return new ActiveXObject("Msxml2.XMLHTTP")`
4
+ end
5
+ end
@@ -0,0 +1,74 @@
1
+ #--
2
+ # Copyright (c) 2007 by Michael Neumann (mneumann@ntecs.de)
3
+ #
4
+ # Copyright 2006 Google Inc.
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License"); you may not
7
+ # use this file except in compliance with the License. You may obtain a copy of
8
+ # the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
14
+ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
15
+ # License for the specific language governing permissions and limitations under
16
+ # the License.
17
+ #++
18
+
19
+ class HTTPRequest
20
+
21
+ #
22
+ # Makes an asynchronous HTTP GET to a remote server.
23
+ #
24
+ # url:: the absolute url to GET
25
+ # returns:: +false+ if the invocation fails to issue
26
+ # yields:: the response
27
+ #
28
+ def self.asyncGet(url, user=nil, pwd=nil, &block)
29
+ asyncImpl(url, "GET", "", user, pwd, &block)
30
+ end
31
+
32
+ #
33
+ # Makes an asynchronous HTTP POST to a remote server.
34
+ #
35
+ # url:: the absolute url to which the POST data is delivered
36
+ # postData:: the data to post
37
+ # returns:: +false+ if the invocation fails to issue
38
+ # yields:: the response
39
+ #
40
+ def self.asyncPost(url, postData, user=nil, pwd=nil, &block)
41
+ asyncImpl(url, "POST", postData, user, pwd, &block)
42
+ end
43
+
44
+ private
45
+
46
+ def self.createXmlHTTPRequest()
47
+ `return new XMLHttpRequest()`
48
+ end
49
+
50
+ def self.asyncImpl(url, method, data="", user=nil, pwd=nil)
51
+ xmlHttp = createXmlHTTPRequest()
52
+ `
53
+ try {
54
+ #<xmlHttp>.open(#<method>, #<url>, true);
55
+ #<xmlHttp>.setRequestHeader("Content-Type", "text/plain; charset=utf-8");
56
+ #<xmlHttp>.onreadystatechange = function() {
57
+ if (#<xmlHttp>.readyState == 4) {
58
+ #<xmlHttp>.onreadystatechange = #<globalattr:null_func>; `
59
+
60
+ yield `(#<xmlHttp>.responseText || "")`
61
+
62
+ `}
63
+ };
64
+ #<xmlHttp>.send(#<data>);
65
+ return true;
66
+ } catch (e) {
67
+ #<xmlHttp>.onreadystatechange = #<globalattr:null_func>;
68
+ return false;
69
+ } `
70
+ end
71
+
72
+ end
73
+
74
+
@@ -0,0 +1,164 @@
1
+ #--
2
+ # Copyright (c) 2007 by Michael Neumann (mneumann@ntecs.de)
3
+ #
4
+ # Copyright 2007 Google Inc.
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License"); you may not
7
+ # use this file except in compliance with the License. You may obtain a copy of
8
+ # the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
14
+ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
15
+ # License for the specific language governing permissions and limitations under
16
+ # the License.
17
+ #++
18
+
19
+
20
+ #
21
+ # Map Event types to methods.
22
+ #
23
+ # We use a hack in that we assign the keys directly to
24
+ # the object. As long as the keys (== event types) are
25
+ # integers (and they are) there is no problem with that
26
+ # approach. This saves us an extra object!
27
+ #
28
+ module EventDispatchMap
29
+
30
+ def onBrowserEvent(event)
31
+ eventType = Event.getType(event)
32
+ `
33
+ var cb = #<self>[#<eventType>];
34
+ if (cb) cb.apply(#<self>, [#<nil>, #<event>]);
35
+ return #<nil>
36
+ `
37
+ end
38
+
39
+ protected
40
+
41
+ def mapEvent(eventType, method)
42
+ `#<self>[#<eventType>] = #<method>`
43
+ end
44
+
45
+ end
46
+
47
+
48
+ #
49
+ # A widget that contains arbitrary text, <i>not</i> interpreted as HTML.
50
+ #
51
+ class Label < Widget
52
+
53
+ #include EventDispatchMap
54
+
55
+ #
56
+ # Creates a label, either empty or with the specified text.
57
+ #
58
+ # text:: the new label's text. if +nil+, label is empty.
59
+ # wordWrap:: +false+ to disable wordwrapping, +true+ to enable,
60
+ # +nil+ to don't care.
61
+ #
62
+ def initialize(text=nil, wordWrap=nil)
63
+ setElement(DOM.createDiv)
64
+ sinkEvents(Event::ONCLICK | Event::MOUSEEVENTS | Event::ONMOUSEWHEEL)
65
+ setStyleName("rwt-Label")
66
+ setText(text) if text
67
+ setWordWrap(wordWrap) unless wordWrap.nil?
68
+
69
+ #
70
+ # setup event map
71
+ #
72
+ =begin
73
+ mapEvent(Event::ONCLICK, `this.#<m:onClick>`)
74
+ mapEvent(Event::ONMOUSEDOWN, `this.#<m:onMouse>`)
75
+ mapEvent(Event::ONMOUSEUP, `this.#<m:onMouse>`)
76
+ mapEvent(Event::ONMOUSEMOVE, `this.#<m:onMouse>`)
77
+ mapEvent(Event::ONMOUSEOVER, `this.#<m:onMouse>`)
78
+ mapEvent(Event::ONMOUSEOUT, `this.#<m:onMouse>`)
79
+ mapEvent(Event::ONMOUSEWHEEL, `this.#<m:onMouseWheel>`)
80
+ =end
81
+ end
82
+
83
+ def addClickListener(listener)
84
+ @clickListeners ||= []
85
+ @clickListeners << listener
86
+ end
87
+
88
+ def addMouseListener(listener)
89
+ @mouseListeners ||= []
90
+ @mouseListeners << listener
91
+ end
92
+
93
+ def addMouseWheelListener(listener)
94
+ @mouseWheelListeners ||= []
95
+ @mouseWheelListeners << listener
96
+ end
97
+
98
+ def removeClickListener(listener)
99
+ @clickListeners.delete(listener) if @clickListeners
100
+ end
101
+
102
+ def removeMouseListener(listener)
103
+ @mouseListeners.delete(listener) if @mouseListeners
104
+ end
105
+
106
+ def removeMouseWheelListener(listener)
107
+ @mouseListeners.delete(listener) if @mouseWheelListeners
108
+ end
109
+
110
+ =begin
111
+ def getHorizontalAlignment
112
+ @horzAlign
113
+ end
114
+
115
+ def setHorizontalAlignment(align)
116
+ @horzAlign = align
117
+ Element.setStyleAttribute(getElement(), "textAlign", align.getTextAlignString())
118
+ end
119
+ =end
120
+
121
+ def getText
122
+ DOM.getInnerText(getElement())
123
+ end
124
+
125
+ def getWordWrap
126
+ DOM.getStyleAttribute(getElement(), "whiteSpace") != 'nowrap'
127
+ end
128
+
129
+ def setText(text)
130
+ DOM.setInnerText(getElement(), text)
131
+ end
132
+
133
+ def setWordWrap(wrap)
134
+ DOM.setStyleAttribute(getElement(), "whiteSpace", wrap ? "normal" : "nowrap")
135
+ end
136
+
137
+ def onBrowserEvent(event)
138
+ eventType = Event.getType(event)
139
+
140
+ if eventType == Event::ONCLICK
141
+ if @clickListeners
142
+ @clickListeners.each {|l| l.onClick(self) }
143
+ end
144
+ end
145
+ end
146
+
147
+ =begin
148
+ def onClick(event)
149
+ if @clickListeners
150
+ @clickListeners.each {|l| l.onClick(self) }
151
+ end
152
+ end
153
+
154
+ def onMouse(event)
155
+ if @mouseListeners
156
+ #@mouseListeners.each {|l| l.onClick(self) }
157
+ end
158
+ end
159
+
160
+ def onMouseWheel(event)
161
+ end
162
+ =end
163
+
164
+ end
@@ -0,0 +1,90 @@
1
+ #--
2
+ # Copyright (c) 2007 by Michael Neumann (mneumann@ntecs.de)
3
+ #
4
+ # Copyright 2006 Google Inc.
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License"); you may not
7
+ # use this file except in compliance with the License. You may obtain a copy of
8
+ # the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
14
+ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
15
+ # License for the specific language governing permissions and limitations under
16
+ # the License.
17
+ #++
18
+
19
+ #
20
+ # Abstract base class for all panels, which are widgets that can contain other
21
+ # widgets.
22
+ #
23
+ class Panel < Widget
24
+
25
+ def add(w)
26
+ raise "This panel does not support no-arg add()"
27
+ end
28
+
29
+ def clear
30
+ raise
31
+ end
32
+
33
+ def each
34
+ raise
35
+ end
36
+
37
+ #
38
+ # This method must be called as part of the add method of any panel. It
39
+ # ensures that the Widget's parent is set properly, and that it is removed
40
+ # from any existing parent widget. It also attaches the child widget's
41
+ # DOM element to its new container, ensuring that this process occurs in the
42
+ # right order.
43
+ #
44
+ # w:: the widget to be adopted
45
+ # container:: the element within which it will be contained
46
+ #
47
+ def adopt(w, container)
48
+ # Remove the widget from its current parent, if any.
49
+ w.removeFromParent
50
+
51
+ # Attach it at the DOM and GWT levels.
52
+ if container
53
+ DOM.appendChild(container, w.getElement)
54
+ end
55
+
56
+ w.setParent(self)
57
+ end
58
+
59
+ #
60
+ # This method must be called whenever a Widget is removed. It ensures that
61
+ # the Widget's parent is cleared.
62
+ #
63
+ # w:: the widget to be disowned
64
+ #
65
+ def disown(w)
66
+ # Only disown it if it's actually contained in this panel.
67
+ raise "w is not a child of this panel" if w.getParent != self
68
+
69
+ # Remove it at the DOM and GWT levels.
70
+ elem = w.getElement
71
+ w.setParent(nil)
72
+ DOM.removeChild(DOM.getParent(elem), elem)
73
+ end
74
+
75
+ def onAttach
76
+ super()
77
+
78
+ # Ensure that all child widgets are attached.
79
+ #FIXME
80
+ each {|child| child.onAttach}
81
+ end
82
+
83
+ def onDetach
84
+ super()
85
+
86
+ # Ensure that all child widgets are detached.
87
+ each {|child| child.onDetach}
88
+ end
89
+
90
+ end
@@ -0,0 +1,16 @@
1
+ class RootPanel < AbsolutePanel
2
+ def self.get
3
+ @rootPanel ||= new()
4
+ end
5
+
6
+ def each
7
+ # FIXME
8
+ end
9
+
10
+ def initialize
11
+ setElement(getBodyElement())
12
+ onAttach()
13
+ end
14
+
15
+ def getBodyElement() `return document.body` end
16
+ end
@@ -0,0 +1,495 @@
1
+ #--
2
+ # Copyright (c) 2007 by Michael Neumann (mneumann@ntecs.de)
3
+ #
4
+ # Copyright 2007 Google Inc.
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License"); you may not
7
+ # use this file except in compliance with the License. You may obtain a copy of
8
+ # the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
14
+ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
15
+ # License for the specific language governing permissions and limitations under
16
+ # the License.
17
+ #++
18
+
19
+ #
20
+ # The superclass for all user-interface objects. It simply wraps a DOM element,
21
+ # and cannot receive events. Most interesting user-interface classes derive
22
+ # from +Widget+.
23
+ #
24
+ # == Styling With CSS
25
+ #
26
+ # All +UIObject+ objects can be styled using CSS. Style names that
27
+ # are specified programmatically in Ruby source are implicitly associated with
28
+ # CSS style rules. In terms of HTML and CSS, a RWT style name is the element's
29
+ # CSS "class". By convention, RWT style names are of the form
30
+ # <tt>[project]-[widget]</tt>.
31
+ #
32
+ # For example, the +Button+ widget has the style name
33
+ # <tt>rwt-Button</tt>, meaning that within the +Button+
34
+ # constructor, the following call occurs:
35
+ #
36
+ # setStyleName("rwt-Button")
37
+ #
38
+ # A corresponding CSS style rule can then be written as follows:
39
+ #
40
+ # // Example of how you might choose to style a Button widget
41
+ # .rwt-Button {
42
+ # background-color: yellow;
43
+ # color: black;
44
+ # font-size: 24pt;
45
+ # }
46
+ #
47
+ # Note the dot prefix in the CSS style rule. This syntax is called a CSS
48
+ # class selector.
49
+ #
50
+ # == Style Name Specifics
51
+ #
52
+ # Every +UIObject+ has a <i>primary style name</i> that
53
+ # identifies the key CSS style rule that should always be applied to it. Use
54
+ # #setStyleName to specify an object's primary style name. In
55
+ # most cases, the primary style name is set in a widget's constructor and never
56
+ # changes again during execution. In the case that no primary style name is
57
+ # specified, it defaults to <tt>rwt-nostyle</tt>.
58
+ #
59
+ # More complex styling behavior can be achieved by manipulating an object's
60
+ # <i>secondary style names</i>. Secondary style names can be added and removed
61
+ # using #addStyleName and #removeStyleName.
62
+ # The purpose of secondary style names is to associate a variety of CSS style
63
+ # rules over time as an object progresses through different visual states.
64
+ #
65
+ # There is an important special formulation of secondary style names called
66
+ # <i>dependent style names</i>. A dependent style name is a secondary style
67
+ # name prefixed with the primary style name of the widget itself. See
68
+ # #addStyleName for details.
69
+ #
70
+ class UIObject
71
+
72
+ STYLE_EMPTY = "rwt-nostyle"
73
+
74
+ #-------------------------------------------------------------------
75
+ # Style-name related
76
+ #-------------------------------------------------------------------
77
+
78
+ #
79
+ # Gets the primary style name associated with the object.
80
+ #
81
+ # return:: the object's primary style name
82
+ #
83
+ # See #setStyleName, #addStyleName, #removeStyleName.
84
+ #
85
+ def getStyleName
86
+ styleNameHelper(MODE_GET)
87
+ end
88
+
89
+ #
90
+ # Sets the object's primary style name and updates all dependent style names.
91
+ #
92
+ # style:: the new primary style name
93
+ #
94
+ # See #addStyleName, #removeStyleName
95
+ #
96
+ def setStyleName(style)
97
+ styleNameHelper(MODE_SET, style)
98
+ end
99
+
100
+ #
101
+ # Adds a secondary or dependent style name to this object. A secondary style
102
+ # name is an additional style name that is, in HTML/CSS terms, included as a
103
+ # space-separated token in the value of the CSS +class+ attribute for this
104
+ # object's root element.
105
+ #
106
+ # The most important use for this method is to add a special kind of
107
+ # secondary style name called a <i>dependent style name</i>. To add a
108
+ # dependent style name, prefix the 'style' argument with the result of
109
+ # +getStyleName+. For example, suppose the primary style name is
110
+ # <tt>rwt-TextBox</tt>. If the following method is called as
111
+ # <tt>obj.setReadOnly(true)</tt>:
112
+ #
113
+ # def setReadOnly(readOnly=true)
114
+ # # Create a dependent style name.
115
+ # readOnlyStyle = getStyleName() + "-readonly"
116
+ #
117
+ # @isReadOnlyMode = readOnly
118
+ # if readOnly
119
+ # addStyleName(readOnlyStyle)
120
+ # else
121
+ # removeStyleName(readOnlyStyle)
122
+ # end
123
+ # end
124
+ #
125
+ # then both of the CSS style rules below will be applied:
126
+ #
127
+ # // This rule is based on the primary style name and is always active.
128
+ # .rwt-TextBox {
129
+ # font-size: 12pt;
130
+ # }
131
+ #
132
+ # // This rule is based on a dependent style name that is only active
133
+ # // when the widget has called addStyleName(getStyleName() + "-readonly").
134
+ # .gwt-TextBox-readonly {
135
+ # background-color: lightgrey;
136
+ # border: none;
137
+ # }
138
+ #
139
+ # Dependent style names are powerful because they are automatically updated
140
+ # whenever the primary style name changes. Continuing with the example above,
141
+ # if the primary style name changed due to the following call:
142
+ #
143
+ # setStyleName("my-TextThingy")
144
+ #
145
+ # then the object would be re-associated with style rules below rather than
146
+ # those above:
147
+ #
148
+ # .my-TextThingy {
149
+ # font-size: 12pt;
150
+ # }
151
+ #
152
+ # .my-TextThingy-readonly {
153
+ # background-color: lightgrey;
154
+ # border: none;
155
+ # }
156
+ #
157
+ # Secondary style names that are not dependent style names are not
158
+ # automatically updated when the primary style name changes.
159
+ #
160
+ # style:: the secondary style name to be added
161
+ #
162
+ # See UIObject, #removeStyleName
163
+ #
164
+ def addStyleName(style)
165
+ styleNameHelper(MODE_ADD, style)
166
+ end
167
+
168
+ #
169
+ # Removes a secondary style name.
170
+ #
171
+ # style:: the secondary style name to be removed
172
+ #
173
+ # See #addStyleName.
174
+ #
175
+ def removeStyleName(style)
176
+ styleNameHelper(MODE_REM, style)
177
+ end
178
+
179
+ #
180
+ # Template method that returns the element to which style names will be
181
+ # applied. By default it returns the root element, but this method may be
182
+ # overridden to apply styles to a child element.
183
+ #
184
+ # return:: the element to which style names will be applied
185
+ #
186
+ def getStyleElement
187
+ return @element
188
+ end
189
+
190
+ protected :getStyleElement
191
+
192
+ #
193
+ # Constants used in +mode+ parameter of +styleNameHelper+ method.
194
+ #
195
+ MODE_GET = 0
196
+ MODE_SET = 1
197
+ MODE_ADD = 2
198
+ MODE_REM = 3
199
+
200
+ #
201
+ # Implement set/get/add/remove of style names.
202
+ #
203
+ def styleNameHelper(mode, style=nil)
204
+ unless elem = getStyleElement()
205
+ raise "Null widget handle!"
206
+ end
207
+
208
+ className = DOM.getProperty(elem, "className").strip
209
+
210
+ # Ensure a primary style name
211
+ if className.empty?
212
+ className = STYLE_EMPTY
213
+ DOM.setProperty(elem, "className", className)
214
+ end
215
+
216
+ if mode != MODE_GET
217
+ # Style names cannot contain leading or trailing whitespace, and cannot
218
+ # legally be empty.
219
+ style = style.strip
220
+ raise "Style names cannot be empty" if style.empty?
221
+ end
222
+
223
+ arr = className.split(" ")
224
+
225
+ # The primary style name is always the first token of the full CSS class name.
226
+ primary = arr.first
227
+
228
+ return primary if mode == MODE_GET
229
+ raise "Cannot remove primary style name" if mode == MODE_REM and primary == style
230
+
231
+ newClassName = []
232
+
233
+ `var e, s;
234
+ for (var i=0; i<#<arr>.length; i++)
235
+ {
236
+ e = #<arr>[i];
237
+ if (e == '') continue;
238
+
239
+ if (#<mode> == #<MODE_SET>)
240
+ {
241
+ // set primary name -> update all dependent style names
242
+ if (e.indexOf(#<primary>) != 0)
243
+ {
244
+ // +e+ doesnt start with the old primary style name
245
+ // -> we dont touch it and keep it!
246
+ s = e;
247
+ }
248
+ else
249
+ {
250
+ // replace +primary+ with +style+
251
+ s = #<style> + e.substring(#<primary>.length);
252
+ }
253
+ }
254
+ else /* MODE_ADD or MODE_REM */
255
+ {
256
+ // remove the style. in case of MODE_ADD, we add it back later!
257
+ s = (e == #<style>) ? null : e;
258
+ }
259
+
260
+ if (s) #<newClassName>.push(s);
261
+ }
262
+
263
+ if (#<mode> == #<MODE_ADD>)
264
+ {
265
+ #<newClassName>.push(#<style>);
266
+ }
267
+
268
+ #<newClassName> = #<newClassName>.join(" ");`
269
+
270
+ DOM.setProperty(elem, "className", newClassName)
271
+ end
272
+
273
+ private :styleNameHelper
274
+
275
+ #-------------------------------------------------------------------
276
+ # Position/Size related
277
+ #-------------------------------------------------------------------
278
+
279
+ #
280
+ # Gets the object's absolute left position in pixels, as measured from the
281
+ # browser window's client area.
282
+ #
283
+ # return:: the object's absolute left position
284
+ #
285
+ def getAbsoluteLeft
286
+ DOM.getAbsoluteLeft(@element)
287
+ end
288
+
289
+ #
290
+ # Gets the object's absolute top position in pixels, as measured from the
291
+ # browser window's client area.
292
+ #
293
+ # return:: the object's absolute top position
294
+ #
295
+ def getAbsoluteTop
296
+ DOM.getAbsoluteTop(@element)
297
+ end
298
+
299
+ #
300
+ # Gets the object's offset height in pixels. This is the total height of the
301
+ # object, including decorations such as border, margin, and padding.
302
+ #
303
+ # return:: the object's offset height
304
+ #
305
+ def getOffsetHeight
306
+ DOM.getPropertyInt(@element, "offsetHeight")
307
+ end
308
+
309
+ #
310
+ # Gets the object's offset width in pixels. This is the total width of the
311
+ # object, including decorations such as border, margin, and padding.
312
+ #
313
+ # return:: the object's offset width
314
+ #
315
+ def getOffsetWidth
316
+ DOM.getPropertyInt(@element, "offsetWidth")
317
+ end
318
+
319
+ #
320
+ # Sets the object's height. This height does not include decorations such as
321
+ # border, margin, and padding.
322
+ #
323
+ # height:: the object's new height, in CSS units (e.g. "10px", "1em")
324
+ #
325
+ def setHeight(height)
326
+ # This exists to deal with an inconsistency in IE's implementation where
327
+ # it won't accept negative numbers in length measurements
328
+ # FIXME: assert extractLengthValue(height.trim().toLowerCase()) >= 0 : "CSS heights should not be negative";
329
+
330
+ DOM.setStyleAttribute(@element, "height", height)
331
+ end
332
+
333
+ #
334
+ # Sets the object's width. This width does not include decorations such as
335
+ # border, margin, and padding.
336
+ #
337
+ # width:: the object's new width, in CSS units (e.g. "10px", "1em")
338
+ #
339
+ def setWidth(width)
340
+ # This exists to deal with an inconsistency in IE's implementation where
341
+ # it won't accept negative numbers in length measurements
342
+ # FIXME: assert extractLengthValue(width.trim().toLowerCase()) >= 0 : "CSS widths should not be negative";
343
+
344
+ DOM.setStyleAttribute(@element, "width", width)
345
+ end
346
+
347
+ #
348
+ # Sets the object's size, in pixels, not including decorations such as
349
+ # border, margin, and padding.
350
+ #
351
+ # width:: the object's new width, in pixels
352
+ # height:: the object's new height, in pixels
353
+ #
354
+ def setPixelSize(width, height)
355
+ setWidth(width + "px") # if width >= 0
356
+ setHeight(height + "px") # if height >= 0
357
+ end
358
+
359
+ #
360
+ # Sets the object's size. This size does not include decorations such as
361
+ # border, margin, and padding.
362
+ #
363
+ # width:: the object's new width, in CSS units (e.g. "10px", "1em")
364
+ # height:: the object's new height, in CSS units (e.g. "10px", "1em")
365
+ #
366
+ def setSize(width, height)
367
+ setWidth(width)
368
+ setHeight(height)
369
+ end
370
+
371
+ #-------------------------------------------------------------------
372
+ # Event related
373
+ #-------------------------------------------------------------------
374
+
375
+ #
376
+ # Adds a set of events to be sunk by this object. Note that only
377
+ # widgets (+Widget+) may actually receive events, but can receive events
378
+ # from all objects contained within them.
379
+ #
380
+ # eventBitsToAdd:: a bitfield representing the set of events to be added
381
+ # to this element's event set
382
+ #
383
+ # See +Event+.
384
+ #
385
+ def sinkEvents(eventBitsToAdd)
386
+ DOM.sinkEvents(@element, eventBitsToAdd | DOM.getEventsSunk(@element))
387
+ end
388
+
389
+ #
390
+ # Removes a set of events from this object's event list.
391
+ #
392
+ # eventBitsToRemove:: a bitfield representing the set of events to be
393
+ # removed from this element's event set
394
+ #
395
+ # See #sinkEvents and +Event+.
396
+ #
397
+ def unsinkEvents(eventBitsToRemove)
398
+ DOM.sinkEvents(@element, DOM.getEventsSunk(@element) & (~eventBitsToRemove))
399
+ end
400
+
401
+ #-------------------------------------------------------------------
402
+ # Element access
403
+ #-------------------------------------------------------------------
404
+
405
+ #
406
+ # Gets a handle to the object's underlying DOM element.
407
+ #
408
+ # return:: the object's browser element
409
+ #
410
+ def getElement
411
+ @element
412
+ end
413
+
414
+ #
415
+ # Sets this object's browser element. UIObject subclasses must call this
416
+ # method before attempting to call any other methods.
417
+ #
418
+ # If the browser element has already been set, then the current element's
419
+ # position is located in the DOM and removed. The new element is added into
420
+ # the previous element's position.
421
+ #
422
+ # elem:: the object's new element
423
+ #
424
+ def setElement(elem)
425
+ if @element
426
+ # replace element in its parent with elem.
427
+ DOM.replace(@element, elem)
428
+ end
429
+
430
+ @element = elem
431
+
432
+ # We do not actually force the creation of a primary style name here.
433
+ # Instead, we do it lazily -- when it is aboslutely required --
434
+ # in getStyleName(), addStyleName(), and removeStyleName().
435
+ end
436
+
437
+ #-------------------------------------------------------------------
438
+ # Misc
439
+ #-------------------------------------------------------------------
440
+
441
+ #
442
+ # Gets the title associated with this object. The title is the 'tool-tip'
443
+ # displayed to users when they hover over the object.
444
+ #
445
+ # return:: the object's title
446
+ #
447
+ def getTitle
448
+ DOM.getProperty(@element, "title")
449
+ end
450
+
451
+ #
452
+ # Sets the title associated with this object. The title is the 'tool-tip'
453
+ # displayed to users when they hover over the object.
454
+ #
455
+ # To remove a title, pass +nil+ as argument.
456
+ #
457
+ # title:: the object's new title
458
+ #
459
+ def setTitle(title)
460
+ if title
461
+ DOM.setAttribute(@element, "title", title)
462
+ else
463
+ DOM.removeAttribute(@element, "title")
464
+ end
465
+ end
466
+
467
+ #
468
+ # Determines whether or not this object is visible.
469
+ #
470
+ # return:: +true+ if the object is visible
471
+ #
472
+ def isVisible
473
+ DOM.isVisible(@element)
474
+ end
475
+
476
+ #
477
+ # Sets whether this object is visible.
478
+ #
479
+ # visible:: +true+ to show the object, +false+ to hide it
480
+ #
481
+ def setVisible(visible=true)
482
+ DOM.setVisible(@element, visible)
483
+ end
484
+
485
+ #
486
+ # This method is overridden so that any object can be viewed in the debugger
487
+ # as an HTML snippet.
488
+ #
489
+ # return:: a string representation of the object
490
+ #
491
+ def to_s
492
+ @element ? DOM.to_s(@element) : "(null handle)"
493
+ end
494
+
495
+ end # class UIObject