// Copyright (c) 2012, 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. var warning = [ 'WARNING: This page is using a deprecated dart.js file. ', 'Please update this page as described here: ', 'http://news.dartlang.org/2013/01/big-breaking-change-dartjs-bootstrap-file-moving-to-pub.html' ].join(''); console.error(warning); // Bootstrap support for Dart scripts on the page as this script. if (navigator.userAgent.indexOf('(Dart)') === -1) { // TODO: // - Support in-browser compilation. // - Handle inline Dart scripts. window.addEventListener("DOMContentLoaded", function (e) { // Fall back to compiled JS. Run through all the scripts and // replace them if they have a type that indicate that they source // in Dart code. // // // var scripts = document.getElementsByTagName("script"); var length = scripts.length; for (var i = 0; i < length; ++i) { if (scripts[i].type == "application/dart") { // Remap foo.dart to foo.dart.js. if (scripts[i].src && scripts[i].src != '') { var script = document.createElement('script'); script.src = scripts[i].src + '.js'; var parent = scripts[i].parentNode; parent.replaceChild(script, scripts[i]); } } } }, false); } // --------------------------------------------------------------------------- // Experimental support for JS interoperability // --------------------------------------------------------------------------- function SendPortSync() { } function ReceivePortSync() { this.id = ReceivePortSync.id++; ReceivePortSync.map[this.id] = this; } (function() { // Serialize the following types as follows: // - primitives / null: unchanged // - lists: [ 'list', internal id, list of recursively serialized elements ] // - maps: [ 'map', internal id, map of keys and recursively serialized values ] // - send ports: [ 'sendport', type, isolate id, port id ] // // Note, internal id's are for cycle detection. function serialize(message) { var visited = []; function checkedSerialization(obj, serializer) { // Implementation detail: for now use linear search. // Another option is expando, but it may prohibit // VM optimizations (like putting object into slow mode // on property deletion.) var id = visited.indexOf(obj); if (id != -1) return [ 'ref', id ]; var id = visited.length; visited.push(obj); return serializer(id); } function doSerialize(message) { if (message == null) { return null; // Convert undefined to null. } else if (typeof(message) == 'string' || typeof(message) == 'number' || typeof(message) == 'boolean') { return message; } else if (message instanceof Array) { return checkedSerialization(message, function(id) { var values = new Array(message.length); for (var i = 0; i < message.length; i++) { values[i] = doSerialize(message[i]); } return [ 'list', id, values ]; }); } else if (message instanceof LocalSendPortSync) { return [ 'sendport', 'nativejs', message.receivePort.id ]; } else if (message instanceof DartSendPortSync) { return [ 'sendport', 'dart', message.isolateId, message.portId ]; } else { return checkedSerialization(message, function(id) { var keys = Object.getOwnPropertyNames(message); var values = new Array(keys.length); for (var i = 0; i < keys.length; i++) { values[i] = doSerialize(message[keys[i]]); } return [ 'map', id, keys, values ]; }); } } return doSerialize(message); } function deserialize(message) { return deserializeHelper(message); } function deserializeHelper(message) { if (message == null || typeof(message) == 'string' || typeof(message) == 'number' || typeof(message) == 'boolean') { return message; } switch (message[0]) { case 'map': return deserializeMap(message); case 'sendport': return deserializeSendPort(message); case 'list': return deserializeList(message); default: throw 'unimplemented'; } } function deserializeMap(message) { var result = { }; var id = message[1]; var keys = message[2]; var values = message[3]; for (var i = 0, length = keys.length; i < length; i++) { var key = deserializeHelper(keys[i]); var value = deserializeHelper(values[i]); result[key] = value; } return result; } function deserializeSendPort(message) { var tag = message[1]; switch (tag) { case 'nativejs': var id = message[2]; return new LocalSendPortSync(ReceivePortSync.map[id]); case 'dart': var isolateId = message[2]; var portId = message[3]; return new DartSendPortSync(isolateId, portId); default: throw 'Illegal SendPortSync type: $tag'; } } function deserializeList(message) { var values = message[2]; var length = values.length; var result = new Array(length); for (var i = 0; i < length; i++) { result[i] = deserializeHelper(values[i]); } return result; } window.registerPort = function(name, port) { var stringified = JSON.stringify(serialize(port)); var attrName = 'dart-port:' + name; document.documentElement.setAttribute(attrName, stringified); // TODO(vsm): Phase out usage of localStorage and delete the // below. We're leaving it in temporarily for backwards // compatibility. try { window.localStorage[attrName] = stringified; } catch (e) { // Swallow errors (e.g., Chrome apps disallow this access). } }; window.lookupPort = function(name) { var attrName = 'dart-port:' + name; var stringified = document.documentElement.getAttribute(attrName); // TODO(vsm): Phase out usage of localStorage. We're leaving it in // temporarily for backwards compatibility. if (!stringified) { stringified = window.localStorage[attrName]; } return deserialize(JSON.parse(stringified)); }; ReceivePortSync.id = 0; ReceivePortSync.map = {}; ReceivePortSync.dispatchCall = function(id, message) { // TODO(vsm): Handle and propagate exceptions. var deserialized = deserialize(message); var result = ReceivePortSync.map[id].callback(deserialized); return serialize(result); }; ReceivePortSync.prototype.receive = function(callback) { this.callback = callback; }; ReceivePortSync.prototype.toSendPort = function() { return new LocalSendPortSync(this); }; ReceivePortSync.prototype.close = function() { delete ReceivePortSync.map[this.id]; }; if (navigator.userAgent.indexOf('(Dart)') !== -1) { window.addEventListener('js-sync-message', function(event) { var data = JSON.parse(getPortSyncEventData(event)); var deserialized = deserialize(data.message); var result = ReceivePortSync.map[data.id].callback(deserialized); // TODO(vsm): Handle and propagate exceptions. dispatchEvent('js-result', serialize(result)); }, false); } function LocalSendPortSync(receivePort) { this.receivePort = receivePort; } LocalSendPortSync.prototype = new SendPortSync(); LocalSendPortSync.prototype.callSync = function(message) { // TODO(vsm): Do a direct deepcopy. message = deserialize(serialize(message)); return this.receivePort.callback(message); } function DartSendPortSync(isolateId, portId) { this.isolateId = isolateId; this.portId = portId; } DartSendPortSync.prototype = new SendPortSync(); function dispatchEvent(receiver, message) { var string = JSON.stringify(message); var event = document.createEvent('CustomEvent'); event.initCustomEvent(receiver, false, false, string); window.dispatchEvent(event); } function getPortSyncEventData(event) { return event.detail; } DartSendPortSync.prototype.callSync = function(message) { var serialized = serialize(message); var target = 'dart-port-' + this.isolateId + '-' + this.portId; // TODO(vsm): Make this re-entrant. // TODO(vsm): Set this up set once, on the first call. var source = target + '-result'; var result = null; var listener = function (e) { result = JSON.parse(getPortSyncEventData(e)); }; window.addEventListener(source, listener, false); dispatchEvent(target, [source, serialized]); window.removeEventListener(source, listener, false); return deserialize(result); } })();