Chromium Code Reviews| OLD | NEW | 
|---|---|
| 1 library apidoc_model; | 1 library apidoc_model; | 
| 2 | 2 | 
| 3 import 'package:web_ui/watcher.dart' as watchers; | 3 import 'package:web_ui/watcher.dart' as watchers; | 
| 4 import 'package:web_ui/safe_html.dart'; | 4 import 'package:web_ui/safe_html.dart'; | 
| 5 import 'markdown.dart' as md; | 5 import 'markdown.dart' as md; | 
| 6 import 'dart:html' as html; | 6 import 'dart:html' as html; | 
| 7 import 'dart:json'; | 7 import 'dart:json'; | 
| 8 import 'ast.dart'; | 8 import 'ast.dart'; | 
| 9 | 9 | 
| 10 // TODO(jacobr): specify the version # in the JSON file. | |
| 
 
Siggi Cherem (dart-lang)
2012/12/19 19:47:33
+1 :)
 
Jacob
2013/01/02 19:54:58
acknowledged :)
 
 | |
| 10 String svnRevisionNumber = "15605"; | 11 String svnRevisionNumber = "15605"; | 
| 11 | 12 | 
| 12 String _activeReferenceId; | 13 /** | 
| 14 * Reference id of [currentElement]. | |
| 15 * * | |
| 
 
Siggi Cherem (dart-lang)
2012/12/19 19:47:33
remove extra *
 
Jacob
2013/01/02 19:54:58
rm * 
:)
 
 | |
| 16 * Stored in addition to [currentElement] as [currentElement] may | |
| 17 * not yet be available if the data model for the library it is part of has | |
| 18 * not yet been loaded. | |
| 19 */ | |
| 20 String _currentReferenceId; | |
| 13 | 21 | 
| 14 /// Current state of the application. | 22 /** | 
| 23 * Current library the user is browsing if any. | |
| 24 */ | |
| 15 LibraryElement currentLibrary; | 25 LibraryElement currentLibrary; | 
| 26 | |
| 27 /** | |
| 28 * Current type the user is viewing if any. | |
| 29 * Should be either a [ClassElement] or a [TypedefElement]. | |
| 30 */ | |
| 16 Element currentType; | 31 Element currentType; | 
| 32 /** | |
| 33 * Current member of either [currentLibrary] or [currentType] that the user is | |
| 34 * viewing. | |
| 35 */ | |
| 17 Element currentMember; | 36 Element currentMember; | 
| 37 | |
| 38 /** | |
| 39 * Element corresponding to [_currentReferenceId]. | |
| 40 * The most specific element of [currentLibrary]. [currentType], and | |
| 41 * [currentMember]. | |
| 42 */ | |
| 18 Element currentElement; | 43 Element currentElement; | 
| 19 | 44 | 
| 45 /** | |
| 46 * Recomputes the Elements part of the current active state from the data model. | |
| 47 * | |
| 48 * This method should be invoked after additional libraries are loaded from the | |
| 49 * server or after the user navigates to a different element in the UI. | |
| 50 */ | |
| 20 void _recomputeActiveState() { | 51 void _recomputeActiveState() { | 
| 21 currentLibrary = null; | 52 currentLibrary = null; | 
| 22 currentType = null; | 53 currentType = null; | 
| 23 currentMember = null; | 54 currentMember = null; | 
| 24 currentElement = null; | 55 currentElement = null; | 
| 25 if (_activeReferenceId != null) { | 56 if (_currentReferenceId != null) { | 
| 26 var path = lookupReferenceId(_activeReferenceId).path; | 57 var referenceElement = lookupReferenceId(_currentReferenceId); | 
| 27 | 58 if (referenceElement != null) { | 
| 28 if (path.length > 0) { | 59 var path = referenceElement.path; | 
| 29 currentLibrary = path[0]; | 60 if (path.length > 0) { | 
| 30 } | 61 currentLibrary = path[0]; | 
| 31 if (path.length > 1) { | 62 } | 
| 32 if (path[1] is ClassElement || path[1] is TypedefElement) { | 63 if (path.length > 1) { | 
| 33 currentType = path[1]; | 64 if (path[1] is ClassElement || path[1] is TypedefElement) { | 
| 34 if (path.length > 2) { | 65 currentType = path[1]; | 
| 35 currentMember = path[2]; | 66 if (path.length > 2) { | 
| 67 currentMember = path[2]; | |
| 68 } | |
| 69 } else { | |
| 70 currentMember = path[1]; | |
| 36 } | 71 } | 
| 72 } | |
| 73 if (currentMember != null) { | |
| 74 currentElement = currentMember; | |
| 75 } else if (currentType != null) { | |
| 76 currentElement = currentType; | |
| 37 } else { | 77 } else { | 
| 38 currentMember = path[1]; | 78 currentElement = currentLibrary; | 
| 39 } | 79 } | 
| 40 } | 80 } | 
| 41 if (currentMember != null) { | |
| 42 currentElement = currentMember; | |
| 43 } else if (currentType != null) { | |
| 44 currentElement = currentType; | |
| 45 } else { | |
| 46 currentElement = currentLibrary; | |
| 47 } | |
| 48 } | 81 } | 
| 49 } | 82 } | 
| 50 | 83 | 
| 84 /** | |
| 85 * Scrolls the [currentElement] into view. | |
| 86 */ | |
| 51 void scrollIntoView() { | 87 void scrollIntoView() { | 
| 52 // TODO(jacobr): there should be a cleaner way to run code that executes | 88 // TODO(jacobr): there should be a cleaner way to run code that executes | 
| 53 // after the UI updates. | 89 // after the UI updates. | 
| 
 
Siggi Cherem (dart-lang)
2012/12/19 19:47:33
let's add an issue for this in web-ui.
 
Jacob
2013/01/02 19:54:58
Referenced https://github.com/dart-lang/web-ui/iss
 
Siggi Cherem (dart-lang)
2013/01/02 21:40:55
Thanks! Sorry that I asked you to filed it and the
 
 | |
| 54 html.window.setTimeout(() { | 90 html.window.setTimeout(() { | 
| 55 if (currentElement != null) { | 91 if (currentElement != null) { | 
| 56 for (var e in html.queryAll('[data-id="${currentElement.id}"]')) { | 92 for (var e in html.queryAll('[data-id="${currentElement.id}"]')) { | 
| 57 e.scrollIntoView(false); | 93 e.scrollIntoView(false); | 
| 58 } | 94 } | 
| 59 } | 95 } | 
| 60 }, 0); | 96 }, 0); | 
| 61 } | 97 } | 
| 62 | 98 | 
| 63 onDataModelChanged() { | 99 /** | 
| 100 * Invoke every time the data model changes. | |
| 101 */ | |
| 102 void _onDataModelChanged() { | |
| 64 _recomputeActiveState(); | 103 _recomputeActiveState(); | 
| 65 scrollIntoView(); | 104 scrollIntoView(); | 
| 66 } | 105 } | 
| 67 | 106 | 
| 68 /** | 107 /** | 
| 69 * Generate a CSS class given an element that may be a class, member, method, | 108 * Generate a CSS class given an element that may be a class, member, method, | 
| 70 * etc. | 109 * etc. | 
| 71 */ | 110 */ | 
| 72 String kindCssClass(Element element) { | 111 String kindCssClass(Element element) { | 
| 73 String classes = 'kind kind-${normalizedKind(element)}'; | 112 String classes = 'kind kind-${_normalizedKind(element)}'; | 
| 74 if (element.isPrivate == true) { | 113 if (element.isPrivate == true) { | 
| 75 classes = '$classes private'; | 114 classes = '$classes private'; | 
| 76 } else if (element is MethodElementBase && element.isStatic) { | 115 } else if (element is MethodElementBase && element.isStatic) { | 
| 77 classes = '$classes static'; | 116 classes = '$classes static'; | 
| 78 } | 117 } | 
| 79 | 118 | 
| 80 // Setters are viewed as methods by the AST. | 119 // Setters are viewed as methods by the AST. | 
| 81 if (element is PropertyElement) { | 120 if (element is PropertyElement) { | 
| 82 classes = '$classes getter'; | 121 classes = '$classes getter'; | 
| 83 } | 122 } | 
| 84 | 123 | 
| 85 if (element is MethodElementBase && element.isSetter) { | 124 if (element is MethodElementBase && element.isSetter) { | 
| 86 classes = '$classes setter'; | 125 classes = '$classes setter'; | 
| 87 } | 126 } | 
| 88 | 127 | 
| 89 return classes; | 128 return classes; | 
| 90 } | 129 } | 
| 91 | 130 | 
| 92 String normalizedKind(obj) { | 131 String _normalizedKind(Element element) { | 
| 93 if (obj is Element) return normalizedKindFromElement(obj); | |
| 94 return obj; | |
| 95 } | |
| 96 | |
| 97 String normalizedKindFromElement(Element element) { | |
| 98 var kind = element.kind; | 132 var kind = element.kind; | 
| 99 var name = element.name; | 133 var name = element.name; | 
| 100 if (kind == 'method' && element.isOperator) { | 134 if (kind == 'method' && element.isOperator) { | 
| 101 kind = 'operator'; | 135 kind = 'operator'; | 
| 102 } | 136 } | 
| 103 // TODO(jacobr): this is horrible but matches what DartDoc does | 137 // TODO(jacobr): this is horrible but matches what DartDoc does | 
| 104 if (kind == 'class' && name.endsWith('Exception')) { | 138 if (kind == 'class' && name.endsWith('Exception')) { | 
| 105 kind = 'exception'; | 139 kind = 'exception'; | 
| 106 } | 140 } | 
| 107 return kind; | 141 return kind; | 
| 108 } | 142 } | 
| 109 | 143 | 
| 110 String toUserVisibleKind(Element element) { | 144 String toUserVisibleKind(Element element) { | 
| 111 return KIND_TITLES[normalizedKind(element)]; | 145 return KIND_TITLES[_normalizedKind(element)]; | 
| 112 } | 146 } | 
| 113 | 147 | 
| 114 /** | 148 /** | 
| 115 * [obj] shoudl be a [Reference] or [Element]. | 149 * [obj] shoudl be a [Reference] or [Element]. | 
| 116 */ | 150 */ | 
| 117 String permalink(var obj) { | 151 String permalink(var obj) { | 
| 118 var data = {'id': obj.refId}; | 152 var data = {'id': obj.refId}; | 
| 153 // TODO(jacobr): evaluate whether the persistent UI state will stay just a | |
| 154 // single reference ID in which case this is overkill. | |
| 119 return "#!${JSON.stringify(data)}"; | 155 return "#!${JSON.stringify(data)}"; | 
| 120 } | 156 } | 
| 121 | 157 | 
| 122 void loadStateFromUrl() { | 158 void loadStateFromUrl() { | 
| 123 String link = html.window.location.hash; | 159 String link = html.window.location.hash; | 
| 124 var data = {}; | 160 var data = {}; | 
| 125 if (link.length > 2) { | 161 if (link.length > 2) { | 
| 126 try { | 162 try { | 
| 127 // strip #! and parse json. | 163 // strip #! and parse json. | 
| 128 data = JSON.parse(link.substring(2)); | 164 data = JSON.parse(link.substring(2)); | 
| 129 } catch (e) { | 165 } catch (e) { | 
| 130 html.window.console.error("Invalid link url"); | 166 html.window.console.error("Invalid link url"); | 
| 131 // TODO(jacobr): redirect to default page or better yet attempt to fixup. | 167 // TODO(jacobr): redirect to default page or better yet attempt to fixup. | 
| 132 } | 168 } | 
| 133 } | 169 } | 
| 134 _activeReferenceId = data['id']; | 170 _currentReferenceId = data['id']; | 
| 135 _recomputeActiveState(); | 171 _recomputeActiveState(); | 
| 136 scrollIntoView(); | 172 scrollIntoView(); | 
| 137 } | 173 } | 
| 138 | 174 | 
| 139 Future loadModel() { | 175 Future loadModel() { | 
| 140 html.window.on.popState.add((e) { | 176 html.window.on.popState.add((e) { | 
| 
 
Siggi Cherem (dart-lang)
2012/12/19 19:47:33
in our todomvc sample we had to listen for both po
 
Siggi Cherem (dart-lang)
2013/01/02 21:40:55
you might have missed this comment...
 
Jacob
2013/01/03 00:09:33
I did... done :)
 
 | |
| 141 loadStateFromUrl(); | 177 loadStateFromUrl(); | 
| 142 watchers.dispatch(); | 178 watchers.dispatch(); | 
| 143 }); | 179 }); | 
| 144 | 180 | 
| 145 // Patch in support for [:...:]-style code to the markdown parser. | 181 // Patch in support for [:...:]-style code to the markdown parser. | 
| 146 // TODO(rnystrom): Markdown already has syntax for this. Phase this out? | 182 // TODO(rnystrom): Markdown already has syntax for this. Phase this out? | 
| 147 md.InlineParser.syntaxes.insertRange(0, 1, | 183 md.InlineParser.syntaxes.insertRange(0, 1, | 
| 148 new md.CodeSyntax(r'\[\:((?:.|\n)*?)\:\]')); | 184 new md.CodeSyntax(r'\[\:((?:.|\n)*?)\:\]')); | 
| 149 | 185 | 
| 150 md.setImplicitLinkResolver(_resolveNameReference); | 186 md.setImplicitLinkResolver(_resolveNameReference); | 
| 151 var completer = new Completer(); | 187 var completer = new Completer(); | 
| 152 // TODO(jacobr): shouldn't have to get this from the parent directory. | 188 // TODO(jacobr): shouldn't have to get this from the parent directory. | 
| 153 new html.HttpRequest.get('../static/apidoc.json', onSuccess(html.HttpRequest r eq) { | 189 new html.HttpRequest.get('../static/apidoc.json', (req) { | 
| 154 for (var libraryJson in JSON.parse(req.responseText)) { | 190 for (var libraryJson in JSON.parse(req.responseText)) { | 
| 155 var library = new LibraryElement(libraryJson, null); | 191 var library = new LibraryElement(libraryJson, null); | 
| 156 libraries[library.id] = library; | 192 libraries[library.id] = library; | 
| 157 } | 193 } | 
| 158 onDataModelChanged(); | 194 _onDataModelChanged(); | 
| 159 completer.complete(true); | 195 completer.complete(true); | 
| 160 }); | 196 }); | 
| 161 return completer.future; | 197 return completer.future; | 
| 162 } | 198 } | 
| 163 | 199 | 
| 164 /** XXX NOT USED TODO(jacobr) remove. | 200 // TODO(jacobr): remove this method and resolve refences to types in the json | 
| 165 class DocComment { | 201 // generation. That way the existing correct logic in Dart2Js can be used rather | 
| 166 final String text; | 202 // than this rather busted logic. | 
| 167 | |
| 168 /** | |
| 169 * Non-null if the comment is inherited from another declaration. | |
| 170 */ | |
| 171 final inheritedFrom; // InterfaceMirror? | |
| 172 | |
| 173 DocComment(this.text, [this.inheritedFrom = null]) { | |
| 174 assert(text != null && !text.trim().isEmpty); | |
| 175 } | |
| 176 | |
| 177 SafeHtml get html => new SafeHtml.unsafe(md.markdownToHtml(text)); | |
| 178 | |
| 179 String toString() => text; | |
| 180 } | |
| 181 | |
| 182 */ | |
| 183 | |
| 184 /** | 203 /** | 
| 185 * This will be called whenever a doc comment hits a `[name]` in square | 204 * This will be called whenever a doc comment hits a `[name]` in square | 
| 186 * brackets. It will try to figure out what the name refers to and link or | 205 * brackets. It will try to figure out what the name refers to and link or | 
| 187 * style it appropriately. | 206 * style it appropriately. | 
| 188 */ | 207 */ | 
| 189 md.Node _resolveNameReference(String name) { | 208 md.Node _resolveNameReference(String name) { | 
| 190 // TODO(jacobr): this isn't right yet and we have made this code quite ugly | 209 // TODO(jacobr): this isn't right yet and we have made this code quite ugly | 
| 191 // by using the verbose universal permalink member even though library is | 210 // by using the verbose universal permalink member even though library is | 
| 192 // always currentLibrary. | 211 // always currentLibrary. | 
| 193 makeLink(String href) { | 212 makeLink(String href) { | 
| 194 return new md.Element.text('a', name) | 213 return new md.Element.text('a', name) | 
| 195 ..attributes['href'] = href | 214 ..attributes['href'] = href | 
| 196 ..attributes['class'] = 'crossref'; | 215 ..attributes['class'] = 'crossref'; | 
| 197 } | 216 } | 
| 198 | 217 | 
| 199 // See if it's a parameter of the current method. | 218 // See if it's a parameter of the current method. | 
| 200 if (currentMember != null && currentMember.kind == 'method') { | 219 if (currentMember != null && currentMember.kind == 'method') { | 
| 201 var parameters = currentMember.children; | 220 var parameters = currentMember.children; | 
| 202 for (final parameter in parameters) { | 221 for (final parameter in parameters) { | 
| 203 if (parameter.name == name) { | 222 if (parameter.name == name) { | 
| 204 final element = new md.Element.text('span', name); | 223 final element = new md.Element.text('span', name); | 
| 205 element.attributes['class'] = 'param'; | 224 element.attributes['class'] = 'param'; | 
| 206 return element; | 225 return element; | 
| 207 } | 226 } | 
| 208 } | 227 } | 
| 209 } | 228 } | 
| 210 | 229 | 
| 211 // See if it's another member of the current type. | 230 // See if it's another member of the current type. | 
| 212 // TODO(jacobr): fixme. this is wrong... members are by id now not simple stri ng name... | 231 // TODO(jacobr): fixme. this is wrong... members are by id now not simple | 
| 232 // string name... | |
| 213 if (currentType != null) { | 233 if (currentType != null) { | 
| 214 final foundMember = currentType.members[name]; | 234 final foundMember = currentType.members[name]; | 
| 215 if (foundMember != null) { | 235 if (foundMember != null) { | 
| 216 return makeLink(permalink(foundMember)); | 236 return makeLink(permalink(foundMember)); | 
| 217 } | 237 } | 
| 218 } | 238 } | 
| 219 | 239 | 
| 220 // See if it's another type or a member of another type in the current | 240 // See if it's another type or a member of another type in the current | 
| 221 // library. | 241 // library. | 
| 222 if (currentLibrary != null) { | 242 if (currentLibrary != null) { | 
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 261 } | 281 } | 
| 262 } | 282 } | 
| 263 | 283 | 
| 264 // TODO(jacobr): Should also consider: | 284 // TODO(jacobr): Should also consider: | 
| 265 // * Names imported by libraries this library imports. Don't think we even | 285 // * Names imported by libraries this library imports. Don't think we even | 
| 266 // store this in the AST. | 286 // store this in the AST. | 
| 267 // * Type parameters of the enclosing type. | 287 // * Type parameters of the enclosing type. | 
| 268 | 288 | 
| 269 return new md.Element.text('code', name); | 289 return new md.Element.text('code', name); | 
| 270 } | 290 } | 
| OLD | NEW |