| Index: observatory_pub_packages/polymer/src/loader.dart
|
| ===================================================================
|
| --- observatory_pub_packages/polymer/src/loader.dart (revision 0)
|
| +++ observatory_pub_packages/polymer/src/loader.dart (working copy)
|
| @@ -0,0 +1,194 @@
|
| +// 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.
|
| +
|
| +part of polymer;
|
| +
|
| +/// Annotation used to automatically register polymer elements.
|
| +class CustomTag {
|
| + final String tagName;
|
| + const CustomTag(this.tagName);
|
| +}
|
| +
|
| +/// Metadata used to label static or top-level methods that are called
|
| +/// automatically when loading the library of a custom element.
|
| +const initMethod = const InitMethodAnnotation();
|
| +
|
| +/// Implementation behind [initMethod]. Only exposed for internal implementation
|
| +/// details
|
| +class InitMethodAnnotation {
|
| + const InitMethodAnnotation();
|
| +}
|
| +
|
| +/// Initializes a polymer application as follows:
|
| +/// * if running in development mode, set up a dirty-checking zone that polls
|
| +/// for observable changes
|
| +/// * initialize template binding and polymer-element
|
| +/// * for each library included transitively from HTML and HTML imports,
|
| +/// register custom elements declared there (labeled with [CustomTag]) and
|
| +/// invoke the initialization method on it (top-level functions annotated with
|
| +/// [initMethod]).
|
| +Zone initPolymer() {
|
| + _initializeLogging();
|
| + if (loader.deployMode) {
|
| + startPolymer(loader.initializers, loader.deployMode);
|
| + return Zone.current;
|
| + }
|
| + return dirtyCheckZone()..run(
|
| + () => startPolymer(loader.initializers, loader.deployMode));
|
| +}
|
| +
|
| +/// True if we're in deployment mode.
|
| +bool _deployMode = false;
|
| +
|
| +bool _startPolymerCalled = false;
|
| +
|
| +/// Starts polymer by running all [initializers] and hooking the polymer.js
|
| +/// code. **Note**: this function is not meant to be invoked directly by
|
| +/// application developers. It is invoked either by [initPolymer] or, if you are
|
| +/// using the experimental bootstrap API, this would be invoked by an entry
|
| +/// point that is automatically generated somehow. In particular, during
|
| +/// development, the entry point would be generated dynamically in `boot.js`.
|
| +/// Similarly, pub-build would generate the entry point for deployment.
|
| +void startPolymer(List<Function> initializers, [bool deployMode = true]) {
|
| + if (_startPolymerCalled) throw 'Initialization was already done.';
|
| + _startPolymerCalled = true;
|
| + _hookJsPolymer();
|
| + _deployMode = deployMode;
|
| +
|
| + if (initializers == null) {
|
| + throw 'Missing initialization of polymer elements. '
|
| + 'Please check that the list of entry points in your pubspec.yaml '
|
| + 'is correct. If you are using pub-serve, you may need to restart it.';
|
| + }
|
| +
|
| + Polymer.registerSync('auto-binding-dart', AutoBindingElement,
|
| + extendsTag: 'template');
|
| +
|
| + for (var initializer in initializers) {
|
| + initializer();
|
| + }
|
| +
|
| + _watchWaitingFor();
|
| +}
|
| +
|
| +/// Configures [initPolymer] making it optimized for deployment to the internet.
|
| +/// With this setup the initializer list is supplied instead of searched for
|
| +/// at runtime. Additionally, after this method is called [initPolymer] omits
|
| +/// the [Zone] that automatically invokes [Observable.dirtyCheck].
|
| +void configureForDeployment(List<Function> initializers) {
|
| + loader.initializers = initializers;
|
| + loader.deployMode = true;
|
| +}
|
| +
|
| +/// To ensure Dart can interoperate with polymer-element registered by
|
| +/// polymer.js, we need to be able to execute Dart code if we are registering
|
| +/// a Dart class for that element. We trigger Dart logic by patching
|
| +/// polymer-element's register function and:
|
| +///
|
| +/// * if it has a Dart class, run PolymerDeclaration's register.
|
| +/// * otherwise it is a JS prototype, run polymer-element's normal register.
|
| +void _hookJsPolymer() {
|
| + var polymerJs = js.context['Polymer'];
|
| + if (polymerJs == null) {
|
| + throw new StateError('polymer.js must be loaded before polymer.dart, please'
|
| + ' add <link rel="import" href="packages/polymer/polymer.html"> to your'
|
| + ' <head> before any Dart scripts. Alternatively you can get a different'
|
| + ' version of polymer.js by following the instructions at'
|
| + ' http://www.polymer-project.org.');
|
| + }
|
| +
|
| + // TODO(jmesserly): dart:js appears to not callback in the correct zone:
|
| + // https://code.google.com/p/dart/issues/detail?id=17301
|
| + var zone = Zone.current;
|
| +
|
| + polymerJs.callMethod('whenPolymerReady',
|
| + [zone.bindCallback(() => Polymer._onReady.complete())]);
|
| +
|
| + JsFunction originalRegister = _polymerElementProto['register'];
|
| + if (originalRegister == null) {
|
| + throw new StateError('polymer.js must expose "register" function on '
|
| + 'polymer-element to enable polymer.dart to interoperate.');
|
| + }
|
| +
|
| + registerDart(jsElem, String name, String extendee) {
|
| + // By the time we get here, we'll know for sure if it is a Dart object
|
| + // or not, because polymer-element will wait for us to notify that
|
| + // the @CustomTag was found.
|
| + final type = _getRegisteredType(name);
|
| + if (type != null) {
|
| + final extendsDecl = _getDeclaration(extendee);
|
| + return zone.run(() =>
|
| + new PolymerDeclaration(jsElem, name, type, extendsDecl).register());
|
| + }
|
| + // It's a JavaScript polymer element, fall back to the original register.
|
| + return originalRegister.apply([name, extendee], thisArg: jsElem);
|
| + }
|
| +
|
| + _polymerElementProto['register'] = new JsFunction.withThis(registerDart);
|
| +}
|
| +
|
| +// Note: we cache this so we can use it later to look up 'init'.
|
| +// See registerSync.
|
| +JsObject _polymerElementProto = () {
|
| + var polyElem = document.createElement('polymer-element');
|
| + var proto = new JsObject.fromBrowserObject(polyElem)['__proto__'];
|
| + if (proto is Node) proto = new JsObject.fromBrowserObject(proto);
|
| + return proto;
|
| +}();
|
| +
|
| +// Add support for the polymer js style of enabling logging. The global logging
|
| +// level is respected for specified loggers (see http://goo.gl/btfDe1). All
|
| +// other loggers will be set to [Level.OFF]. Logs will also be printed to the
|
| +// console automatically if any are supplied.
|
| +void _initializeLogging() {
|
| + hierarchicalLoggingEnabled = true;
|
| + var webComponents = js.context['WebComponents'];
|
| + var logFlags = (webComponents == null || webComponents['flags'] == null) ? {}
|
| + : webComponents['flags']['log'];
|
| + if (logFlags == null) logFlags = {};
|
| + var loggers =
|
| + [_observeLog, _eventsLog, _unbindLog, _bindLog, _watchLog, _readyLog];
|
| + var polymerLogger = new Logger('polymer');
|
| +
|
| + // If no loggers specified then disable globally and return.
|
| + if (!loggers.any((logger) => logFlags[logger.name] == true)) {
|
| + polymerLogger.level = Level.OFF;
|
| + return;
|
| + }
|
| +
|
| + // Disable the loggers that were not specified.
|
| + loggers.where((logger) => logFlags[logger.name] != true)
|
| + .forEach((logger) {logger.level = Level.OFF;});
|
| +
|
| + // Listen to the polymer logs and print them to the console.
|
| + polymerLogger.onRecord.listen((rec) {print(rec);});
|
| +}
|
| +
|
| +/// Watches the waitingFor queue and if it fails to make progress then prints
|
| +/// a message to the console.
|
| +void _watchWaitingFor() {
|
| + int lastWaiting = Polymer.waitingFor.length;
|
| + int lastAlert;
|
| + new Timer.periodic(new Duration(seconds: 1), (Timer timer) {
|
| + var waiting = Polymer.waitingFor;
|
| + // Done, cancel timer.
|
| + if (waiting.isEmpty) {
|
| + timer.cancel();
|
| + return;
|
| + }
|
| + // Made progress, don't alert.
|
| + if (waiting.length != lastWaiting) {
|
| + lastWaiting = waiting.length;
|
| + return;
|
| + }
|
| + // Only alert once per waiting state.
|
| + if (lastAlert == lastWaiting) return;
|
| + lastAlert = lastWaiting;
|
| +
|
| + print('No elements registered in a while, but still waiting on '
|
| + '${waiting.length} elements to be registered. Check that you have a '
|
| + 'class with an @CustomTag annotation for each of the following tags: '
|
| + '${waiting.map((e) => "'${e.attributes['name']}'").join(', ')}');
|
| + });
|
| +}
|
|
|