|  | // 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. | 
|  | // | 
|  | //   <script type="application/dart" src="..."></script> | 
|  | // | 
|  | 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); | 
|  | } | 
|  | })(); |