OLD | NEW |
| (Empty) |
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 | |
3 // BSD-style license that can be found in the LICENSE file. | |
4 | |
5 part of polymer; | |
6 | |
7 /// Initializes a polymer application as follows: | |
8 /// * if running in development mode, set up a dirty-checking zone that polls | |
9 /// for observable changes | |
10 /// * initialize template binding and polymer-element | |
11 /// * for each library included transitively from HTML and HTML imports, | |
12 /// register custom elements declared there (labeled with [CustomTag]) and | |
13 /// invoke the initialization method on it (top-level functions annotated with | |
14 /// [initMethod]). | |
15 Future<Zone> initPolymer() { | |
16 _initializeLogging(); | |
17 if (_deployMode) { | |
18 return startPolymer().then((_) => Zone.current); | |
19 } | |
20 return dirtyCheckZone() | |
21 .run(() => startPolymer().then((_) => dirtyCheckZone())); | |
22 } | |
23 | |
24 bool _startPolymerCalled = false; | |
25 | |
26 /// Starts polymer by hooking the polymer.js code. **Note**: this function is | |
27 /// not meant to be invoked directly by application developers. It is invoked | |
28 /// by [initPolymer]. | |
29 Future startPolymer() { | |
30 // First wait for all html imports to finish, then run the rest of the | |
31 // initializers. | |
32 return initWebComponents(initAll: false).then((_) { | |
33 // Polymer js is now loaded, hook it before running @CustomTag annotations. | |
34 if (_startPolymerCalled) throw 'Initialization was already done.'; | |
35 _startPolymerCalled = true; | |
36 _hookJsPolymer(); | |
37 }).then((_) => initWebComponents()).then((_) { | |
38 Polymer.registerSync('auto-binding-dart', AutoBindingElement, | |
39 extendsTag: 'template'); | |
40 | |
41 _watchWaitingFor(); | |
42 Polymer._onInitDone.complete(); | |
43 }); | |
44 } | |
45 | |
46 /// Configures [initPolymer] making it optimized for deployment to the internet. | |
47 /// Additionally, after this method is called [initPolymer] omits the [Zone] | |
48 /// that automatically invokes [Observable.dirtyCheck]. | |
49 void configureForDeployment() { | |
50 _deployMode = true; | |
51 } | |
52 | |
53 /// To ensure Dart can interoperate with polymer-element registered by | |
54 /// polymer.js, we need to be able to execute Dart code if we are registering | |
55 /// a Dart class for that element. We trigger Dart logic by patching | |
56 /// polymer-element's register function and: | |
57 /// | |
58 /// * if it has a Dart class, run PolymerDeclaration's register. | |
59 /// * otherwise it is a JS prototype, run polymer-element's normal register. | |
60 void _hookJsPolymer() { | |
61 if (!PolymerJs.checkExists()) { | |
62 throw new StateError('An error occurred initializing polymer, (could not' | |
63 'find polymer js). Please file a bug at ' | |
64 'https://github.com/dart-lang/polymer-dart/issues/new.'); | |
65 } | |
66 | |
67 // TODO(jmesserly): dart:js appears to not callback in the correct zone: | |
68 // https://code.google.com/p/dart/issues/detail?id=17301 | |
69 var zone = Zone.current; | |
70 | |
71 PolymerJs.whenPolymerReady(() => Polymer._onReady.complete()); | |
72 | |
73 JsFunction originalRegister = _polymerElementProto['register']; | |
74 if (originalRegister == null) { | |
75 throw new StateError('polymer.js must expose "register" function on ' | |
76 'polymer-element to enable polymer.dart to interoperate.'); | |
77 } | |
78 | |
79 registerDart(jsElem, String name, String extendee) { | |
80 // By the time we get here, we'll know for sure if it is a Dart object | |
81 // or not, because polymer-element will wait for us to notify that | |
82 // the @CustomTag was found. | |
83 final type = _getRegisteredType(name); | |
84 if (type != null) { | |
85 final extendsDecl = _getDeclaration(extendee); | |
86 return zone.run(() => | |
87 new PolymerDeclaration(jsElem, name, type, extendsDecl).register()); | |
88 } | |
89 // It's a JavaScript polymer element, fall back to the original register. | |
90 return originalRegister.apply([name, extendee], thisArg: jsElem); | |
91 } | |
92 | |
93 _polymerElementProto['register'] = new JsFunction.withThis(registerDart); | |
94 } | |
95 | |
96 // Note: we cache this so we can use it later to look up 'init'. | |
97 // See registerSync. | |
98 JsObject _polymerElementProto = () { | |
99 var polyElem = document.createElement('polymer-element'); | |
100 var proto = new JsObject.fromBrowserObject(polyElem)['__proto__']; | |
101 if (proto is Node) proto = new JsObject.fromBrowserObject(proto); | |
102 return proto; | |
103 }(); | |
104 | |
105 // Add support for the polymer js style of enabling logging. The global logging | |
106 // level is respected for specified loggers (see http://goo.gl/btfDe1). All | |
107 // other loggers will be set to [Level.OFF]. Logs will also be printed to the | |
108 // console automatically if any are supplied. | |
109 void _initializeLogging() { | |
110 hierarchicalLoggingEnabled = true; | |
111 var webComponents = js.context['WebComponents']; | |
112 var logFlags = (webComponents == null || webComponents['flags'] == null) | |
113 ? {} | |
114 : webComponents['flags']['log']; | |
115 if (logFlags == null) logFlags = {}; | |
116 var loggers = [ | |
117 _observeLog, | |
118 _eventsLog, | |
119 _unbindLog, | |
120 _bindLog, | |
121 _watchLog, | |
122 _readyLog | |
123 ]; | |
124 var polymerLogger = new Logger('polymer'); | |
125 | |
126 // If no loggers specified then disable globally and return. | |
127 if (!loggers.any((logger) => logFlags[logger.name] == true)) { | |
128 polymerLogger.level = Level.OFF; | |
129 return; | |
130 } | |
131 | |
132 // Disable the loggers that were not specified. | |
133 loggers.where((logger) => logFlags[logger.name] != true).forEach((logger) { | |
134 logger.level = Level.OFF; | |
135 }); | |
136 | |
137 // Listen to the polymer logs and print them to the console. | |
138 polymerLogger.onRecord.listen((rec) { | |
139 print(rec); | |
140 }); | |
141 } | |
142 | |
143 /// Watches the waitingFor queue and if it fails to make progress then prints | |
144 /// a message to the console. | |
145 void _watchWaitingFor() { | |
146 int lastWaiting = Polymer.waitingFor.length; | |
147 int lastAlert; | |
148 new Timer.periodic(new Duration(seconds: 1), (Timer timer) { | |
149 var waiting = Polymer.waitingFor; | |
150 // Done, cancel timer. | |
151 if (waiting.isEmpty) { | |
152 timer.cancel(); | |
153 return; | |
154 } | |
155 // Made progress, don't alert. | |
156 if (waiting.length != lastWaiting) { | |
157 lastWaiting = waiting.length; | |
158 return; | |
159 } | |
160 // Only alert once per waiting state. | |
161 if (lastAlert == lastWaiting) return; | |
162 lastAlert = lastWaiting; | |
163 | |
164 print('No elements registered in a while, but still waiting on ' | |
165 '${waiting.length} elements to be registered. Check that you have a ' | |
166 'class with an @CustomTag annotation for each of the following tags: ' | |
167 '${waiting.map((e) => "'${e.attributes['name']}'").join(', ')}'); | |
168 }); | |
169 } | |
OLD | NEW |