| OLD | NEW | 
|    1 // Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file |    1 // Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file | 
|    2 // for details. All rights reserved. Use of this source code is governed by a |    2 // for details. All rights reserved. Use of this source code is governed by a | 
|    3 // BSD-style license that can be found in the LICENSE file. |    3 // BSD-style license that can be found in the LICENSE file. | 
|    4  |    4  | 
|    5 /** |    5 /** | 
|    6  * Support for interoperating with JavaScript. |    6  * Support for interoperating with JavaScript. | 
|    7  *  |    7  * | 
|    8  * This library provides access to JavaScript objects from Dart, allowing |    8  * This library provides access to JavaScript objects from Dart, allowing | 
|    9  * Dart code to get and set properties, and call methods of JavaScript objects |    9  * Dart code to get and set properties, and call methods of JavaScript objects | 
|   10  * and invoke JavaScript functions. The library takes care of converting |   10  * and invoke JavaScript functions. The library takes care of converting | 
|   11  * between Dart and JavaScript objects where possible, or providing proxies if |   11  * between Dart and JavaScript objects where possible, or providing proxies if | 
|   12  * conversion isn't possible. |   12  * conversion isn't possible. | 
|   13  * |   13  * | 
|   14  * This library does not yet make Dart objects usable from JavaScript, their |   14  * This library does not yet make Dart objects usable from JavaScript, their | 
|   15  * methods and proeprties are not accessible, though it does allow Dart |   15  * methods and proeprties are not accessible, though it does allow Dart | 
|   16  * functions to be passed into and called from JavaScript. |   16  * functions to be passed into and called from JavaScript. | 
|   17  * |   17  * | 
|   18  * [JsObject] is the core type and represents a proxy of a JavaScript object. |   18  * [JsObject] is the core type and represents a proxy of a JavaScript object. | 
|   19  * JsObject gives access to the underlying JavaScript objects properties and |   19  * JsObject gives access to the underlying JavaScript objects properties and | 
|   20  * methods. `JsObject`s can be acquired by calls to JavaScript, or they can be |   20  * methods. `JsObject`s can be acquired by calls to JavaScript, or they can be | 
|   21  * created from proxies to JavaScript constructors. |   21  * created from proxies to JavaScript constructors. | 
|   22  * |   22  * | 
|   23  * The top-level getter [context] provides a [JsObject] that represents the |   23  * The top-level getter [context] provides a [JsObject] that represents the | 
|   24  * global object in JavaScript, usually `window`. |   24  * global object in JavaScript, usually `window`. | 
|   25  * |   25  * | 
|   26  * The following example shows an alert dialog via a JavaScript call to the |   26  * The following example shows an alert dialog via a JavaScript call to the | 
|   27  * global function `alert()`: |   27  * global function `alert()`: | 
|   28  * |   28  * | 
|   29  *     import 'dart:js'; |   29  *     import 'dart:js'; | 
|   30  *      |   30  * | 
|   31  *     main() => context.callMethod('alert', ['Hello from Dart!']); |   31  *     main() => context.callMethod('alert', ['Hello from Dart!']); | 
|   32  * |   32  * | 
|   33  * This example shows how to create a [JsObject] from a JavaScript constructor |   33  * This example shows how to create a [JsObject] from a JavaScript constructor | 
|   34  * and access its properties: |   34  * and access its properties: | 
|   35  * |   35  * | 
|   36  *     import 'dart:js'; |   36  *     import 'dart:js'; | 
|   37  *      |   37  * | 
|   38  *     main() { |   38  *     main() { | 
|   39  *       var object = new JsObject(context['Object']); |   39  *       var object = new JsObject(context['Object']); | 
|   40  *       object['greeting'] = 'Hello'; |   40  *       object['greeting'] = 'Hello'; | 
|   41  *       object['greet'] = (name) => "${object['greeting']} $name"; |   41  *       object['greet'] = (name) => "${object['greeting']} $name"; | 
|   42  *       var message = object.callMethod('greet', ['JavaScript']); |   42  *       var message = object.callMethod('greet', ['JavaScript']); | 
|   43  *       context['console'].callMethod('log', [message]); |   43  *       context['console'].callMethod('log', [message]); | 
|   44  *     } |   44  *     } | 
|   45  * |   45  * | 
|   46  * ## Proxying and automatic conversion |   46  * ## Proxying and automatic conversion | 
|   47  *  |   47  * | 
|   48  * When setting properties on a JsObject or passing arguments to a Javascript |   48  * When setting properties on a JsObject or passing arguments to a Javascript | 
|   49  * method or function, Dart objects are automatically converted or proxied to |   49  * method or function, Dart objects are automatically converted or proxied to | 
|   50  * JavaScript objects. When accessing JavaScript properties, or when a Dart |   50  * JavaScript objects. When accessing JavaScript properties, or when a Dart | 
|   51  * closure is invoked from JavaScript, the JavaScript objects are also |   51  * closure is invoked from JavaScript, the JavaScript objects are also | 
|   52  * converted to Dart. |   52  * converted to Dart. | 
|   53  * |   53  * | 
|   54  * Functions and closures are proxied in such a way that they are callable. A |   54  * Functions and closures are proxied in such a way that they are callable. A | 
|   55  * Dart closure assigned to a JavaScript property is proxied by a function in |   55  * Dart closure assigned to a JavaScript property is proxied by a function in | 
|   56  * JavaScript. A JavaScript function accessed from Dart is proxied by a |   56  * JavaScript. A JavaScript function accessed from Dart is proxied by a | 
|   57  * [JsFunction], which has a [apply] method to invoke it. |   57  * [JsFunction], which has a [apply] method to invoke it. | 
| (...skipping 15 matching lines...) Expand all  Loading... | 
|   73  * ## Converting collections with JsObject.jsify() |   73  * ## Converting collections with JsObject.jsify() | 
|   74  * |   74  * | 
|   75  * To create a JavaScript collection from a Dart collection use the |   75  * To create a JavaScript collection from a Dart collection use the | 
|   76  * [JsObject.jsify] constructor, which converts Dart [Map]s and [Iterable]s |   76  * [JsObject.jsify] constructor, which converts Dart [Map]s and [Iterable]s | 
|   77  * into JavaScript Objects and Arrays. |   77  * into JavaScript Objects and Arrays. | 
|   78  * |   78  * | 
|   79  * The following expression creats a new JavaScript object with the properties |   79  * The following expression creats a new JavaScript object with the properties | 
|   80  * `a` and `b` defined: |   80  * `a` and `b` defined: | 
|   81  * |   81  * | 
|   82  *     var jsMap = new JsObject.jsify({'a': 1, 'b': 2}); |   82  *     var jsMap = new JsObject.jsify({'a': 1, 'b': 2}); | 
|   83  *  |   83  * | 
|   84  * This expression creates a JavaScript array: |   84  * This expression creates a JavaScript array: | 
|   85  * |   85  * | 
|   86  *     var jsArray = new JsObject.jsify([1, 2, 3]); |   86  *     var jsArray = new JsObject.jsify([1, 2, 3]); | 
|   87  */ |   87  */ | 
|   88 library dart.js; |   88 library dart.js; | 
|   89  |   89  | 
|   90 import 'dart:html' show Blob, Event, ImageData, Node, Window; |   90 import 'dart:html' show Blob, Event, ImageData, Node, Window; | 
|   91 import 'dart:collection' show HashMap, ListMixin; |   91 import 'dart:collection' show HashMap, ListMixin; | 
|   92 import 'dart:indexed_db' show KeyRange; |   92 import 'dart:indexed_db' show KeyRange; | 
|   93 import 'dart:typed_data' show TypedData; |   93 import 'dart:typed_data' show TypedData; | 
| (...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
|  158     return _wrapToDart(jsObj); |  158     return _wrapToDart(jsObj); | 
|  159   } |  159   } | 
|  160  |  160  | 
|  161   /** |  161   /** | 
|  162    * Constructs a [JsObject] that proxies a native Dart object; _for expert use |  162    * Constructs a [JsObject] that proxies a native Dart object; _for expert use | 
|  163    * only_. |  163    * only_. | 
|  164    * |  164    * | 
|  165    * Use this constructor only if you wish to get access to JavaScript |  165    * Use this constructor only if you wish to get access to JavaScript | 
|  166    * properties attached to a browser host object, such as a Node or Blob, that |  166    * properties attached to a browser host object, such as a Node or Blob, that | 
|  167    * is normally automatically converted into a native Dart object. |  167    * is normally automatically converted into a native Dart object. | 
|  168    *  |  168    * | 
|  169    * An exception will be thrown if [object] either is `null` or has the type |  169    * An exception will be thrown if [object] either is `null` or has the type | 
|  170    * `bool`, `num`, or `String`. |  170    * `bool`, `num`, or `String`. | 
|  171    */ |  171    */ | 
|  172   factory JsObject.fromBrowserObject(object) { |  172   factory JsObject.fromBrowserObject(object) { | 
|  173     if (object is num || object is String || object is bool || object == null) { |  173     if (object is num || object is String || object is bool || object == null) { | 
|  174       throw new ArgumentError( |  174       throw new ArgumentError( | 
|  175         "object cannot be a num, string, bool, or null"); |  175         "object cannot be a num, string, bool, or null"); | 
|  176     } |  176     } | 
|  177     return _wrapToDart(_convertToJS(object)); |  177     return _wrapToDart(_convertToJS(object)); | 
|  178   } |  178   } | 
| (...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
|  225    * object. |  225    * object. | 
|  226    * |  226    * | 
|  227    * The type of [property] must be either [String] or [num]. |  227    * The type of [property] must be either [String] or [num]. | 
|  228    */ |  228    */ | 
|  229   dynamic operator[](property) { |  229   dynamic operator[](property) { | 
|  230     if (property is! String && property is! num) { |  230     if (property is! String && property is! num) { | 
|  231       throw new ArgumentError("property is not a String or num"); |  231       throw new ArgumentError("property is not a String or num"); | 
|  232     } |  232     } | 
|  233     return _convertToDart(JS('', '#[#]', _jsObject, property)); |  233     return _convertToDart(JS('', '#[#]', _jsObject, property)); | 
|  234   } |  234   } | 
|  235    |  235  | 
|  236   /** |  236   /** | 
|  237    * Sets the value associated with [property] on the proxied JavaScript |  237    * Sets the value associated with [property] on the proxied JavaScript | 
|  238    * object. |  238    * object. | 
|  239    * |  239    * | 
|  240    * The type of [property] must be either [String] or [num]. |  240    * The type of [property] must be either [String] or [num]. | 
|  241    */ |  241    */ | 
|  242   operator[]=(property, value) { |  242   operator[]=(property, value) { | 
|  243     if (property is! String && property is! num) { |  243     if (property is! String && property is! num) { | 
|  244       throw new ArgumentError("property is not a String or num"); |  244       throw new ArgumentError("property is not a String or num"); | 
|  245     } |  245     } | 
| (...skipping 157 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
|  403   void set length(int length) { super['length'] = length; } |  403   void set length(int length) { super['length'] = length; } | 
|  404  |  404  | 
|  405  |  405  | 
|  406   // Methods overriden for better performance |  406   // Methods overriden for better performance | 
|  407  |  407  | 
|  408   void add(E value) { |  408   void add(E value) { | 
|  409     callMethod('push', [value]); |  409     callMethod('push', [value]); | 
|  410   } |  410   } | 
|  411  |  411  | 
|  412   void addAll(Iterable<E> iterable) { |  412   void addAll(Iterable<E> iterable) { | 
|  413     var list = (JS('bool', '# instanceof Array', iterable))  |  413     var list = (JS('bool', '# instanceof Array', iterable)) | 
|  414         ? iterable |  414         ? iterable | 
|  415         : new List.from(iterable); |  415         : new List.from(iterable); | 
|  416     callMethod('push', list); |  416     callMethod('push', list); | 
|  417   } |  417   } | 
|  418  |  418  | 
|  419   void insert(int index, E element) { |  419   void insert(int index, E element) { | 
|  420     _checkInsertIndex(index); |  420     _checkInsertIndex(index); | 
|  421     callMethod('splice', [index, 0, element]); |  421     callMethod('splice', [index, 0, element]); | 
|  422   } |  422   } | 
|  423  |  423  | 
| (...skipping 19 matching lines...) Expand all  Loading... | 
|  443     if (skipCount < 0) throw new ArgumentError(skipCount); |  443     if (skipCount < 0) throw new ArgumentError(skipCount); | 
|  444     var args = [start, length]..addAll(iterable.skip(skipCount).take(length)); |  444     var args = [start, length]..addAll(iterable.skip(skipCount).take(length)); | 
|  445     callMethod('splice', args); |  445     callMethod('splice', args); | 
|  446   } |  446   } | 
|  447  |  447  | 
|  448   void sort([int compare(E a, E b)]) { |  448   void sort([int compare(E a, E b)]) { | 
|  449     callMethod('sort', [compare]); |  449     callMethod('sort', [compare]); | 
|  450   } |  450   } | 
|  451 } |  451 } | 
|  452  |  452  | 
 |  453 // When multiple Dart programs are run in the same page we must make sure that | 
 |  454 // they don't share their Dart wrappers. We use a global (shared) counter in | 
 |  455 // the Object class to assign a unique id to every Dart program. | 
 |  456 int _getAndUpdateDisambiguationId() { | 
 |  457   const DISAMBIGUATION_PROPERTY_NAME = "__DART_INTEROP_COUNTER_"; | 
 |  458   int counter = JS("int", 'Object[#]||0', DISAMBIGUATION_PROPERTY_NAME); | 
 |  459   JS("", 'Object[#]=#', DISAMBIGUATION_PROPERTY_NAME, counter + 1); | 
 |  460   return counter; | 
 |  461 } | 
 |  462  | 
 |  463 final int _disambiguationId = _getAndUpdateDisambiguationId(); | 
 |  464  | 
 |  465 // Converts the given string to a v8 internal symbol which is faster. | 
 |  466 String _intern(String str) { | 
 |  467   return JS("String", "convertToJSSymbol(#)", str); | 
 |  468 } | 
 |  469  | 
