superchris-rubyjs 0.8.2

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