blob: 7f806b73f3744794eeca4b725e61093b6cf12008 [file] [log] [blame]
// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
library dart.js;
import 'dart:_foreign_helper' show JS;
import 'dart:_js_helper' show convertDartClosureToJS;
JsObject get context {
return new JsObject._fromJs(JS('=Object', 'window'));
}
JsObject jsify(dynamic data) => data == null ? null : new JsObject._json(data);
class Callback implements Serializable<JsFunction> {
final Function _f; // here to allow capture in closure
final bool _withThis; // here to allow capture in closure
dynamic _jsFunction;
Callback._(this._f, this._withThis) {
_jsFunction = JS('=Object', r'''
(function(){
var f = #;
return function(){
return f(this, Array.prototype.slice.apply(arguments));
};
}).apply(this)''', convertDartClosureToJS(_call, 2));
}
factory Callback(Function f) => new Callback._(f, false);
factory Callback.withThis(Function f) => new Callback._(f, true);
_call(thisArg, List args) {
final arguments = new List.from(args);
if (_withThis) arguments.insert(0, thisArg);
final dartArgs = arguments.map(_convertToDart).toList();
return _convertToJS(Function.apply(_f, dartArgs));
}
JsFunction toJs() => new JsFunction._fromJs(_jsFunction);
}
class JsObject implements Serializable<JsObject> {
final dynamic _jsObject;
JsObject._fromJs(this._jsObject);
// TODO(vsm): Type constructor as Serializable<JsFunction> when
// dartbug.com/11854 is fixed.
factory JsObject(var constructor, [List arguments]) {
final constr = _convertToJS(constructor);
if (arguments == null) {
return new JsObject._fromJs(JS('=Object', 'new #()', constr));
}
final args = arguments.map(_convertToJS).toList();
switch (args.length) {
case 0:
return new JsObject._fromJs(JS('=Object', 'new #()', constr));
case 1:
return new JsObject._fromJs(JS('=Object', 'new #(#)', constr, args[0]));
case 2:
return new JsObject._fromJs(JS('=Object', 'new #(#,#)', constr, args[0],
args[1]));
case 3:
return new JsObject._fromJs(JS('=Object', 'new #(#,#,#)', constr,
args[0], args[1], args[2]));
case 4:
return new JsObject._fromJs(JS('=Object', 'new #(#,#,#,#)', constr,
args[0], args[1], args[2], args[3]));
case 5:
return new JsObject._fromJs(JS('=Object', 'new #(#,#,#,#,#)', constr,
args[0], args[1], args[2], args[3], args[4]));
case 6:
return new JsObject._fromJs(JS('=Object', 'new #(#,#,#,#,#,#)', constr,
args[0], args[1], args[2], args[3], args[4], args[5]));
case 7:
return new JsObject._fromJs(JS('=Object', 'new #(#,#,#,#,#,#,#)',
constr, args[0], args[1], args[2], args[3], args[4], args[5],
args[6]));
case 8:
return new JsObject._fromJs(JS('=Object', 'new #(#,#,#,#,#,#,#,#)',
constr, args[0], args[1], args[2], args[3], args[4], args[5],
args[6], args[7]));
case 9:
return new JsObject._fromJs(JS('=Object', 'new #(#,#,#,#,#,#,#,#,#)',
constr, args[0], args[1], args[2], args[3], args[4], args[5],
args[6], args[7], args[8]));
case 10:
return new JsObject._fromJs(JS('=Object', 'new #(#,#,#,#,#,#,#,#,#,#)',
constr, args[0], args[1], args[2], args[3], args[4], args[5],
args[6], args[7], args[8], args[9]));
}
return new JsObject._fromJs(JS('=Object', r'''(function(){
var Type = function(){};
Type.prototype = #.prototype;
var instance = new Type();
ret = #.apply(instance, #);
ret = Object(ret) === ret ? ret : instance;
return ret;
})()''', constr, constr, args));
}
factory JsObject._json(data) => new JsObject._fromJs(_convertDataTree(data));
static _convertDataTree(data) {
if (data is Map) {
final convertedData = JS('=Object', '{}');
for (var key in data.keys) {
JS('=Object', '#[#]=#', convertedData, key,
_convertDataTree(data[key]));
}
return convertedData;
} else if (data is Iterable) {
return data.map(_convertDataTree).toList();
} else {
return _convertToJS(data);
}
}
JsObject toJs() => this;
operator[](key) =>
_convertToDart(JS('=Object', '#[#]', _convertToJS(this), key));
operator[]=(key, value) => JS('void', '#[#]=#', _convertToJS(this), key,
_convertToJS(value));
int get hashCode => 0;
operator==(other) => other is JsObject &&
JS('bool', '# === #', _convertToJS(this), _convertToJS(other));
bool hasProperty(String property) => JS('bool', '# in #', property,
_convertToJS(this));
void deleteProperty(String name) {
JS('void', 'delete #[#]', _convertToJS(this), name);
}
// TODO(vsm): Type type as Serializable<JsFunction> when
// dartbug.com/11854 is fixed.
bool instanceof(var type) =>
JS('bool', '# instanceof #', _convertToJS(this), _convertToJS(type));
String toString() {
try {
return JS('String', '#.toString()', _convertToJS(this));
} catch(e) {
return super.toString();
}
}
callMethod(String name, [List args]) =>
_convertToDart(JS('=Object', '#[#].apply(#, #)', _convertToJS(this), name,
_convertToJS(this),
args == null ? null : args.map(_convertToJS).toList()));
}
class JsFunction extends JsObject implements Serializable<JsFunction> {
JsFunction._fromJs(jsObject) : super._fromJs(jsObject);
apply(thisArg, [List args]) =>
_convertToDart(JS('=Object', '#.apply(#, #)', _convertToJS(this),
_convertToJS(thisArg),
args == null ? null : args.map(_convertToJS).toList()));
}
abstract class Serializable<T> {
T toJs();
}
dynamic _convertToJS(dynamic o) {
if (o == null) {
return null;
} else if (o is String || o is num || o is bool) {
return o;
} else if (o is JsObject) {
return o._jsObject;
} else if (o is Serializable) {
return _convertToJS(o.toJs());
} else if (o is Function) {
return _convertToJS(new Callback(o));
} else {
return JS('=Object', 'new DartProxy(#)', o);
}
}
dynamic _convertToDart(dynamic o) {
if (JS('bool', '# == null', o)) {
return null;
} else if (JS('bool', 'typeof # == "string" || # instanceof String', o, o) ||
JS('bool', 'typeof # == "number" || # instanceof Number', o, o) ||
JS('bool', 'typeof # == "boolean" || # instanceof Boolean', o, o)) {
return o;
} else if (JS('bool', '# instanceof Function', o)) {
return new JsFunction._fromJs(JS('=Object', '#', o));
} else if (JS('bool', '# instanceof DartProxy', o)) {
return JS('var', '#.o', o);
} else {
return new JsObject._fromJs(JS('=Object', '#', o));
}
}