| Index: client/web/model.dart | 
| diff --git a/client/web/model.dart b/client/web/model.dart | 
| index 87401a2c5ee5fde847759ed6704c5ddca817ba38..985b51b1e85aa424b2063781267ce0d52c42ee1c 100644 | 
| --- a/client/web/model.dart | 
| +++ b/client/web/model.dart | 
| @@ -1,3 +1,7 @@ | 
| +// 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. | 
| + | 
| library apidoc_model; | 
|  | 
| import 'package:web_ui/watcher.dart' as watchers; | 
| @@ -7,50 +11,86 @@ import 'dart:html' as html; | 
| import 'dart:json'; | 
| import 'ast.dart'; | 
|  | 
| +// TODO(jacobr): specify the version # in the JSON file. | 
| String svnRevisionNumber = "15605"; | 
|  | 
| -String _activeReferenceId; | 
| +/** | 
| + * Reference id of [currentElement]. | 
| + * | 
| + * Stored in addition to [currentElement] as [currentElement] may | 
| + * not yet be available if the data model for the library it is part of has | 
| + * not yet been loaded. | 
| + */ | 
| +String _currentReferenceId; | 
|  | 
| -/// Current state of the application. | 
| +/** | 
| + * Current library the user is browsing if any. | 
| + */ | 
| LibraryElement currentLibrary; | 
| + | 
| +/** | 
| + * Current type the user is viewing if any. | 
| + * Should be either a [ClassElement] or a [TypedefElement]. | 
| + */ | 
| Element currentType; | 
| +/** | 
| + * Current member of either [currentLibrary] or [currentType] that the user is | 
| + * viewing. | 
| + */ | 
| Element currentMember; | 
| + | 
| +/** | 
| + * Element corresponding to [_currentReferenceId]. | 
| + * The most specific element of [currentLibrary]. [currentType], and | 
| + * [currentMember]. | 
| + */ | 
| Element currentElement; | 
|  | 
| +/** | 
| + * Recomputes the Elements part of the current active state from the data model. | 
| + * | 
| + * This method should be invoked after additional libraries are loaded from the | 
| + * server or after the user navigates to a different element in the UI. | 
| + */ | 
| void _recomputeActiveState() { | 
| currentLibrary = null; | 
| currentType = null; | 
| currentMember = null; | 
| currentElement = null; | 
| -  if (_activeReferenceId != null) { | 
| -    var path = lookupReferenceId(_activeReferenceId).path; | 
| - | 
| -    if (path.length > 0) { | 
| -      currentLibrary = path[0]; | 
| -    } | 
| -    if (path.length > 1) { | 
| -      if (path[1] is ClassElement || path[1] is TypedefElement) { | 
| -        currentType = path[1]; | 
| -        if (path.length > 2) { | 
| -          currentMember = path[2]; | 
| +  if (_currentReferenceId != null) { | 
| +    var referenceElement = lookupReferenceId(_currentReferenceId); | 
| +    if (referenceElement != null) { | 
| +      var path = referenceElement.path; | 
| +      if (path.length > 0) { | 
| +        currentLibrary = path[0]; | 
| +      } | 
| +      if (path.length > 1) { | 
| +        if (path[1] is ClassElement || path[1] is TypedefElement) { | 
| +          currentType = path[1]; | 
| +          if (path.length > 2) { | 
| +            currentMember = path[2]; | 
| +          } | 
| +        } else { | 
| +          currentMember = path[1]; | 
| } | 
| +      } | 
| +      if (currentMember != null) { | 
| +        currentElement = currentMember; | 
| +      } else if (currentType != null) { | 
| +        currentElement = currentType; | 
| } else { | 
| -        currentMember = path[1]; | 
| +        currentElement = currentLibrary; | 
| } | 
| } | 
| -    if (currentMember != null) { | 
| -      currentElement = currentMember; | 
| -    } else if (currentType != null) { | 
| -      currentElement = currentType; | 
| -    } else { | 
| -      currentElement = currentLibrary; | 
| -    } | 
| } | 
| } | 
|  | 
| +/** | 
| + * Scrolls the [currentElement] into view. | 
| + */ | 
| void scrollIntoView() { | 
| // TODO(jacobr): there should be a cleaner way to run code that executes | 
| -  // after the UI updates. | 
| +  // after the UI updates. https://github.com/dart-lang/web-ui/issues/188 | 
| html.window.setTimeout(() { | 
| if (currentElement != null) { | 
| for (var e in html.queryAll('[data-id="${currentElement.id}"]')) { | 
| @@ -60,7 +100,10 @@ void scrollIntoView() { | 
| }, 0); | 
| } | 
|  | 
| -onDataModelChanged() { | 
| +/** | 
| + * Invoke every time the data model changes. | 
| + */ | 
| +void _onDataModelChanged() { | 
| _recomputeActiveState(); | 
| scrollIntoView(); | 
| } | 
| @@ -70,10 +113,10 @@ onDataModelChanged() { | 
| * etc. | 
| */ | 
| String kindCssClass(Element element) { | 
| -  String classes = 'kind kind-${normalizedKind(element)}'; | 
| +  String classes = 'kind kind-${_normalizedKind(element)}'; | 
| if (element.isPrivate == true) { | 
| classes = '$classes private'; | 
| -  } else if (element is MethodElementBase && element.isStatic) { | 
| +  } else if (element is MethodLikeElement && element.isStatic) { | 
| classes = '$classes static'; | 
| } | 
|  | 
| @@ -82,19 +125,14 @@ String kindCssClass(Element element) { | 
| classes = '$classes getter'; | 
| } | 
|  | 
| -  if (element is MethodElementBase && element.isSetter) { | 
| +  if (element is MethodLikeElement && element.isSetter) { | 
| classes = '$classes setter'; | 
| } | 
|  | 
| return classes; | 
| } | 
|  | 
| -String normalizedKind(obj) { | 
| -  if (obj is Element) return normalizedKindFromElement(obj); | 
| -  return obj; | 
| -} | 
| - | 
| -String normalizedKindFromElement(Element element) { | 
| +String _normalizedKind(Element element) { | 
| var kind = element.kind; | 
| var name = element.name; | 
| if (kind == 'method' && element.isOperator) { | 
| @@ -108,7 +146,7 @@ String normalizedKindFromElement(Element element) { | 
| } | 
|  | 
| String toUserVisibleKind(Element element) { | 
| -  return KIND_TITLES[normalizedKind(element)]; | 
| +  return KIND_TITLES[_normalizedKind(element)]; | 
| } | 
|  | 
| /** | 
| @@ -116,6 +154,8 @@ String toUserVisibleKind(Element element) { | 
| */ | 
| String permalink(var obj) { | 
| var data = {'id': obj.refId}; | 
| +  // TODO(jacobr): evaluate whether the persistent UI state will stay just a | 
| +  // single reference ID in which case this is overkill. | 
| return "#!${JSON.stringify(data)}"; | 
| } | 
|  | 
| @@ -131,16 +171,22 @@ void loadStateFromUrl() { | 
| // TODO(jacobr): redirect to default page or better yet attempt to fixup. | 
| } | 
| } | 
| -  _activeReferenceId = data['id']; | 
| +  _currentReferenceId = data['id']; | 
| _recomputeActiveState(); | 
| scrollIntoView(); | 
| } | 
|  | 
| Future loadModel() { | 
| -  html.window.on.popState.add((e) { | 
| +  // Note: listen on both popState and hashChange, because IE9 doens't support | 
| +  // history API, and it doesn't work properly on Opera 12. | 
| +  // See http://dartbug.com/5483 | 
| +  updateState(e) { | 
| loadStateFromUrl(); | 
| watchers.dispatch(); | 
| -  }); | 
| +  } | 
| +  html.window.on | 
| +    ..popState.add(updateState) | 
| +    ..hashChange.add(updateState); | 
|  | 
| // Patch in support for [:...:]-style code to the markdown parser. | 
| // TODO(rnystrom): Markdown already has syntax for this. Phase this out? | 
| @@ -150,37 +196,20 @@ Future loadModel() { | 
| md.setImplicitLinkResolver(_resolveNameReference); | 
| var completer = new Completer(); | 
| // TODO(jacobr): shouldn't have to get this from the parent directory. | 
| -  new html.HttpRequest.get('../static/apidoc.json', onSuccess(html.HttpRequest req) { | 
| +  new html.HttpRequest.get('../static/apidoc.json', (req) { | 
| for (var libraryJson in JSON.parse(req.responseText)) { | 
| var library = new LibraryElement(libraryJson, null); | 
| libraries[library.id] = library; | 
| } | 
| -    onDataModelChanged(); | 
| +    _onDataModelChanged(); | 
| completer.complete(true); | 
| }); | 
| return completer.future; | 
| } | 
|  | 
| -/** XXX NOT USED TODO(jacobr) remove. | 
| -class DocComment { | 
| -  final String text; | 
| - | 
| -  /** | 
| -   * Non-null if the comment is inherited from another declaration. | 
| -   */ | 
| -  final inheritedFrom; // InterfaceMirror? | 
| - | 
| -  DocComment(this.text, [this.inheritedFrom = null]) { | 
| -    assert(text != null && !text.trim().isEmpty); | 
| -  } | 
| - | 
| -  SafeHtml get html => new SafeHtml.unsafe(md.markdownToHtml(text)); | 
| - | 
| -  String toString() => text; | 
| -} | 
| - | 
| -*/ | 
| - | 
| +// TODO(jacobr): remove this method and resolve refences to types in the json | 
| +// generation. That way the existing correct logic in Dart2Js can be used rather | 
| +// than this rather busted logic. | 
| /** | 
| * This will be called whenever a doc comment hits a `[name]` in square | 
| * brackets. It will try to figure out what the name refers to and link or | 
| @@ -209,9 +238,11 @@ md.Node _resolveNameReference(String name) { | 
| } | 
|  | 
| // See if it's another member of the current type. | 
| -  // TODO(jacobr): fixme. this is wrong... members are by id now not simple string name... | 
| +  // TODO(jacobr): fixme. this is wrong... members are by id now not simple | 
| +  // string name... | 
| if (currentType != null) { | 
| -    final foundMember = currentType.members[name]; | 
| +    // TODO(jacobr): this should be foundMember = currentType.members[name]; | 
| +    final foundMember = null; | 
| if (foundMember != null) { | 
| return makeLink(permalink(foundMember)); | 
| } | 
| @@ -243,7 +274,8 @@ md.Node _resolveNameReference(String name) { | 
| if (match == null) return null; | 
| var foundType = currentLibrary.classes[match[1]]; | 
| if (foundType == null) return null; | 
| -      var foundMember = foundType.members[match[2]]; | 
| +      // TODO(jacobr): should be foundMember = foundType.members[match[2]]; | 
| +      var foundMember = null; | 
| if (foundMember == null) return null; | 
| return makeLink(permalink(foundMember)); | 
| })(); | 
| @@ -255,7 +287,8 @@ md.Node _resolveNameReference(String name) { | 
| } | 
|  | 
| // See if it's a top-level member in the current library. | 
| -    var foundMember = currentLibrary.members[name]; | 
| +    // TODO(jacobr): should be foundMember = currentLibrary.members[name]; | 
| +    var foundMember = null; | 
| if (foundMember != null) { | 
| return makeLink(permalink(foundMember)); | 
| } | 
|  |