opal-js_wrap-three 0.1.0
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.
- checksums.yaml +7 -0
- data/.github/workflows/main.yml +16 -0
- data/.gitignore +16 -0
- data/.rspec +3 -0
- data/.rubocop.yml +13 -0
- data/CHANGELOG.md +5 -0
- data/Gemfile +14 -0
- data/Gemfile.lock +74 -0
- data/LICENSE.txt +21 -0
- data/README.md +56 -0
- data/Rakefile +18 -0
- data/bin/console +15 -0
- data/bin/setup +8 -0
- data/examples/readme/Gemfile +9 -0
- data/examples/readme/example.rb +33 -0
- data/examples/readme/run.sh +3 -0
- data/lib/opal/js_wrap/three/version.rb +9 -0
- data/lib/opal/js_wrap/three.rb +14 -0
- data/lib-opal/js_wrap/three/three.js +41268 -0
- data/lib-opal/js_wrap/three.rb +4 -0
- data/lib-opal/js_wrap.rb +720 -0
- data/opal-js_wrap-three.gemspec +37 -0
- data/package-lock.json +3991 -0
- data/package.json +13 -0
- data/rollup.config.js +24 -0
- data/src/three.mjs +3 -0
- metadata +84 -0
data/lib-opal/js_wrap.rb
ADDED
@@ -0,0 +1,720 @@
|
|
1
|
+
# helpers: native, defineProperty
|
2
|
+
|
3
|
+
`var inspect_stack = []`
|
4
|
+
|
5
|
+
# JSWrap is basically a Native v2. It has the same aims and goals. It should
|
6
|
+
# be interoperable with Native, ie. both modules can coexist.
|
7
|
+
|
8
|
+
# TODO: DSL methods:
|
9
|
+
# - js_method
|
10
|
+
# - (
|
11
|
+
# how about something like: js_method :set_timeout => [:$block, :timeout]
|
12
|
+
# which would allow us also to call it using any of the following:
|
13
|
+
# `Window.set_timeout(timeout: 123) { p "Hello!" }`
|
14
|
+
# `Window.set_timeout(123) { p "Hello!" }`
|
15
|
+
# `Window.set_timeout(-> { p "Hello" }, 123)`
|
16
|
+
# )
|
17
|
+
# TODO: support for restricted values
|
18
|
+
# TODO: to_js
|
19
|
+
|
20
|
+
module JSWrap
|
21
|
+
module WrapperClassMethods
|
22
|
+
# By default, we will transfer calls and properties like
|
23
|
+
# "my_method" into "myMethod". Since, unlike with Native, we
|
24
|
+
# inherit from Object, not BasicObject, some call names may
|
25
|
+
# conflict, but we can use names like "class_" instead of
|
26
|
+
# "class". This setting allows us to disable this behavior.
|
27
|
+
attr_accessor :js_raw_naming
|
28
|
+
|
29
|
+
# The default behavior when dispatching to a function is to
|
30
|
+
# call it and return a result.
|
31
|
+
attr_accessor :js_function_wrap
|
32
|
+
|
33
|
+
# The default behavior for things based on ObjectWrapper is to
|
34
|
+
# automatically wrap even the regular Arrays, so that we may
|
35
|
+
# correctly wrap all members.
|
36
|
+
#
|
37
|
+
# The default behavior for things based on Wrapper is to never
|
38
|
+
# wrap the regular Arrays, only Array-like things. It is the
|
39
|
+
# responsibility of the programmer to provide a good interface.
|
40
|
+
#
|
41
|
+
# This property allows you to change the default behavior.
|
42
|
+
attr_accessor :js_array_wrap
|
43
|
+
|
44
|
+
def js_attr_reader(*vars)
|
45
|
+
vars.each do |var|
|
46
|
+
define_method(var) { js_property(var) }
|
47
|
+
end
|
48
|
+
vars.length > 1 ? vars : vars.first
|
49
|
+
end
|
50
|
+
|
51
|
+
def js_attr_writer(*vars)
|
52
|
+
vars.each do |var|
|
53
|
+
define_method(:"#{var}=") { |val| js_property_set(var, val) }
|
54
|
+
end
|
55
|
+
vars.length > 1 ? vars : vars.first
|
56
|
+
end
|
57
|
+
|
58
|
+
def js_attr_accessor(*vars)
|
59
|
+
Array(js_attr_reader(*vars)) + Array(js_attr_writer(*vars))
|
60
|
+
end
|
61
|
+
|
62
|
+
def js_method(*vars)
|
63
|
+
vars.each do |var|
|
64
|
+
define_method(var) do |*args,&block|
|
65
|
+
args << block if block_given?
|
66
|
+
js_property(var, args: args)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
vars.length > 1 ? vars : vars.first
|
70
|
+
end
|
71
|
+
|
72
|
+
def js_export_class(classname = name)
|
73
|
+
`Opal.global[name] = self`
|
74
|
+
end
|
75
|
+
|
76
|
+
alias js_export_module js_export_class
|
77
|
+
|
78
|
+
# Example:
|
79
|
+
#
|
80
|
+
# module Blobulizer
|
81
|
+
# include JSWrap::Wrapper
|
82
|
+
# js_export_module
|
83
|
+
#
|
84
|
+
# js_export def self.extended_blobulize(thing)
|
85
|
+
# Blobulizer::Processor.new(thing).extended_blobulize
|
86
|
+
# end
|
87
|
+
# end
|
88
|
+
#
|
89
|
+
# JS:
|
90
|
+
# Blobulizer.extendedBlobulize({hello: "world"})
|
91
|
+
def js_export(*args)
|
92
|
+
args = Array(args.first) if args.length == 1
|
93
|
+
|
94
|
+
args.each do |i|
|
95
|
+
`self[#{js_property_name_rb2js(i)}] = #{JSWrap.unwrap_proc(`method(i).to_proc`, self)}`
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def wrap(object, parent = nil)
|
100
|
+
obj = allocate
|
101
|
+
obj.initialize_wrapped(object, parent)
|
102
|
+
obj
|
103
|
+
end
|
104
|
+
|
105
|
+
def js_class(js_constructor)
|
106
|
+
js_constructor = JSWrap.unwrap(js_constructor, self)
|
107
|
+
@native = js_constructor
|
108
|
+
JSWrap.register_wrapping(self, js_constructor)
|
109
|
+
end
|
110
|
+
|
111
|
+
# Also includes RawClassWrapper, but we don't have that yet.
|
112
|
+
end
|
113
|
+
|
114
|
+
module Wrapper
|
115
|
+
def self.included(klass)
|
116
|
+
klass.extend(WrapperClassMethods)
|
117
|
+
super
|
118
|
+
end
|
119
|
+
|
120
|
+
attr_accessor :native
|
121
|
+
|
122
|
+
def initialize_wrapped(object, parent = nil)
|
123
|
+
%x{
|
124
|
+
try {
|
125
|
+
$defineProperty(object, '$$js_wrap', self)
|
126
|
+
}
|
127
|
+
catch(e) {}
|
128
|
+
}
|
129
|
+
@native_parent = parent if parent
|
130
|
+
@native = object
|
131
|
+
end
|
132
|
+
|
133
|
+
def initialize(*args, &block)
|
134
|
+
raise NotImplementedError, 'See [doc:JSWrap::Wrapper#initialize]' unless `#{self.class}.native`
|
135
|
+
|
136
|
+
args << block if block_given?
|
137
|
+
args = JSWrap.unwrap_ary(args, self)
|
138
|
+
obj = `new (self.$$class.native.bind.apply(self.$$class.native, [null].concat(args)))`
|
139
|
+
initialize_wrapped(obj)
|
140
|
+
end
|
141
|
+
|
142
|
+
# @private
|
143
|
+
def js_property_name_rb2js(name, raw: nil)
|
144
|
+
raw = self.js_function_wrap if raw.nil?
|
145
|
+
if raw || name.start_with?(/[A-Z]/)
|
146
|
+
name
|
147
|
+
else
|
148
|
+
name.gsub(/_(.?)/) { Regexp.last_match(1).upcase }
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
# @private
|
153
|
+
def js_property_name_js2rb(name, raw: nil)
|
154
|
+
raw = self.js_function_wrap if raw.nil?
|
155
|
+
if raw || name.start_with?(/[A-Z]/)
|
156
|
+
name
|
157
|
+
else
|
158
|
+
name.gsub(/([A-Z])/) { '_' + Regexp.last_match(1).downcase }
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
def js_properties(not_just_own = true, raw: nil)
|
163
|
+
%x{
|
164
|
+
var out = [];
|
165
|
+
for (var name in self.native) {
|
166
|
+
if (!not_just_own || self.native.hasOwnProperty(name)) {
|
167
|
+
#{`out` << js_property_name_js2rb(`name`, raw: raw)}
|
168
|
+
}
|
169
|
+
}
|
170
|
+
return out;
|
171
|
+
}
|
172
|
+
end
|
173
|
+
|
174
|
+
def js_property?(name, raw: nil)
|
175
|
+
%x{
|
176
|
+
return #{js_property_name_rb2js(name, raw: raw)} in self.native
|
177
|
+
}
|
178
|
+
end
|
179
|
+
|
180
|
+
# Returns a wrapped property. If args are nil, functions are
|
181
|
+
# always returned verbatim.
|
182
|
+
def js_property(name, raw: nil, args: nil)
|
183
|
+
JSWrap(`self.native[#{
|
184
|
+
js_property_name_rb2js(name, raw: raw)
|
185
|
+
}]`,
|
186
|
+
self, args, name: name
|
187
|
+
)
|
188
|
+
end
|
189
|
+
|
190
|
+
def js_property_set(name, value, raw: nil)
|
191
|
+
`self.native[#{
|
192
|
+
js_property_name_rb2js(name, raw: raw)
|
193
|
+
}] = #{JSWrap.unwrap(value, self)}`
|
194
|
+
end
|
195
|
+
|
196
|
+
def js_raw_naming
|
197
|
+
@js_raw_naming.nil? ? self.class.js_raw_naming : @js_raw_naming
|
198
|
+
end
|
199
|
+
|
200
|
+
def js_function_wrap
|
201
|
+
@js_function_wrap.nil? ? self.class.js_function_wrap : @js_function_wrap
|
202
|
+
end
|
203
|
+
|
204
|
+
def js_array_wrap
|
205
|
+
@js_array_wrap.nil? ? self.class.js_array_wrap : @js_array_wrap
|
206
|
+
end
|
207
|
+
|
208
|
+
def to_js
|
209
|
+
@native
|
210
|
+
end
|
211
|
+
|
212
|
+
alias to_n to_js
|
213
|
+
end
|
214
|
+
|
215
|
+
module ObjectWrapper
|
216
|
+
include Enumerable
|
217
|
+
include Wrapper
|
218
|
+
|
219
|
+
alias [] js_property
|
220
|
+
|
221
|
+
def []=(name, kwargs = {}, value)
|
222
|
+
js_property_set(name, value, **kwargs)
|
223
|
+
end
|
224
|
+
|
225
|
+
alias keys js_properties
|
226
|
+
alias has? js_property
|
227
|
+
alias include? js_property
|
228
|
+
|
229
|
+
def values
|
230
|
+
to_h.values
|
231
|
+
end
|
232
|
+
|
233
|
+
def each(&block)
|
234
|
+
return enum_for(:each) { length } unless block_given?
|
235
|
+
|
236
|
+
%x{
|
237
|
+
for (var name in self.native) {
|
238
|
+
Opal.yieldX(block, [
|
239
|
+
#{js_property_name_js2rb `name`},
|
240
|
+
#{JSWrap(`self.native[name]`, self)}
|
241
|
+
]);
|
242
|
+
}
|
243
|
+
}
|
244
|
+
self
|
245
|
+
end
|
246
|
+
|
247
|
+
def inspect
|
248
|
+
if `inspect_stack`.include?(__id__)
|
249
|
+
inside = '...'
|
250
|
+
else
|
251
|
+
inside = to_h.inspect
|
252
|
+
`inspect_stack` << __id__
|
253
|
+
pushed = true
|
254
|
+
end
|
255
|
+
klass = `Object.prototype.toString.apply(self.native)`
|
256
|
+
klass =~ /^\[object (.*?)\]$/
|
257
|
+
"#<#{self.class.name} #{Regexp.last_match(1) || klass} #{inside}>"
|
258
|
+
ensure
|
259
|
+
`inspect_stack.pop()` if pushed
|
260
|
+
end
|
261
|
+
|
262
|
+
def pretty_print(o)
|
263
|
+
klass = `Object.prototype.toString.apply(self.native)`
|
264
|
+
klass =~ /^\[object (.*?)\]$/
|
265
|
+
o.group(1, "#<#{self.class.name} #{Regexp.last_match(1) || klass}", '>') do
|
266
|
+
o.breakable
|
267
|
+
o.pp(to_h)
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
271
|
+
def method_missing(method, *args, &block)
|
272
|
+
if method.end_with? '='
|
273
|
+
raise ArgumentError, 'JS attr assignment needs 1 argument' if args.length != 1
|
274
|
+
js_property_set(method[0..-2], args.first)
|
275
|
+
elsif js_property?(method)
|
276
|
+
args << block if block_given?
|
277
|
+
js_property(method, args: args)
|
278
|
+
else
|
279
|
+
super
|
280
|
+
end
|
281
|
+
end
|
282
|
+
|
283
|
+
def respond_to_missing?(method, include_all = true)
|
284
|
+
if method.end_with?('=') || js_property?(method)
|
285
|
+
true
|
286
|
+
else
|
287
|
+
super
|
288
|
+
end
|
289
|
+
end
|
290
|
+
|
291
|
+
def raw(raw = true)
|
292
|
+
self.dup.tap do |i|
|
293
|
+
i.instance_variable_set(:@js_raw_naming, true)
|
294
|
+
end
|
295
|
+
end
|
296
|
+
end
|
297
|
+
|
298
|
+
module ArrayWrapper
|
299
|
+
include Enumerable
|
300
|
+
include ObjectWrapper
|
301
|
+
|
302
|
+
def each(&block)
|
303
|
+
return enum_for(:each) { length } unless block_given?
|
304
|
+
|
305
|
+
%x{
|
306
|
+
for (var i = 0; i < self.native.length; i++) {
|
307
|
+
Opal.yield1(block, #{JSWrap(`self.native[i]`, self)});
|
308
|
+
}
|
309
|
+
}
|
310
|
+
self
|
311
|
+
end
|
312
|
+
|
313
|
+
def [](key)
|
314
|
+
if key.is_a? Number
|
315
|
+
JSWrap.wrap(`self.native[key]`, self)
|
316
|
+
else
|
317
|
+
super
|
318
|
+
end
|
319
|
+
end
|
320
|
+
|
321
|
+
def []=(key, kwargs = {}, value)
|
322
|
+
if key.is_a? Number
|
323
|
+
`self.native[key] = #{JSWrap.unwrap(value, self)}`
|
324
|
+
else
|
325
|
+
super
|
326
|
+
end
|
327
|
+
end
|
328
|
+
|
329
|
+
def <<(value)
|
330
|
+
`Array.prototype.push.apply(self.native, [#{JSWrap.unwrap(value, self)}])`
|
331
|
+
self
|
332
|
+
end
|
333
|
+
|
334
|
+
alias append <<
|
335
|
+
|
336
|
+
def length
|
337
|
+
`self.native.length`
|
338
|
+
end
|
339
|
+
|
340
|
+
def inspect
|
341
|
+
if `inspect_stack`.include?(__id__)
|
342
|
+
inside = '[...]'
|
343
|
+
else
|
344
|
+
`inspect_stack` << __id__
|
345
|
+
pushed = true
|
346
|
+
inside = to_a.inspect
|
347
|
+
end
|
348
|
+
klass = `Object.prototype.toString.apply(self.native)`
|
349
|
+
klass =~ /^\[object (.*?)\]$/
|
350
|
+
"#<#{self.class.name} #{Regexp.last_match(1) || klass} #{inside}>"
|
351
|
+
ensure
|
352
|
+
`inspect_stack.pop()` if pushed
|
353
|
+
end
|
354
|
+
|
355
|
+
def pretty_print(o)
|
356
|
+
klass = `Object.prototype.toString.apply(self.native)`
|
357
|
+
klass =~ /^\[object (.*?)\]$/
|
358
|
+
o.group(1, "#<#{self.class.name} #{Regexp.last_match(1) || klass}", '>') do
|
359
|
+
o.breakable
|
360
|
+
o.pp(to_a)
|
361
|
+
end
|
362
|
+
end
|
363
|
+
end
|
364
|
+
|
365
|
+
module FunctionWrapper
|
366
|
+
include ObjectWrapper
|
367
|
+
|
368
|
+
def call(*args, &block)
|
369
|
+
JSWrap.call(@native_parent, @native, *args, &block)
|
370
|
+
end
|
371
|
+
|
372
|
+
def to_proc
|
373
|
+
proc do |*args, &block|
|
374
|
+
call(*args, &block)
|
375
|
+
end
|
376
|
+
end
|
377
|
+
|
378
|
+
def inspect
|
379
|
+
if `typeof self.native.toString !== 'function'`
|
380
|
+
super
|
381
|
+
else
|
382
|
+
fundesc = `self.native.toString()`.split('{').first.strip.delete("\n")
|
383
|
+
"#<#{self.class.name} #{fundesc}>"
|
384
|
+
end
|
385
|
+
end
|
386
|
+
|
387
|
+
def pretty_print(o)
|
388
|
+
if `typeof self.native.toString !== 'function'`
|
389
|
+
super
|
390
|
+
else
|
391
|
+
o.text(inspect)
|
392
|
+
end
|
393
|
+
end
|
394
|
+
end
|
395
|
+
|
396
|
+
module RawClassWrapper
|
397
|
+
include Wrapper
|
398
|
+
|
399
|
+
def superclass
|
400
|
+
JSWrap(`#{@native}.prototype.__proto__`, self)
|
401
|
+
end
|
402
|
+
end
|
403
|
+
|
404
|
+
module ClassWrapper
|
405
|
+
include FunctionWrapper
|
406
|
+
include RawClassWrapper
|
407
|
+
|
408
|
+
def const_missing(name)
|
409
|
+
if js_property?(name)
|
410
|
+
js_property(name)
|
411
|
+
else
|
412
|
+
super
|
413
|
+
end
|
414
|
+
end
|
415
|
+
|
416
|
+
def new(*args, &block)
|
417
|
+
args << block if block_given?
|
418
|
+
args = JSWrap.unwrap_ary(args, self)
|
419
|
+
JSWrap(`new (#{@native}.bind.apply(#{@native}, [null].concat(args)))`, self)
|
420
|
+
end
|
421
|
+
end
|
422
|
+
|
423
|
+
def self.wrapped?(object)
|
424
|
+
`object != null && '$$class' in object && Opal.is_a(object, Opal.JSWrap.Wrapper)`
|
425
|
+
end
|
426
|
+
|
427
|
+
def self.unwrap(object, parent = nil)
|
428
|
+
%x{
|
429
|
+
if (object === null || object === undefined || object === nil) {
|
430
|
+
return null;
|
431
|
+
}
|
432
|
+
else if (object.$$class !== undefined) {
|
433
|
+
// Opal < 1.4 bug: Opal.respond_to vs #respond_to_missing? does not call
|
434
|
+
// #respond_to?
|
435
|
+
if (object.$to_js !== undefined && !object.$to_js.$$stub) {
|
436
|
+
return object.$to_js(parent);
|
437
|
+
}
|
438
|
+
else if (object.$to_n !== undefined && !object.$to_n.$$stub) {
|
439
|
+
return object.$to_n();
|
440
|
+
}
|
441
|
+
}
|
442
|
+
return object;
|
443
|
+
}
|
444
|
+
end
|
445
|
+
|
446
|
+
def self.unwrap_ary(ary, parent = nil)
|
447
|
+
%x{
|
448
|
+
var i;
|
449
|
+
for (i = 0; i < ary.length; i++) {
|
450
|
+
ary[i] = #{unwrap(ary[`i`], parent)}
|
451
|
+
}
|
452
|
+
return ary;
|
453
|
+
}
|
454
|
+
end
|
455
|
+
|
456
|
+
def self.unwrap_proc(proc, parent = nil)
|
457
|
+
%x{
|
458
|
+
var f = (function() {
|
459
|
+
var i, ary = Array.prototype.slice.apply(arguments), ret;
|
460
|
+
for (i = 0; i < ary.length; i++) {
|
461
|
+
ary[i] = #{wrap(`ary[i]`, parent)}
|
462
|
+
}
|
463
|
+
ret = proc.apply(null, ary);
|
464
|
+
return #{unwrap(`ret`, parent)}
|
465
|
+
});
|
466
|
+
f.$$proc_unwrapped = proc;
|
467
|
+
return f;
|
468
|
+
}
|
469
|
+
end
|
470
|
+
|
471
|
+
def self.wrap(object, parent = nil, args = nil, name: nil)
|
472
|
+
%x{
|
473
|
+
var i, out, wrapping;
|
474
|
+
for (i = self.wrappings.length - 1; i >= 0; i--) {
|
475
|
+
wrapping = self.wrappings[i];
|
476
|
+
if (wrapping.block !== nil) {
|
477
|
+
out = #{`wrapping.block`.call(object, parent, args, name)}
|
478
|
+
if (out == null) return nil;
|
479
|
+
else if (out !== nil) return out;
|
480
|
+
}
|
481
|
+
else if (object instanceof wrapping.js_constructor) {
|
482
|
+
return #{`wrapping.opal_klass`.wrap(`object`)}
|
483
|
+
}
|
484
|
+
}
|
485
|
+
}
|
486
|
+
end
|
487
|
+
|
488
|
+
def self.call(parent, item, *args, &block)
|
489
|
+
args << block if block_given?
|
490
|
+
orig_parent = parent
|
491
|
+
parent = unwrap(parent, orig_parent)
|
492
|
+
args = unwrap_ary(args, orig_parent)
|
493
|
+
%x{
|
494
|
+
if (typeof item !== 'function') {
|
495
|
+
item = parent[item];
|
496
|
+
}
|
497
|
+
item = item.apply(parent, args);
|
498
|
+
return #{wrap(item, orig_parent)}
|
499
|
+
}
|
500
|
+
end
|
501
|
+
|
502
|
+
@wrappings = []
|
503
|
+
def self.register_wrapping(opal_klass = undefined, js_constructor = undefined, priority: 0, &block)
|
504
|
+
@wrappings << `{
|
505
|
+
priority: priority,
|
506
|
+
opal_klass: opal_klass,
|
507
|
+
js_constructor: js_constructor,
|
508
|
+
block: block
|
509
|
+
}`
|
510
|
+
@wrappings.sort_by!(&`function(i){
|
511
|
+
return -i.priority;
|
512
|
+
}`
|
513
|
+
)
|
514
|
+
end
|
515
|
+
|
516
|
+
module WrapperClassMethods
|
517
|
+
include RawClassWrapper
|
518
|
+
end
|
519
|
+
|
520
|
+
class ObjectView
|
521
|
+
extend WrapperClassMethods
|
522
|
+
include ObjectWrapper
|
523
|
+
self.js_array_wrap = true
|
524
|
+
undef Array, String
|
525
|
+
end
|
526
|
+
class ArrayView
|
527
|
+
extend WrapperClassMethods
|
528
|
+
include ArrayWrapper
|
529
|
+
self.js_array_wrap = true
|
530
|
+
end
|
531
|
+
# ClassView does not work as a class fully. This is only a limited contract.
|
532
|
+
# ie. ClassView.wrap(`BigInt`).new.class == ObjectView.
|
533
|
+
# The reason why we inherit from Class is that so we can access properties like A::B
|
534
|
+
class ClassView < Class
|
535
|
+
extend WrapperClassMethods
|
536
|
+
include ClassWrapper
|
537
|
+
self.js_array_wrap = true
|
538
|
+
end
|
539
|
+
class FunctionView
|
540
|
+
extend WrapperClassMethods
|
541
|
+
include FunctionWrapper
|
542
|
+
self.js_array_wrap = true
|
543
|
+
end
|
544
|
+
end
|
545
|
+
|
546
|
+
module Kernel
|
547
|
+
def JSWrap(*args, &block)
|
548
|
+
JSWrap.wrap(*args, &block)
|
549
|
+
end
|
550
|
+
end
|
551
|
+
|
552
|
+
JSWrap.register_wrapping(priority: -10) do |item, parent, args, name|
|
553
|
+
%x{
|
554
|
+
var type = typeof item;
|
555
|
+
|
556
|
+
var array_wrap = $truthy(parent) ? $truthy(parent.$js_array_wrap()) : true;
|
557
|
+
var function_wrap = $truthy(parent) ? $truthy(parent.$js_function_wrap()) : false;
|
558
|
+
|
559
|
+
if (type === 'undefined' || item === null || item === nil) {
|
560
|
+
// A special case not documented anywhere: null makes it dispatch a nil.
|
561
|
+
if (args !== nil && args.length !== 0) {
|
562
|
+
#{raise ArgumentError, 'given args while dispatching a null value'}
|
563
|
+
}
|
564
|
+
return null;
|
565
|
+
}
|
566
|
+
else if (type === 'symbol' || type === 'bigint') {
|
567
|
+
// As of Opal 1.4, we don't support those. Let's pretend they
|
568
|
+
// are just objects.
|
569
|
+
if (args !== nil && args.length !== 0) {
|
570
|
+
#{raise ArgumentError, "given args while dispatching a #{`type`}"}
|
571
|
+
}
|
572
|
+
return #{JSWrap::ObjectView.wrap(item, parent)}
|
573
|
+
}
|
574
|
+
else if (type === 'number' || type === 'string' || type === 'boolean') {
|
575
|
+
// Otherwise it's some primitive and it's wrapped
|
576
|
+
if (args !== nil && args.length !== 0) {
|
577
|
+
#{raise ArgumentError, "given args while dispatching a #{`type`}"}
|
578
|
+
}
|
579
|
+
return item;
|
580
|
+
}
|
581
|
+
else if ('$$js_wrap' in item) {
|
582
|
+
// Wrapping is dispatched already, we can trust it to be wrapped properly
|
583
|
+
if (args !== nil && args.length !== 0) {
|
584
|
+
#{raise ArgumentError, 'given args while dispatching an already dispatched value'}
|
585
|
+
}
|
586
|
+
return item.$$js_wrap;
|
587
|
+
}
|
588
|
+
else if (type === 'function') {
|
589
|
+
if (item.$$class === Opal.Class) {
|
590
|
+
// Opal Class
|
591
|
+
if (args !== nil && args.length !== 0) {
|
592
|
+
#{raise ArgumentError, 'given args while dispatching an Opal class'}
|
593
|
+
}
|
594
|
+
return item;
|
595
|
+
}
|
596
|
+
else if ("$$arity" in item) {
|
597
|
+
// Native Opal proc
|
598
|
+
return item;
|
599
|
+
}
|
600
|
+
else if ("$$proc_unwrapped" in item) {
|
601
|
+
// Unwrapped native Opal proc
|
602
|
+
return item.$$proc_unwrapped;
|
603
|
+
}
|
604
|
+
else if (name !== nil && 'prototype' in item && name.match(/^[A-Z]/)) {
|
605
|
+
// Class
|
606
|
+
// There is no reliable way to detect a JS class. So we check if its
|
607
|
+
// name starts with an uppercase letter.
|
608
|
+
if (args !== nil && args.length !== 0) {
|
609
|
+
#{raise ArgumentError, 'given args while dispatching a class'}
|
610
|
+
}
|
611
|
+
return #{JSWrap::ClassView.wrap(item, parent)}
|
612
|
+
}
|
613
|
+
else {
|
614
|
+
// Regular function
|
615
|
+
if (function_wrap || args === nil) {
|
616
|
+
return #{JSWrap::FunctionView.wrap(item, parent)}
|
617
|
+
}
|
618
|
+
else {
|
619
|
+
var ret = #{JSWrap.call(parent, item, *args)}
|
620
|
+
return ret === nil ? null : ret;
|
621
|
+
}
|
622
|
+
}
|
623
|
+
}
|
624
|
+
else if (type === 'object') {
|
625
|
+
if (item instanceof Array) {
|
626
|
+
// A regular array
|
627
|
+
if (args !== nil && args.length !== 0) {
|
628
|
+
#{raise ArgumentError, 'given args while dispatching an array'}
|
629
|
+
}
|
630
|
+
if (array_wrap) {
|
631
|
+
return #{JSWrap::ArrayView.wrap(item, parent)}
|
632
|
+
}
|
633
|
+
else {
|
634
|
+
return item;
|
635
|
+
}
|
636
|
+
}
|
637
|
+
else if ('$$class' in item) {
|
638
|
+
// Opal item
|
639
|
+
if (args !== nil && args.length !== 0) {
|
640
|
+
#{raise ArgumentError, 'given args while dispatching an Opal object'}
|
641
|
+
}
|
642
|
+
return item;
|
643
|
+
}
|
644
|
+
else {
|
645
|
+
// Pass.
|
646
|
+
return nil;
|
647
|
+
}
|
648
|
+
}
|
649
|
+
else {
|
650
|
+
#{raise ArgumentError, "unknown value type #{type}"}
|
651
|
+
}
|
652
|
+
}
|
653
|
+
end
|
654
|
+
|
655
|
+
# Now custom wrappers run... but if nothing is found... we run a
|
656
|
+
# final wrapper:
|
657
|
+
|
658
|
+
JSWrap.register_wrapping(priority: 10) do |item, parent, args|
|
659
|
+
%x{
|
660
|
+
if (args !== nil && args.length !== 0) {
|
661
|
+
#{raise ArgumentError, 'given args while dispatching an object'}
|
662
|
+
}
|
663
|
+
else if ('length' in item) {
|
664
|
+
// A pseudo-array, we always wrap those
|
665
|
+
return #{JSWrap::ArrayView.wrap(item, parent)}
|
666
|
+
}
|
667
|
+
else {
|
668
|
+
// Otherwise it is a regulat object
|
669
|
+
return #{JSWrap::ObjectView.wrap(item, parent)}
|
670
|
+
}
|
671
|
+
}
|
672
|
+
end
|
673
|
+
|
674
|
+
class Hash
|
675
|
+
# @return a JavaScript object with the same keys but calling #to_n on
|
676
|
+
# all values.
|
677
|
+
def to_js(parent=nil)
|
678
|
+
%x{
|
679
|
+
var result = {},
|
680
|
+
keys = self.$$keys,
|
681
|
+
smap = self.$$smap,
|
682
|
+
key, value;
|
683
|
+
for (var i = 0, length = keys.length; i < length; i++) {
|
684
|
+
key = keys[i];
|
685
|
+
if (key.$$is_string) {
|
686
|
+
value = smap[key];
|
687
|
+
} else {
|
688
|
+
key = key.key;
|
689
|
+
value = key.value;
|
690
|
+
}
|
691
|
+
key = #{parent.js_property_name_rb2js(`key`)}
|
692
|
+
result[key] = #{JSWrap.unwrap(`value`, parent)}
|
693
|
+
}
|
694
|
+
return result;
|
695
|
+
}
|
696
|
+
end
|
697
|
+
end
|
698
|
+
|
699
|
+
class Method
|
700
|
+
def to_js(parent=nil)
|
701
|
+
JSWrap.unwrap_proc(to_proc, parent)
|
702
|
+
end
|
703
|
+
end
|
704
|
+
|
705
|
+
class Proc
|
706
|
+
def to_js(parent=nil)
|
707
|
+
%x{
|
708
|
+
// Is this a native Opal Proc? Or is it JavaScript
|
709
|
+
// created?
|
710
|
+
if ('$$arity' in self) {
|
711
|
+
return #{JSWrap.unwrap_proc(self, parent)}
|
712
|
+
}
|
713
|
+
else {
|
714
|
+
return self;
|
715
|
+
}
|
716
|
+
}
|
717
|
+
end
|
718
|
+
end
|
719
|
+
|
720
|
+
JSGlobal = JSWrap(`Opal.global`)
|