-
Notifications
You must be signed in to change notification settings - Fork 63
/
element.rb
314 lines (257 loc) · 6.96 KB
/
element.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
%x{
var root = $opal.global, dom_class;
if (root.jQuery) {
dom_class = jQuery
}
else if (root.Zepto) {
dom_class = Zepto.zepto.Z;
}
else {
throw new Error("jQuery must be included before opal-jquery");
}
}
class Element < `dom_class`
include Enumerable
def self.find(selector)
`$(#{selector})`
end
def self.[](selector)
`$(#{selector})`
end
def self.id(id)
%x{
var el = document.getElementById(id);
if (!el) {
return nil;
}
return $(el);
}
end
def self.new(tag = 'div')
`$(document.createElement(tag))`
end
def self.parse(str)
`$(str)`
end
def self.expose(*methods)
%x{
for (var i = 0, length = methods.length, method; i < length; i++) {
method = methods[i];
self._proto['$' + method] = self._proto[method];
}
return nil;
}
end
attr_reader :selector
# Bridged functions - we just expose all core jquery functions as ruby
# methods on this class.
expose :after, :before, :parent, :parents, :prepend, :prev, :remove
expose :hide, :show, :toggle, :children, :blur, :closest, :data
expose :focus, :find, :next, :siblings, :text, :trigger, :append
expose :height, :width, :serialize, :is, :filter, :last, :first
expose :wrap, :stop, :clone, :empty
# We alias some jquery methods to common ruby method names.
alias succ next
alias << append
# Here we map the remaining jquery methods, but change their names to
# snake_case to be more consistent with ruby.
alias_native :[]=, :attr
alias_native :add_class, :addClass
alias_native :append_to, :appendTo
alias_native :has_class?, :hasClass
alias_native :html=, :html
alias_native :remove_attr, :removeAttr
alias_native :remove_class, :removeClass
alias_native :text=, :text
alias_native :toggle_class, :toggleClass
alias_native :value=, :val
alias_native :scroll_left=, :scrollLeft
alias_native :scroll_left, :scrollLeft
alias_native :remove_attribute, :removeAttr
alias_native :slide_down, :slideDown
alias_native :slide_up, :slideUp
alias_native :slide_toggle, :slideToggle
alias_native :fade_toggle, :fadeToggle
def to_n
self
end
def [](name)
`self.attr(name) || ""`
end
def add_attribute name
self[name] = ''
end
def has_attribute? name
`!!self.attr(name)`
end
def append_to_body
`self.appendTo(document.body)`
end
def append_to_head
`self.appendTo(document.head)`
end
# Returns the element at the given index as a new `DOM` instance.
# Negative indexes can be used and are counted from the end. If the
# given index is outside the range then `nil` is returned.
def at(index)
%x{
var length = self.length;
if (index < 0) {
index += length;
}
if (index < 0 || index >= length) {
return nil;
}
return $(self[index]);
}
end
# Returns the CSS class name of the firt element in self collection.
# If the collection is empty then an empty string is returned. Only
# the class name of the first element will ever be returned.
def class_name
%x{
var first = self[0];
return (first && first.className) || "";
}
end
# Sets the CSS class name of every element in self collection to the
# given string. self does not append the class names, it replaces
# the entire current class name.
def class_name=(name)
%x{
for (var i = 0, length = self.length; i < length; i++) {
self[i].className = name;
}
}
self
end
# Get or set css properties on each element in self collection. If
# only the `name` is given, then that css property name is read from
# the first element in the collection and returned. If the `value`
# property is also given then the given css property is set to the
# given value for each of the elements in self collection. The
# property can also be a hash of properties and values.
def css(name, value=nil)
if value.nil? && name.is_a?(String)
return `self.css(name)`
else
name.is_a?(Hash) ? `self.css(#{name.to_n})` : `self.css(name, value)`
end
self
end
# Set css values over time to create animations. The first parameter is a
# set of css properties and values to animate to. The first parameter
# also accepts a special :speed value to set animation speed. If a block
# is given, the block is run as a callback when the animation finishes.
def animate(params, &block)
speed = params.has_key?(:speed) ? params.delete(:speed) : 400
%x{
self.animate(#{params.to_n}, #{speed}, function() {
#{block.call if block_given?}
})
}
end
# Start a visual effect (e.g. fadeIn, fadeOut, …) passing its name.
# Underscored style is automatically converted (e.g. `effect(:fade_in)`).
# Also accepts additional arguments and a block for the finished callback.
def effect(name, *args, &block)
name = name.gsub(/_\w/) { |match| match[1].upcase }
args = args.map { |a| a.to_n if a.respond_to? :to_n }.compact
args << `function() { #{block.call if block_given?} }`
`self[#{name}].apply(self, #{args})`
end
def visible?
`self.is(':visible')`
end
def offset
Hash.from_native(`self.offset()`)
end
def each
`for (var i = 0, length = self.length; i < length; i++) {`
yield `$(self[i])`
`}`
self
end
def first
`self.length ? self.first() : nil`
end
def html
`self.html() || ""`
end
def id
%x{
var first = self[0];
return (first && first.id) || "";
}
end
def id=(id)
%x{
var first = self[0];
if (first) {
first.id = id;
}
return self;
}
end
def tag_name
`self.length > 0 ? self[0].tagName.toLowerCase() : #{nil}`
end
def inspect
%x{
var val, el, str, result = [];
for (var i = 0, length = self.length; i < length; i++) {
el = self[i];
str = "<" + el.tagName.toLowerCase();
if (val = el.id) str += (' id="' + val + '"');
if (val = el.className) str += (' class="' + val + '"');
result.push(str + '>');
}
return '#<Element [' + result.join(', ') + ']>';
}
end
def length
`self.length`
end
def any?
`self.length > 0`
end
def empty?
`self.length === 0`
end
alias empty? none?
def on(name, sel = nil, &block)
%x{
var wrapper = function(evt) {
if (evt.preventDefault) {
evt = #{Event.new `evt`};
}
return block.apply(null, arguments);
};
block._jq_wrap = wrapper;
if (sel == nil) {
self.on(name, wrapper);
}
else {
self.on(name, sel, wrapper);
}
}
block
end
def off(name, sel, block = nil)
%x{
if (sel == null) {
return self.off(name);
}
else if (block === nil) {
return self.off(name, sel._jq_wrap);
}
else {
return self.off(name, sel, block._jq_wrap);
}
}
end
alias size length
def value
`self.val() || ""`
end
end