|  453 // property added to a Dart object referencing its JS-side DartObject proxy |  470 // property added to a Dart object referencing its JS-side DartObject proxy | 
|  454 const _DART_OBJECT_PROPERTY_NAME = r'_$dart_dartObject'; |  471 final String _DART_OBJECT_PROPERTY_NAME = | 
|  455 const _DART_CLOSURE_PROPERTY_NAME = r'_$dart_dartClosure'; |  472     _intern("_\$dart_dartObject$_disambiguationId"); | 
 |  473 final String _DART_CLOSURE_PROPERTY_NAME = | 
 |  474     _intern("_\$dart_dartObject$_disambiguationId"); | 
|  456  |  475  | 
|  457 // property added to a JS object referencing its Dart-side JsObject proxy |  476 // property added to a JS object referencing its Dart-side JsObject proxy | 
|  458 const _JS_OBJECT_PROPERTY_NAME = r'_$dart_jsObject'; |  477 const _JS_OBJECT_PROPERTY_NAME = r'_$dart_jsObject'; | 
|  459 const _JS_FUNCTION_PROPERTY_NAME = r'$dart_jsFunction'; |  478 const _JS_FUNCTION_PROPERTY_NAME = r'$dart_jsFunction'; | 
|  460  |  479  | 
|  461 bool _defineProperty(o, String name, value) { |  480 bool _defineProperty(o, String name, value) { | 
|  462   if (JS('bool', 'Object.isExtensible(#)', o)) { |  481   if (JS('bool', 'Object.isExtensible(#)', o)) { | 
|  463     try { |  482     try { | 
|  464       JS('void', 'Object.defineProperty(#, #, { value: #})', o, name, value); |  483       JS('void', 'Object.defineProperty(#, #, { value: #})', o, name, value); | 
|  465       return true; |  484       return true; | 
| (...skipping 91 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
|  557   // Dart in that context. The JS object will have a cached proxy |  576   // Dart in that context. The JS object will have a cached proxy | 
|  558   // but it won't be a valid Dart object in this context. |  577   // but it won't be a valid Dart object in this context. | 
|  559   // For now we throw away the cached proxy, but we should be able |  578   // For now we throw away the cached proxy, but we should be able | 
|  560   // to cache proxies from multiple JS contexts and Dart isolates. |  579   // to cache proxies from multiple JS contexts and Dart isolates. | 
|  561   if (dartProxy == null || !_isLocalObject(o)) { |  580   if (dartProxy == null || !_isLocalObject(o)) { | 
|  562     dartProxy = createProxy(o); |  581     dartProxy = createProxy(o); | 
|  563     _defineProperty(o, propertyName, dartProxy); |  582     _defineProperty(o, propertyName, dartProxy); | 
|  564   } |  583   } | 
|  565   return dartProxy; |  584   return dartProxy; | 
|  566 } |  585 } | 
| OLD | NEW |