diff --git a/src/aphront/AphrontController.php b/src/aphront/AphrontController.php
index 6d84c46195..a591271270 100644
--- a/src/aphront/AphrontController.php
+++ b/src/aphront/AphrontController.php
@@ -1,67 +1,86 @@
 <?php
 
 /**
  * @group aphront
  */
 abstract class AphrontController extends Phobject {
 
   private $request;
   private $currentApplication;
   private $delegatingController;
 
-
   public function setDelegatingController(
     AphrontController $delegating_controller) {
     $this->delegatingController = $delegating_controller;
     return $this;
   }
 
   public function getDelegatingController() {
     return $this->delegatingController;
   }
 
   public function willBeginExecution() {
     return;
   }
 
   public function willProcessRequest(array $uri_data) {
     return;
   }
 
   public function didProcessRequest($response) {
     return $response;
   }
 
   abstract public function processRequest();
 
   final public function __construct(AphrontRequest $request) {
     $this->request = $request;
   }
 
   final public function getRequest() {
     return $this->request;
   }
 
   final public function delegateToController(AphrontController $controller) {
     $controller->setDelegatingController($this);
 
     $application = $this->getCurrentApplication();
     if ($application) {
       $controller->setCurrentApplication($application);
     }
 
     return $controller->processRequest();
   }
 
   final public function setCurrentApplication(
     PhabricatorApplication $current_application) {
 
     $this->currentApplication = $current_application;
     return $this;
   }
 
   final public function getCurrentApplication() {
     return $this->currentApplication;
   }
 
+  public function getDefaultResourceSource() {
+    throw new Exception(
+      pht(
+        'A Controller must implement getDefaultResourceSource() before you '.
+        'can invoke requireResource() or initBehavior().'));
+  }
+
+  public function requireResource($symbol) {
+    $response = CelerityAPI::getStaticResourceResponse();
+    $response->requireResource($symbol, $this->getDefaultResourceSource());
+    return $this;
+  }
+
+  public function initBehavior($name, $config = array()) {
+    Javelin::initBehavior(
+      $name,
+      $config,
+      $this->getDefaultResourceSource());
+  }
+
 }
diff --git a/src/applications/base/controller/PhabricatorController.php b/src/applications/base/controller/PhabricatorController.php
index 86009fd9fc..4d7d6e3243 100644
--- a/src/applications/base/controller/PhabricatorController.php
+++ b/src/applications/base/controller/PhabricatorController.php
@@ -1,411 +1,416 @@
 <?php
 
 abstract class PhabricatorController extends AphrontController {
 
   private $handles;
 
   public function shouldRequireLogin() {
     return true;
   }
 
   public function shouldRequireAdmin() {
     return false;
   }
 
   public function shouldRequireEnabledUser() {
     return true;
   }
 
   public function shouldAllowPublic() {
     return false;
   }
 
   public function shouldRequireEmailVerification() {
     return PhabricatorUserEmail::isEmailVerificationRequired();
   }
 
   public function willBeginExecution() {
 
     $request = $this->getRequest();
     if ($request->getUser()) {
       // NOTE: Unit tests can set a user explicitly. Normal requests are not
       // permitted to do this.
       PhabricatorTestCase::assertExecutingUnitTests();
       $user = $request->getUser();
     } else {
       $user = new PhabricatorUser();
 
       $phusr = $request->getCookie('phusr');
       $phsid = $request->getCookie('phsid');
 
       if (strlen($phusr) && $phsid) {
         $info = queryfx_one(
           $user->establishConnection('r'),
           'SELECT u.* FROM %T u JOIN %T s ON u.phid = s.userPHID
             AND s.type LIKE %> AND s.sessionKey = %s',
           $user->getTableName(),
           'phabricator_session',
           'web-',
           PhabricatorHash::digest($phsid));
         if ($info) {
           $user->loadFromArray($info);
         }
       }
 
       $request->setUser($user);
     }
 
     $translation = $user->getTranslation();
     if ($translation &&
         $translation != PhabricatorEnv::getEnvConfig('translation.provider')) {
       $translation = newv($translation, array());
       PhutilTranslator::getInstance()
         ->setLanguage($translation->getLanguage())
         ->addTranslations($translation->getTranslations());
     }
 
     $preferences = $user->loadPreferences();
     if (PhabricatorEnv::getEnvConfig('darkconsole.enabled')) {
       $dark_console = PhabricatorUserPreferences::PREFERENCE_DARK_CONSOLE;
       if ($preferences->getPreference($dark_console) ||
          PhabricatorEnv::getEnvConfig('darkconsole.always-on')) {
         $console = new DarkConsoleCore();
         $request->getApplicationConfiguration()->setConsole($console);
       }
     }
 
     if ($this->shouldRequireEnabledUser()) {
       if ($user->isLoggedIn() && !$user->getIsApproved()) {
         $controller = new PhabricatorAuthNeedsApprovalController($request);
         return $this->delegateToController($controller);
       }
       if ($user->getIsDisabled()) {
         $controller = new PhabricatorDisabledUserController($request);
         return $this->delegateToController($controller);
       }
     }
 
     $event = new PhabricatorEvent(
       PhabricatorEventType::TYPE_CONTROLLER_CHECKREQUEST,
       array(
         'request' => $request,
         'controller' => $this,
       ));
     $event->setUser($user);
     PhutilEventEngine::dispatchEvent($event);
     $checker_controller = $event->getValue('controller');
     if ($checker_controller != $this) {
       return $this->delegateToController($checker_controller);
     }
 
     if ($this->shouldRequireLogin()) {
       // This actually means we need either:
       //   - a valid user, or a public controller; and
       //   - permission to see the application.
 
       $auth_class = 'PhabricatorApplicationAuth';
       $auth_application = PhabricatorApplication::getByClass($auth_class);
 
       $allow_public = $this->shouldAllowPublic() &&
                       PhabricatorEnv::getEnvConfig('policy.allow-public');
 
       // If this controller isn't public, and the user isn't logged in, require
       // login.
       if (!$allow_public && !$user->isLoggedIn()) {
         $login_controller = new PhabricatorAuthStartController($request);
         $this->setCurrentApplication($auth_application);
         return $this->delegateToController($login_controller);
       }
 
       if ($user->isLoggedIn()) {
         if ($this->shouldRequireEmailVerification()) {
           if (!$user->getIsEmailVerified()) {
             $controller = new PhabricatorMustVerifyEmailController($request);
             $this->setCurrentApplication($auth_application);
             return $this->delegateToController($controller);
           }
         }
       }
 
       // If the user doesn't have access to the application, don't let them use
       // any of its controllers. We query the application in order to generate
       // a policy exception if the viewer doesn't have permission.
 
       $application = $this->getCurrentApplication();
       if ($application) {
         id(new PhabricatorApplicationQuery())
           ->setViewer($user)
           ->withPHIDs(array($application->getPHID()))
           ->executeOne();
       }
     }
 
     // NOTE: We do this last so that users get a login page instead of a 403
     // if they need to login.
     if ($this->shouldRequireAdmin() && !$user->getIsAdmin()) {
       return new Aphront403Response();
     }
 
   }
 
   public function buildStandardPageView() {
     $view = new PhabricatorStandardPageView();
     $view->setRequest($this->getRequest());
     $view->setController($this);
     return $view;
   }
 
   public function buildStandardPageResponse($view, array $data) {
     $page = $this->buildStandardPageView();
     $page->appendChild($view);
     $response = new AphrontWebpageResponse();
     $response->setContent($page->render());
     return $response;
   }
 
   public function getApplicationURI($path = '') {
     if (!$this->getCurrentApplication()) {
       throw new Exception("No application!");
     }
     return $this->getCurrentApplication()->getApplicationURI($path);
   }
 
   public function buildApplicationPage($view, array $options) {
     $page = $this->buildStandardPageView();
 
     $title = PhabricatorEnv::getEnvConfig('phabricator.serious-business') ?
       'Phabricator' :
       pht('Bacon Ice Cream for Breakfast');
 
     $application = $this->getCurrentApplication();
     $page->setTitle(idx($options, 'title', $title));
     if ($application) {
       $page->setApplicationName($application->getName());
       if ($application->getTitleGlyph()) {
         $page->setGlyph($application->getTitleGlyph());
       }
     }
 
     if (!($view instanceof AphrontSideNavFilterView)) {
       $nav = new AphrontSideNavFilterView();
       $nav->appendChild($view);
       $view = $nav;
     }
 
     $user = $this->getRequest()->getUser();
     $view->setUser($user);
 
     $page->appendChild($view);
 
     $object_phids = idx($options, 'pageObjects', array());
     if ($object_phids) {
       $page->appendPageObjects($object_phids);
       foreach ($object_phids as $object_phid) {
         PhabricatorFeedStoryNotification::updateObjectNotificationViews(
           $user,
           $object_phid);
       }
     }
 
     if (idx($options, 'device')) {
       $page->setDeviceReady(true);
     }
 
     $page->setShowChrome(idx($options, 'chrome', true));
 
     $application_menu = $this->buildApplicationMenu();
     if ($application_menu) {
       $page->setApplicationMenu($application_menu);
     }
 
     $response = new AphrontWebpageResponse();
     return $response->setContent($page->render());
   }
 
   public function didProcessRequest($response) {
     $request = $this->getRequest();
     $response->setRequest($request);
 
     $seen = array();
     while ($response instanceof AphrontProxyResponse) {
 
       $hash = spl_object_hash($response);
       if (isset($seen[$hash])) {
         $seen[] = get_class($response);
         throw new Exception(
           "Cycle while reducing proxy responses: ".
           implode(' -> ', $seen));
       }
       $seen[$hash] = get_class($response);
 
       $response = $response->reduceProxyResponse();
     }
 
     if ($response instanceof AphrontDialogResponse) {
       if (!$request->isAjax()) {
         $view = new PhabricatorStandardPageView();
         $view->setRequest($request);
         $view->setController($this);
         $view->appendChild(phutil_tag(
           'div',
           array('style' => 'padding: 2em 0;'),
           $response->buildResponseString()));
         $page_response = new AphrontWebpageResponse();
         $page_response->setContent($view->render());
         $page_response->setHTTPResponseCode($response->getHTTPResponseCode());
         return $page_response;
       } else {
         $response->getDialog()->setIsStandalone(true);
 
         return id(new AphrontAjaxResponse())
           ->setContent(array(
             'dialog' => $response->buildResponseString(),
           ));
       }
     } else if ($response instanceof AphrontRedirectResponse) {
       if ($request->isAjax()) {
         return id(new AphrontAjaxResponse())
           ->setContent(
             array(
               'redirect' => $response->getURI(),
             ));
       }
     }
     return $response;
   }
 
   protected function getHandle($phid) {
     if (empty($this->handles[$phid])) {
       throw new Exception(
         "Attempting to access handle which wasn't loaded: {$phid}");
     }
     return $this->handles[$phid];
   }
 
   protected function loadHandles(array $phids) {
     $phids = array_filter($phids);
     $this->handles = $this->loadViewerHandles($phids);
     return $this;
   }
 
   protected function getLoadedHandles() {
     return $this->handles;
   }
 
   protected function loadViewerHandles(array $phids) {
     return id(new PhabricatorHandleQuery())
       ->setViewer($this->getRequest()->getUser())
       ->withPHIDs($phids)
       ->execute();
   }
 
 
   /**
    * Render a list of links to handles, identified by PHIDs. The handles must
    * already be loaded.
    *
    * @param   list<phid>  List of PHIDs to render links to.
    * @param   string      Style, one of "\n" (to put each item on its own line)
    *                      or "," (to list items inline, separated by commas).
    * @return  string      Rendered list of handle links.
    */
   protected function renderHandlesForPHIDs(array $phids, $style = "\n") {
     $style_map = array(
       "\n"  => phutil_tag('br'),
       ','   => ', ',
     );
 
     if (empty($style_map[$style])) {
       throw new Exception("Unknown handle list style '{$style}'!");
     }
 
     return implode_selected_handle_links($style_map[$style],
       $this->getLoadedHandles(),
       array_filter($phids));
   }
 
   protected function buildApplicationMenu() {
     return null;
   }
 
   protected function buildApplicationCrumbs() {
 
     $crumbs = array();
 
     $application = $this->getCurrentApplication();
     if ($application) {
       $sprite = $application->getIconName();
       if (!$sprite) {
         $sprite = 'application';
       }
 
       $crumbs[] = id(new PhabricatorCrumbView())
         ->setHref($this->getApplicationURI())
         ->setIcon($sprite);
     }
 
     $view = new PhabricatorCrumbsView();
     foreach ($crumbs as $crumb) {
       $view->addCrumb($crumb);
     }
 
     return $view;
   }
 
   protected function hasApplicationCapability($capability) {
     return PhabricatorPolicyFilter::hasCapability(
       $this->getRequest()->getUser(),
       $this->getCurrentApplication(),
       $capability);
   }
 
   protected function requireApplicationCapability($capability) {
     PhabricatorPolicyFilter::requireCapability(
       $this->getRequest()->getUser(),
       $this->getCurrentApplication(),
       $capability);
   }
 
   protected function explainApplicationCapability(
     $capability,
     $positive_message,
     $negative_message) {
 
     $can_act = $this->hasApplicationCapability($capability);
     if ($can_act) {
       $message = $positive_message;
       $icon_name = 'enable-grey';
     } else {
       $message = $negative_message;
       $icon_name = 'lock';
     }
 
     $icon = id(new PHUIIconView())
       ->setSpriteSheet(PHUIIconView::SPRITE_ICONS)
       ->setSpriteIcon($icon_name);
 
     require_celerity_resource('policy-css');
 
     $phid = $this->getCurrentApplication()->getPHID();
     $explain_uri = "/policy/explain/{$phid}/{$capability}/";
 
     $message = phutil_tag(
       'div',
       array(
         'class' => 'policy-capability-explanation',
       ),
       array(
         $icon,
         javelin_tag(
           'a',
           array(
             'href' => $explain_uri,
             'sigil' => 'workflow',
           ),
           $message),
       ));
 
     return array($can_act, $message);
   }
 
+  public function getDefaultResourceSource() {
+    return 'phabricator';
+  }
+
+
 }
diff --git a/src/applications/differential/controller/DifferentialRevisionViewController.php b/src/applications/differential/controller/DifferentialRevisionViewController.php
index 14d37ffb29..8e15dd7de3 100644
--- a/src/applications/differential/controller/DifferentialRevisionViewController.php
+++ b/src/applications/differential/controller/DifferentialRevisionViewController.php
@@ -1,928 +1,928 @@
 <?php
 
 final class DifferentialRevisionViewController extends DifferentialController {
 
   private $revisionID;
 
   public function shouldAllowPublic() {
     return true;
   }
 
   public function willProcessRequest(array $data) {
     $this->revisionID = $data['id'];
   }
 
   public function processRequest() {
 
     $request = $this->getRequest();
     $user = $request->getUser();
     $viewer_is_anonymous = !$user->isLoggedIn();
 
     $revision = id(new DifferentialRevisionQuery())
       ->withIDs(array($this->revisionID))
       ->setViewer($request->getUser())
       ->needRelationships(true)
       ->needReviewerStatus(true)
       ->needReviewerAuthority(true)
       ->executeOne();
 
     if (!$revision) {
       return new Aphront404Response();
     }
 
     $diffs = id(new DifferentialDiffQuery())
       ->setViewer($request->getUser())
       ->withRevisionIDs(array($this->revisionID))
       ->execute();
     $diffs = array_reverse($diffs, $preserve_keys = true);
 
     if (!$diffs) {
       throw new Exception(
         "This revision has no diffs. Something has gone quite wrong.");
     }
 
     $diff_vs = $request->getInt('vs');
 
     $target_id = $request->getInt('id');
     $target = idx($diffs, $target_id, end($diffs));
 
     $target_manual = $target;
     if (!$target_id) {
       foreach ($diffs as $diff) {
         if ($diff->getCreationMethod() != 'commit') {
           $target_manual = $diff;
         }
       }
     }
 
     if (empty($diffs[$diff_vs])) {
       $diff_vs = null;
     }
 
     $arc_project = $target->loadArcanistProject();
     $repository = ($arc_project ? $arc_project->loadRepository() : null);
 
     list($changesets, $vs_map, $vs_changesets, $rendering_references) =
       $this->loadChangesetsAndVsMap(
         $target,
         idx($diffs, $diff_vs),
         $repository);
 
     if ($request->getExists('download')) {
       return $this->buildRawDiffResponse(
         $revision,
         $changesets,
         $vs_changesets,
         $vs_map,
         $repository);
     }
 
     $props = id(new DifferentialDiffProperty())->loadAllWhere(
       'diffID = %d',
       $target_manual->getID());
     $props = mpull($props, 'getData', 'getName');
 
     $aux_fields = $this->loadAuxiliaryFields($revision);
 
     $comments = $revision->loadComments();
 
     $all_changesets = $changesets;
     $inlines = $this->loadInlineComments(
       $revision,
       $all_changesets);
 
     $object_phids = array_merge(
       $revision->getReviewers(),
       $revision->getCCPHIDs(),
       $revision->loadCommitPHIDs(),
       array(
         $revision->getAuthorPHID(),
         $user->getPHID(),
       ),
       mpull($comments, 'getAuthorPHID'));
 
     foreach ($comments as $comment) {
       foreach ($comment->getRequiredHandlePHIDs() as $phid) {
         $object_phids[] = $phid;
       }
     }
 
     foreach ($revision->getAttached() as $type => $phids) {
       foreach ($phids as $phid => $info) {
         $object_phids[] = $phid;
       }
     }
 
     $aux_phids = array();
     foreach ($aux_fields as $key => $aux_field) {
       $aux_field->setDiff($target);
       $aux_field->setManualDiff($target_manual);
       $aux_field->setDiffProperties($props);
       $aux_phids[$key] = $aux_field->getRequiredHandlePHIDsForRevisionView();
     }
     $object_phids = array_merge($object_phids, array_mergev($aux_phids));
     $object_phids = array_unique($object_phids);
 
     $handles = $this->loadViewerHandles($object_phids);
 
     foreach ($aux_fields as $key => $aux_field) {
       // Make sure each field only has access to handles it specifically
       // requested, not all handles. Otherwise you can get a field which works
       // only in the presence of other fields.
       $aux_field->setHandles(array_select_keys($handles, $aux_phids[$key]));
     }
 
     $reviewer_warning = null;
     if ($revision->getStatus() ==
         ArcanistDifferentialRevisionStatus::NEEDS_REVIEW) {
       $has_live_reviewer = false;
       foreach ($revision->getReviewers() as $reviewer) {
         if (!$handles[$reviewer]->isDisabled()) {
           $has_live_reviewer = true;
           break;
         }
       }
       if (!$has_live_reviewer) {
         $reviewer_warning = new AphrontErrorView();
         $reviewer_warning->setSeverity(AphrontErrorView::SEVERITY_WARNING);
         $reviewer_warning->setTitle(pht('No Active Reviewers'));
         if ($revision->getReviewers()) {
           $reviewer_warning->appendChild(
             phutil_tag(
               'p',
               array(),
               pht('All specified reviewers are disabled and this revision '.
                   'needs review. You may want to add some new reviewers.')));
         } else {
           $reviewer_warning->appendChild(
             phutil_tag(
               'p',
               array(),
               pht('This revision has no specified reviewers and needs '.
                   'review. You may want to add some reviewers.')));
         }
       }
     }
 
     $request_uri = $request->getRequestURI();
 
     $limit = 100;
     $large = $request->getStr('large');
     if (count($changesets) > $limit && !$large) {
       $count = count($changesets);
       $warning = new AphrontErrorView();
       $warning->setTitle('Very Large Diff');
       $warning->setSeverity(AphrontErrorView::SEVERITY_WARNING);
       $warning->appendChild(hsprintf(
         '%s <strong>%s</strong>',
         pht(
           'This diff is very large and affects %s files. Load each file '.
             'individually.',
           new PhutilNumber($count)),
         phutil_tag(
           'a',
           array(
             'href' => $request_uri
               ->alter('large', 'true')
               ->setFragment('toc'),
           ),
           pht('Show All Files Inline'))));
       $warning = $warning->render();
 
       $my_inlines = id(new DifferentialInlineCommentQuery())
         ->withDraftComments($user->getPHID(), $this->revisionID)
         ->execute();
 
       $visible_changesets = array();
       foreach ($inlines + $my_inlines as $inline) {
         $changeset_id = $inline->getChangesetID();
         if (isset($changesets[$changeset_id])) {
           $visible_changesets[$changeset_id] = $changesets[$changeset_id];
         }
       }
 
       if (!empty($props['arc:lint'])) {
         $changeset_paths = mpull($changesets, null, 'getFilename');
         foreach ($props['arc:lint'] as $lint) {
           $changeset = idx($changeset_paths, $lint['path']);
           if ($changeset) {
             $visible_changesets[$changeset->getID()] = $changeset;
           }
         }
       }
 
     } else {
       $warning = null;
       $visible_changesets = $changesets;
     }
 
     $revision_detail = id(new DifferentialRevisionDetailView())
       ->setUser($user)
       ->setRevision($revision)
       ->setDiff(end($diffs))
       ->setAuxiliaryFields($aux_fields)
       ->setURI($request->getRequestURI());
 
     $actions = $this->getRevisionActions($revision);
 
     $custom_renderer_class = PhabricatorEnv::getEnvConfig(
       'differential.revision-custom-detail-renderer');
     if ($custom_renderer_class) {
 
       // TODO: build a better version of the action links and deprecate the
       // whole DifferentialRevisionDetailRenderer class.
       $custom_renderer = newv($custom_renderer_class, array());
       $custom_renderer->setUser($user);
       $custom_renderer->setDiff($target);
       if ($diff_vs) {
         $custom_renderer->setVSDiff($diffs[$diff_vs]);
       }
       $actions = array_merge(
         $actions,
         $custom_renderer->generateActionLinks($revision, $target_manual));
     }
 
     $whitespace = $request->getStr(
       'whitespace',
       DifferentialChangesetParser::WHITESPACE_IGNORE_ALL);
 
     if ($arc_project) {
       list($symbol_indexes, $project_phids) = $this->buildSymbolIndexes(
         $arc_project,
         $visible_changesets);
     } else {
       $symbol_indexes = array();
       $project_phids = null;
     }
 
     $revision_detail->setActions($actions);
     $revision_detail->setUser($user);
 
     $comment_view = new DifferentialRevisionCommentListView();
     $comment_view->setComments($comments);
     $comment_view->setHandles($handles);
     $comment_view->setInlineComments($inlines);
     $comment_view->setChangesets($all_changesets);
     $comment_view->setUser($user);
     $comment_view->setTargetDiff($target);
     $comment_view->setVersusDiffID($diff_vs);
 
     if ($arc_project) {
       Javelin::initBehavior(
         'repository-crossreference',
         array(
           'section' => $comment_view->getID(),
           'projects' => $project_phids,
         ));
     }
 
     $changeset_view = new DifferentialChangesetListView();
     $changeset_view->setChangesets($changesets);
     $changeset_view->setVisibleChangesets($visible_changesets);
 
     if (!$viewer_is_anonymous) {
       $changeset_view->setInlineCommentControllerURI(
         '/differential/comment/inline/edit/'.$revision->getID().'/');
     }
 
     $changeset_view->setStandaloneURI('/differential/changeset/');
     $changeset_view->setRawFileURIs(
       '/differential/changeset/?view=old',
       '/differential/changeset/?view=new');
 
     $changeset_view->setUser($user);
     $changeset_view->setDiff($target);
     $changeset_view->setRenderingReferences($rendering_references);
     $changeset_view->setVsMap($vs_map);
     $changeset_view->setWhitespace($whitespace);
     if ($repository) {
       $changeset_view->setRepository($repository);
     }
     $changeset_view->setSymbolIndexes($symbol_indexes);
     $changeset_view->setTitle('Diff '.$target->getID());
 
     $diff_history = new DifferentialRevisionUpdateHistoryView();
     $diff_history->setDiffs($diffs);
     $diff_history->setSelectedVersusDiffID($diff_vs);
     $diff_history->setSelectedDiffID($target->getID());
     $diff_history->setSelectedWhitespace($whitespace);
     $diff_history->setUser($user);
 
     $local_view = new DifferentialLocalCommitsView();
     $local_view->setUser($user);
     $local_view->setLocalCommits(idx($props, 'local:commits'));
 
     if ($repository) {
       $other_revisions = $this->loadOtherRevisions(
         $changesets,
         $target,
         $repository);
     } else {
       $other_revisions = array();
     }
 
     $other_view = null;
     if ($other_revisions) {
       $other_view = $this->renderOtherRevisions($other_revisions);
     }
 
     $toc_view = new DifferentialDiffTableOfContentsView();
     $toc_view->setChangesets($changesets);
     $toc_view->setVisibleChangesets($visible_changesets);
     $toc_view->setRenderingReferences($rendering_references);
     $toc_view->setUnitTestData(idx($props, 'arc:unit', array()));
     if ($repository) {
       $toc_view->setRepository($repository);
     }
     $toc_view->setDiff($target);
     $toc_view->setUser($user);
     $toc_view->setRevisionID($revision->getID());
     $toc_view->setWhitespace($whitespace);
 
     $comment_form = null;
     if (!$viewer_is_anonymous) {
       $draft = id(new PhabricatorDraft())->loadOneWhere(
         'authorPHID = %s AND draftKey = %s',
         $user->getPHID(),
         'differential-comment-'.$revision->getID());
 
       $reviewers = array();
       $ccs = array();
       if ($draft) {
         $reviewers = idx($draft->getMetadata(), 'reviewers', array());
         $ccs = idx($draft->getMetadata(), 'ccs', array());
         if ($reviewers || $ccs) {
           $handles = $this->loadViewerHandles(array_merge($reviewers, $ccs));
           $reviewers = array_select_keys($handles, $reviewers);
           $ccs = array_select_keys($handles, $ccs);
         }
       }
 
       $comment_form = new DifferentialAddCommentView();
       $comment_form->setRevision($revision);
       $comment_form->setAuxFields($aux_fields);
       $comment_form->setActions($this->getRevisionCommentActions($revision));
       $comment_form->setActionURI('/differential/comment/save/');
       $comment_form->setUser($user);
       $comment_form->setDraft($draft);
       $comment_form->setReviewers(mpull($reviewers, 'getFullName', 'getPHID'));
       $comment_form->setCCs(mpull($ccs, 'getFullName', 'getPHID'));
 
       // TODO: This just makes the "Z" key work. Generalize this and remove
       // it at some point.
       $comment_form = phutil_tag(
         'div',
         array(
           'class' => 'differential-add-comment-panel',
         ),
         $comment_form);
     }
 
     $pane_id = celerity_generate_unique_node_id();
     Javelin::initBehavior(
       'differential-keyboard-navigation',
       array(
         'haunt' => $pane_id,
       ));
     Javelin::initBehavior('differential-user-select');
 
     $page_pane = id(new DifferentialPrimaryPaneView())
       ->setID($pane_id)
       ->appendChild(array(
         $comment_view,
         $diff_history,
         $warning,
         $local_view,
         $toc_view,
         $other_view,
         $changeset_view,
       ));
     if ($comment_form) {
       $page_pane->appendChild($comment_form);
     } else {
       // TODO: For now, just use this to get "Login to Comment".
       $page_pane->appendChild(
         id(new PhabricatorApplicationTransactionCommentView())
           ->setUser($user)
           ->setRequestURI($request->getRequestURI()));
     }
 
 
     $object_id = 'D'.$revision->getID();
 
     $top_anchor = id(new PhabricatorAnchorView())
       ->setAnchorName('top')
       ->setNavigationMarker(true);
 
     $content = array(
       $reviewer_warning,
       $top_anchor,
       $revision_detail,
       $page_pane,
     );
 
     $crumbs = $this->buildApplicationCrumbs();
     $crumbs->addTextCrumb($object_id, '/'.$object_id);
 
     $prefs = $user->loadPreferences();
 
     $pref_filetree = PhabricatorUserPreferences::PREFERENCE_DIFF_FILETREE;
     if ($prefs->getPreference($pref_filetree)) {
       $collapsed = $prefs->getPreference(
         PhabricatorUserPreferences::PREFERENCE_NAV_COLLAPSED,
         false);
 
       $nav = id(new DifferentialChangesetFileTreeSideNavBuilder())
         ->setAnchorName('top')
         ->setTitle('D'.$revision->getID())
         ->setBaseURI(new PhutilURI('/D'.$revision->getID()))
         ->setCollapsed((bool)$collapsed)
         ->build($changesets);
       $nav->appendChild($content);
       $nav->setCrumbs($crumbs);
       $content = $nav;
     } else {
       array_unshift($content, $crumbs);
     }
 
     return $this->buildApplicationPage(
       $content,
       array(
         'title' => $object_id.' '.$revision->getTitle(),
         'pageObjects' => array($revision->getPHID()),
       ));
   }
 
   private function getRevisionActions(DifferentialRevision $revision) {
     $user = $this->getRequest()->getUser();
     $viewer_phid = $user->getPHID();
     $viewer_is_owner = ($revision->getAuthorPHID() == $viewer_phid);
     $viewer_is_reviewer = in_array($viewer_phid, $revision->getReviewers());
     $viewer_is_cc = in_array($viewer_phid, $revision->getCCPHIDs());
     $logged_in = $this->getRequest()->getUser()->isLoggedIn();
     $status = $revision->getStatus();
     $revision_id = $revision->getID();
     $revision_phid = $revision->getPHID();
 
     $links = array();
 
     $can_edit = PhabricatorPolicyFilter::hasCapability(
       $user,
       $revision,
       PhabricatorPolicyCapability::CAN_EDIT);
 
     $links[] = array(
       'icon'  =>  'edit',
       'href'  => "/differential/revision/edit/{$revision_id}/",
       'name'  => pht('Edit Revision'),
       'disabled' => !$can_edit,
       'sigil' => $can_edit ? null : 'workflow',
     );
 
     if (!$viewer_is_owner && !$viewer_is_reviewer) {
       $action = $viewer_is_cc ? 'rem' : 'add';
       $links[] = array(
         'icon'    => $viewer_is_cc ? 'disable' : 'check',
         'href'    => "/differential/subscribe/{$action}/{$revision_id}/",
         'name'    => $viewer_is_cc ? pht('Unsubscribe') : pht('Subscribe'),
         'instant' => $logged_in,
         'disabled' => !$logged_in,
         'sigil' => $can_edit ? null : 'workflow',
       );
     } else {
       $links[] = array(
         'icon'     => 'enable',
         'name'     => pht('Automatically Subscribed'),
         'disabled' => true,
       );
     }
 
-    require_celerity_resource('phabricator-object-selector-css');
-    require_celerity_resource('javelin-behavior-phabricator-object-selector');
+    $this->requireResource('phabricator-object-selector-css');
+    $this->requireResource('javelin-behavior-phabricator-object-selector');
 
     $links[] = array(
       'icon'  => 'link',
       'name'  => pht('Edit Dependencies'),
       'href'  => "/search/attach/{$revision_phid}/DREV/dependencies/",
       'sigil' => 'workflow',
       'disabled' => !$can_edit,
     );
 
     $maniphest = 'PhabricatorApplicationManiphest';
     if (PhabricatorApplication::isClassInstalled($maniphest)) {
       $links[] = array(
         'icon'  => 'attach',
         'name'  => pht('Edit Maniphest Tasks'),
         'href'  => "/search/attach/{$revision_phid}/TASK/",
         'sigil' => 'workflow',
         'disabled' => !$can_edit,
       );
     }
 
     $request_uri = $this->getRequest()->getRequestURI();
     $links[] = array(
       'icon'  => 'download',
       'name'  => pht('Download Raw Diff'),
       'href'  => $request_uri->alter('download', 'true')
     );
 
     return $links;
   }
 
   private function getRevisionCommentActions(DifferentialRevision $revision) {
 
     $actions = array(
       DifferentialAction::ACTION_COMMENT => true,
     );
 
     $viewer = $this->getRequest()->getUser();
     $viewer_phid = $viewer->getPHID();
     $viewer_is_owner = ($viewer_phid == $revision->getAuthorPHID());
     $viewer_is_reviewer = in_array($viewer_phid, $revision->getReviewers());
     $status = $revision->getStatus();
 
     $viewer_has_accepted = false;
     $viewer_has_rejected = false;
     $status_accepted = DifferentialReviewerStatus::STATUS_ACCEPTED;
     $status_rejected = DifferentialReviewerStatus::STATUS_REJECTED;
     foreach ($revision->getReviewerStatus() as $reviewer) {
       if ($reviewer->getReviewerPHID() == $viewer_phid) {
         if ($reviewer->getStatus() == $status_accepted) {
           $viewer_has_accepted = true;
         }
         if ($reviewer->getStatus() == $status_rejected) {
           $viewer_has_rejected = true;
         }
         break;
       }
     }
 
     $allow_self_accept = PhabricatorEnv::getEnvConfig(
       'differential.allow-self-accept');
     $always_allow_close = PhabricatorEnv::getEnvConfig(
       'differential.always-allow-close');
     $allow_reopen = PhabricatorEnv::getEnvConfig(
       'differential.allow-reopen');
 
     if ($viewer_is_owner) {
       switch ($status) {
         case ArcanistDifferentialRevisionStatus::NEEDS_REVIEW:
           $actions[DifferentialAction::ACTION_ACCEPT] = $allow_self_accept;
           $actions[DifferentialAction::ACTION_ABANDON] = true;
           $actions[DifferentialAction::ACTION_RETHINK] = true;
           break;
         case ArcanistDifferentialRevisionStatus::NEEDS_REVISION:
           $actions[DifferentialAction::ACTION_ACCEPT] = $allow_self_accept;
           $actions[DifferentialAction::ACTION_ABANDON] = true;
           $actions[DifferentialAction::ACTION_REQUEST] = true;
           break;
         case ArcanistDifferentialRevisionStatus::ACCEPTED:
           $actions[DifferentialAction::ACTION_ABANDON] = true;
           $actions[DifferentialAction::ACTION_REQUEST] = true;
           $actions[DifferentialAction::ACTION_RETHINK] = true;
           $actions[DifferentialAction::ACTION_CLOSE] = true;
           break;
         case ArcanistDifferentialRevisionStatus::CLOSED:
           break;
         case ArcanistDifferentialRevisionStatus::ABANDONED:
           $actions[DifferentialAction::ACTION_RECLAIM] = true;
           break;
       }
     } else {
       switch ($status) {
         case ArcanistDifferentialRevisionStatus::NEEDS_REVIEW:
           $actions[DifferentialAction::ACTION_ACCEPT] = true;
           $actions[DifferentialAction::ACTION_REJECT] = true;
           $actions[DifferentialAction::ACTION_RESIGN] = $viewer_is_reviewer;
           break;
         case ArcanistDifferentialRevisionStatus::NEEDS_REVISION:
           $actions[DifferentialAction::ACTION_ACCEPT] = true;
           $actions[DifferentialAction::ACTION_REJECT] = !$viewer_has_rejected;
           $actions[DifferentialAction::ACTION_RESIGN] = $viewer_is_reviewer;
           break;
         case ArcanistDifferentialRevisionStatus::ACCEPTED:
           $actions[DifferentialAction::ACTION_ACCEPT] = !$viewer_has_accepted;
           $actions[DifferentialAction::ACTION_REJECT] = true;
           $actions[DifferentialAction::ACTION_RESIGN] = $viewer_is_reviewer;
           break;
         case ArcanistDifferentialRevisionStatus::CLOSED:
         case ArcanistDifferentialRevisionStatus::ABANDONED:
           break;
       }
       if ($status != ArcanistDifferentialRevisionStatus::CLOSED) {
         $actions[DifferentialAction::ACTION_CLAIM] = true;
         $actions[DifferentialAction::ACTION_CLOSE] = $always_allow_close;
       }
     }
 
     $actions[DifferentialAction::ACTION_ADDREVIEWERS] = true;
     $actions[DifferentialAction::ACTION_ADDCCS] = true;
     $actions[DifferentialAction::ACTION_REOPEN] = $allow_reopen &&
       ($status == ArcanistDifferentialRevisionStatus::CLOSED);
 
     $actions = array_keys(array_filter($actions));
     $actions_dict = array();
     foreach ($actions as $action) {
       $actions_dict[$action] = DifferentialAction::getActionVerb($action);
     }
 
     return $actions_dict;
   }
 
   private function loadInlineComments(
     DifferentialRevision $revision,
     array &$changesets) {
     assert_instances_of($changesets, 'DifferentialChangeset');
 
     $inline_comments = array();
 
     $inline_comments = id(new DifferentialInlineCommentQuery())
       ->withRevisionIDs(array($revision->getID()))
       ->withNotDraft(true)
       ->execute();
 
     $load_changesets = array();
     foreach ($inline_comments as $inline) {
       $changeset_id = $inline->getChangesetID();
       if (isset($changesets[$changeset_id])) {
         continue;
       }
       $load_changesets[$changeset_id] = true;
     }
 
     $more_changesets = array();
     if ($load_changesets) {
       $changeset_ids = array_keys($load_changesets);
       $more_changesets += id(new DifferentialChangeset())
         ->loadAllWhere(
           'id IN (%Ld)',
           $changeset_ids);
     }
 
     if ($more_changesets) {
       $changesets += $more_changesets;
       $changesets = msort($changesets, 'getSortKey');
     }
 
     return $inline_comments;
   }
 
   private function loadChangesetsAndVsMap(
     DifferentialDiff $target,
     DifferentialDiff $diff_vs = null,
     PhabricatorRepository $repository = null) {
 
     $load_ids = array();
     if ($diff_vs) {
       $load_ids[] = $diff_vs->getID();
     }
     $load_ids[] = $target->getID();
 
     $raw_changesets = id(new DifferentialChangeset())
       ->loadAllWhere(
         'diffID IN (%Ld)',
         $load_ids);
     $changeset_groups = mgroup($raw_changesets, 'getDiffID');
 
     $changesets = idx($changeset_groups, $target->getID(), array());
     $changesets = mpull($changesets, null, 'getID');
 
     $refs          = array();
     $vs_map        = array();
     $vs_changesets = array();
     if ($diff_vs) {
       $vs_id                  = $diff_vs->getID();
       $vs_changesets_path_map = array();
       foreach (idx($changeset_groups, $vs_id, array()) as $changeset) {
         $path = $changeset->getAbsoluteRepositoryPath($repository, $diff_vs);
         $vs_changesets_path_map[$path] = $changeset;
         $vs_changesets[$changeset->getID()] = $changeset;
       }
       foreach ($changesets as $key => $changeset) {
         $path = $changeset->getAbsoluteRepositoryPath($repository, $target);
         if (isset($vs_changesets_path_map[$path])) {
           $vs_map[$changeset->getID()] =
             $vs_changesets_path_map[$path]->getID();
           $refs[$changeset->getID()] =
             $changeset->getID().'/'.$vs_changesets_path_map[$path]->getID();
           unset($vs_changesets_path_map[$path]);
         } else {
           $refs[$changeset->getID()] = $changeset->getID();
         }
       }
       foreach ($vs_changesets_path_map as $path => $changeset) {
         $changesets[$changeset->getID()] = $changeset;
         $vs_map[$changeset->getID()]     = -1;
         $refs[$changeset->getID()]       = $changeset->getID().'/-1';
       }
     } else {
       foreach ($changesets as $changeset) {
         $refs[$changeset->getID()] = $changeset->getID();
       }
     }
 
     $changesets = msort($changesets, 'getSortKey');
 
     return array($changesets, $vs_map, $vs_changesets, $refs);
   }
 
   private function loadAuxiliaryFields(DifferentialRevision $revision) {
 
     $aux_fields = DifferentialFieldSelector::newSelector()
       ->getFieldSpecifications();
     foreach ($aux_fields as $key => $aux_field) {
       if (!$aux_field->shouldAppearOnRevisionView()) {
         unset($aux_fields[$key]);
       } else {
         $aux_field->setUser($this->getRequest()->getUser());
       }
     }
 
     $aux_fields = DifferentialAuxiliaryField::loadFromStorage(
       $revision,
       $aux_fields);
 
     return $aux_fields;
   }
 
   private function buildSymbolIndexes(
     PhabricatorRepositoryArcanistProject $arc_project,
     array $visible_changesets) {
     assert_instances_of($visible_changesets, 'DifferentialChangeset');
 
     $engine = PhabricatorSyntaxHighlighter::newEngine();
 
     $langs = $arc_project->getSymbolIndexLanguages();
     if (!$langs) {
       return array(array(), array());
     }
 
     $symbol_indexes = array();
 
     $project_phids = array_merge(
       array($arc_project->getPHID()),
       nonempty($arc_project->getSymbolIndexProjects(), array()));
 
     $indexed_langs = array_fill_keys($langs, true);
     foreach ($visible_changesets as $key => $changeset) {
       $lang = $engine->getLanguageFromFilename($changeset->getFilename());
       if (isset($indexed_langs[$lang])) {
         $symbol_indexes[$key] = array(
           'lang'      => $lang,
           'projects'  => $project_phids,
         );
       }
     }
 
     return array($symbol_indexes, $project_phids);
   }
 
   private function loadOtherRevisions(
     array $changesets,
     DifferentialDiff $target,
     PhabricatorRepository $repository) {
     assert_instances_of($changesets, 'DifferentialChangeset');
 
     $paths = array();
     foreach ($changesets as $changeset) {
       $paths[] = $changeset->getAbsoluteRepositoryPath(
         $repository,
         $target);
     }
 
     if (!$paths) {
       return array();
     }
 
     $path_map = id(new DiffusionPathIDQuery($paths))->loadPathIDs();
 
     if (!$path_map) {
       return array();
     }
 
     $query = id(new DifferentialRevisionQuery())
       ->setViewer($this->getRequest()->getUser())
       ->withStatus(DifferentialRevisionQuery::STATUS_OPEN)
       ->setOrder(DifferentialRevisionQuery::ORDER_PATH_MODIFIED)
       ->setLimit(10)
       ->needRelationships(true);
 
     foreach ($path_map as $path => $path_id) {
       $query->withPath($repository->getID(), $path_id);
     }
 
     $results = $query->execute();
 
     // Strip out *this* revision.
     foreach ($results as $key => $result) {
       if ($result->getID() == $this->revisionID) {
         unset($results[$key]);
       }
     }
 
     return $results;
   }
 
   private function renderOtherRevisions(array $revisions) {
     assert_instances_of($revisions, 'DifferentialRevision');
 
     $user = $this->getRequest()->getUser();
 
     $view = id(new DifferentialRevisionListView())
       ->setRevisions($revisions)
       ->setFields(DifferentialRevisionListView::getDefaultFields($user))
       ->setUser($user)
       ->loadAssets();
 
     $phids = $view->getRequiredHandlePHIDs();
     $handles = $this->loadViewerHandles($phids);
     $view->setHandles($handles);
 
     return id(new PHUIObjectBoxView())
       ->setHeaderText(pht('Open Revisions Affecting These Files'))
       ->appendChild($view);
   }
 
 
   /**
    * Note this code is somewhat similar to the buildPatch method in
    * @{class:DifferentialReviewRequestMail}.
    *
    * @return @{class:AphrontRedirectResponse}
    */
   private function buildRawDiffResponse(
     DifferentialRevision $revision,
     array $changesets,
     array $vs_changesets,
     array $vs_map,
     PhabricatorRepository $repository = null) {
 
     assert_instances_of($changesets,    'DifferentialChangeset');
     assert_instances_of($vs_changesets, 'DifferentialChangeset');
 
     $viewer = $this->getRequest()->getUser();
 
     foreach ($changesets as $changeset) {
       $changeset->attachHunks($changeset->loadHunks());
     }
 
     $diff = new DifferentialDiff();
     $diff->attachChangesets($changesets);
     $raw_changes = $diff->buildChangesList();
     $changes = array();
     foreach ($raw_changes as $changedict) {
       $changes[] = ArcanistDiffChange::newFromDictionary($changedict);
     }
 
     $loader = id(new PhabricatorFileBundleLoader())
       ->setViewer($viewer);
 
     $bundle = ArcanistBundle::newFromChanges($changes);
     $bundle->setLoadFileDataCallback(array($loader, 'loadFileData'));
 
     $vcs = $repository ? $repository->getVersionControlSystem() : null;
     switch ($vcs) {
       case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
       case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
         $raw_diff = $bundle->toGitPatch();
         break;
       case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
       default:
         $raw_diff = $bundle->toUnifiedDiff();
         break;
     }
 
     $request_uri = $this->getRequest()->getRequestURI();
 
     // this ends up being something like
     //   D123.diff
     // or the verbose
     //   D123.vs123.id123.whitespaceignore-all.diff
     // lame but nice to include these options
     $file_name = ltrim($request_uri->getPath(), '/').'.';
     foreach ($request_uri->getQueryParams() as $key => $value) {
       if ($key == 'download') {
         continue;
       }
       $file_name .= $key.$value.'.';
     }
     $file_name .= 'diff';
 
     $file = PhabricatorFile::buildFromFileDataOrHash(
       $raw_diff,
       array(
         'name' => $file_name,
         'ttl' => (60 * 60 * 24),
         'viewPolicy' => PhabricatorPolicies::POLICY_NOONE,
       ));
 
     $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
       $file->attachToObject(
         $this->getRequest()->getUser(),
         $revision->getPHID());
     unset($unguarded);
 
     return id(new AphrontRedirectResponse())->setURI($file->getBestURI());
 
   }
 }
diff --git a/src/applications/differential/view/DifferentialAddCommentView.php b/src/applications/differential/view/DifferentialAddCommentView.php
index 822a85d6ac..a95ee9254f 100644
--- a/src/applications/differential/view/DifferentialAddCommentView.php
+++ b/src/applications/differential/view/DifferentialAddCommentView.php
@@ -1,205 +1,205 @@
 <?php
 
 final class DifferentialAddCommentView extends AphrontView {
 
   private $revision;
   private $actions;
   private $actionURI;
   private $draft;
   private $auxFields;
   private $reviewers = array();
   private $ccs = array();
 
   public function setRevision($revision) {
     $this->revision = $revision;
     return $this;
   }
 
   public function setAuxFields(array $aux_fields) {
     assert_instances_of($aux_fields, 'DifferentialFieldSpecification');
     $this->auxFields = $aux_fields;
     return $this;
   }
 
   public function setActions(array $actions) {
     $this->actions = $actions;
     return $this;
   }
 
   public function setActionURI($uri) {
     $this->actionURI = $uri;
     return $this;
   }
 
   public function setDraft(PhabricatorDraft $draft = null) {
     $this->draft = $draft;
     return $this;
   }
 
   public function setReviewers(array $names) {
     $this->reviewers = $names;
     return $this;
   }
 
   public function setCCs(array $names) {
     $this->ccs = $names;
     return $this;
   }
 
   public function render() {
 
-    require_celerity_resource('differential-revision-add-comment-css');
+    $this->requireResource('differential-revision-add-comment-css');
 
     $is_serious = PhabricatorEnv::getEnvConfig('phabricator.serious-business');
 
     $revision = $this->revision;
 
     $action = null;
     if ($this->draft) {
       $action = idx($this->draft->getMetadata(), 'action');
     }
 
     $enable_reviewers = DifferentialAction::allowReviewers($action);
     $enable_ccs = ($action == DifferentialAction::ACTION_ADDCCS);
     $add_reviewers_labels = array(
       'add_reviewers' => pht('Add Reviewers'),
       'request_review' => pht('Add Reviewers'),
       'resign' => pht('Suggest Reviewers'),
     );
 
     $form = new AphrontFormView();
     $form
       ->setWorkflow(true)
       ->setUser($this->user)
       ->setAction($this->actionURI)
       ->addHiddenInput('revision_id', $revision->getID())
       ->appendChild(
         id(new AphrontFormSelectControl())
           ->setLabel(pht('Action'))
           ->setName('action')
           ->setValue($action)
           ->setID('comment-action')
           ->setOptions($this->actions))
       ->appendChild(
         id(new AphrontFormTokenizerControl())
           ->setLabel($enable_reviewers ? $add_reviewers_labels[$action] :
             $add_reviewers_labels['add_reviewers'])
           ->setName('reviewers')
           ->setControlID('add-reviewers')
           ->setControlStyle($enable_reviewers ? null : 'display: none')
           ->setID('add-reviewers-tokenizer')
           ->setDisableBehavior(true))
       ->appendChild(
         id(new AphrontFormTokenizerControl())
           ->setLabel(pht('Add CCs'))
           ->setName('ccs')
           ->setControlID('add-ccs')
           ->setControlStyle($enable_ccs ? null : 'display: none')
           ->setID('add-ccs-tokenizer')
           ->setDisableBehavior(true))
       ->appendChild(
         id(new PhabricatorRemarkupControl())
           ->setName('comment')
           ->setID('comment-content')
           ->setLabel(pht('Comment'))
           ->setValue($this->draft ? $this->draft->getDraft() : null)
           ->setUser($this->user))
       ->appendChild(
         id(new AphrontFormSubmitControl())
           ->setValue($is_serious ? pht('Submit') : pht('Clowncopterize')));
 
     Javelin::initBehavior(
       'differential-add-reviewers-and-ccs',
       array(
         'dynamic' => array(
           'add-reviewers-tokenizer' => array(
             'actions' => array(
               'request_review' => 1,
               'add_reviewers' => 1,
               'resign' => 1,
             ),
             'src' => '/typeahead/common/usersorprojects/',
             'value' => $this->reviewers,
             'row' => 'add-reviewers',
             'ondemand' => PhabricatorEnv::getEnvConfig('tokenizer.ondemand'),
             'labels' => $add_reviewers_labels,
             'placeholder' => pht('Type a user or project name...'),
           ),
           'add-ccs-tokenizer' => array(
             'actions' => array('add_ccs' => 1),
             'src' => '/typeahead/common/mailable/',
             'value' => $this->ccs,
             'row' => 'add-ccs',
             'ondemand' => PhabricatorEnv::getEnvConfig('tokenizer.ondemand'),
             'placeholder' => pht('Type a user or mailing list...'),
           ),
         ),
         'select' => 'comment-action',
       ));
 
     $diff = $revision->loadActiveDiff();
     $warnings = mpull($this->auxFields, 'renderWarningBoxForRevisionAccept');
 
     Javelin::initBehavior(
       'differential-accept-with-errors',
       array(
         'select' => 'comment-action',
         'warnings' => 'warnings',
       ));
 
     $rev_id = $revision->getID();
 
     Javelin::initBehavior(
       'differential-feedback-preview',
       array(
         'uri'       => '/differential/comment/preview/'.$rev_id.'/',
         'preview'   => 'comment-preview',
         'action'    => 'comment-action',
         'content'   => 'comment-content',
         'previewTokenizers' => array(
           'reviewers' => 'add-reviewers-tokenizer',
           'ccs'       => 'add-ccs-tokenizer',
         ),
 
         'inlineuri' => '/differential/comment/inline/preview/'.$rev_id.'/',
         'inline'    => 'inline-comment-preview',
       ));
 
     $warning_container = array();
     foreach ($warnings as $warning) {
       if ($warning) {
         $warning_container[] = $warning->render();
       }
     }
 
     $header = id(new PHUIHeaderView())
       ->setHeader($is_serious ? pht('Add Comment') : pht('Leap Into Action'));
 
     $anchor = id(new PhabricatorAnchorView())
         ->setAnchorName('comment')
         ->setNavigationMarker(true);
 
     $warn = phutil_tag('div', array('id' => 'warnings'), $warning_container);
 
     $loading = phutil_tag(
       'span',
       array('class' => 'aphront-panel-preview-loading-text'),
       pht('Loading comment preview...'));
 
     $preview = phutil_tag_div(
       'aphront-panel-preview aphront-panel-flush',
       array(
         phutil_tag('div', array('id' => 'comment-preview'), $loading),
         phutil_tag('div', array('id' => 'inline-comment-preview')),
       ));
 
 
     $comment_box = id(new PHUIObjectBoxView())
       ->setHeader($header)
       ->appendChild($anchor)
       ->appendChild($warn)
       ->appendChild($form);
 
     return array($comment_box, $preview);
   }
 }
diff --git a/src/applications/differential/view/DifferentialChangesetDetailView.php b/src/applications/differential/view/DifferentialChangesetDetailView.php
index 029bd0a2a0..0e9a1711f3 100644
--- a/src/applications/differential/view/DifferentialChangesetDetailView.php
+++ b/src/applications/differential/view/DifferentialChangesetDetailView.php
@@ -1,164 +1,164 @@
 <?php
 
 final class DifferentialChangesetDetailView extends AphrontView {
 
   private $changeset;
   private $buttons = array();
   private $editable;
   private $symbolIndex;
   private $id;
   private $vsChangesetID;
 
   public function setChangeset($changeset) {
     $this->changeset = $changeset;
     return $this;
   }
 
   public function addButton($button) {
     $this->buttons[] = $button;
     return $this;
   }
 
   public function setEditable($editable) {
     $this->editable = $editable;
     return $this;
   }
 
   public function setSymbolIndex($symbol_index) {
     $this->symbolIndex = $symbol_index;
     return $this;
   }
 
   public function getID() {
     if (!$this->id) {
       $this->id = celerity_generate_unique_node_id();
     }
     return $this->id;
   }
 
   public function setVsChangesetID($vs_changeset_id) {
     $this->vsChangesetID = $vs_changeset_id;
     return $this;
   }
 
   public function getVsChangesetID() {
     return $this->vsChangesetID;
   }
 
   public function getFileIcon($filename) {
     $path_info = pathinfo($filename);
     $extension = idx($path_info, 'extension');
     switch ($extension) {
       case 'psd':
       case 'ai':
         $icon = 'preview';
         break;
       case 'conf':
         $icon = 'wrench';
         break;
       case 'wav':
       case 'mp3':
       case 'aiff':
         $icon = 'music';
         break;
       case 'm4v':
       case 'mov':
         $icon = 'film';
         break;
       case 'sql';
       case 'db':
       case 'csv':
         $icon = 'data';
         break;
       case 'ics':
         $icon = 'calendar';
         break;
       case 'zip':
       case 'tar':
       case 'bz':
       case 'tgz':
       case 'gz':
         $icon = 'zip';
         break;
       case 'png':
       case 'jpg':
       case 'bmp':
       case 'gif':
         $icon = 'image';
         break;
       default:
         $icon = 'file';
         break;
     }
     return $icon;
   }
 
   public function render() {
-    require_celerity_resource('differential-changeset-view-css');
-    require_celerity_resource('syntax-highlighting-css');
+    $this->requireResource('differential-changeset-view-css');
+    $this->requireResource('syntax-highlighting-css');
 
     Javelin::initBehavior('phabricator-oncopy', array());
 
     $changeset = $this->changeset;
     $class = 'differential-changeset';
     if (!$this->editable) {
       $class .= ' differential-changeset-immutable';
     }
 
     $buttons = null;
     if ($this->buttons) {
       $buttons = phutil_tag(
         'div',
         array(
           'class' => 'differential-changeset-buttons',
         ),
         $this->buttons);
     }
 
     $id = $this->getID();
 
     if ($this->symbolIndex) {
       Javelin::initBehavior(
         'repository-crossreference',
         array(
           'container' => $id,
         ) + $this->symbolIndex);
     }
 
     $display_filename = $changeset->getDisplayFilename();
     $display_icon = $this->getFileIcon($display_filename);
     $icon = id(new PHUIIconView())
       ->setSpriteSheet(PHUIIconView::SPRITE_ICONS)
       ->setSpriteIcon($display_icon);
 
     return javelin_tag(
       'div',
       array(
         'sigil' => 'differential-changeset',
         'meta'  => array(
           'left'  => nonempty(
             $this->getVsChangesetID(),
             $this->changeset->getID()),
           'right' => $this->changeset->getID(),
         ),
         'class' => $class,
         'id'    => $id,
       ),
       array(
         id(new PhabricatorAnchorView())
           ->setAnchorName($changeset->getAnchorName())
           ->setNavigationMarker(true)
           ->render(),
         $buttons,
         phutil_tag('h1',
           array(
             'class' => 'differential-file-icon-header'),
           array(
             $icon,
             $display_filename)),
         phutil_tag('div', array('style' => 'clear: both'), ''),
         $this->renderChildren(),
       ));
   }
 
 }
diff --git a/src/applications/differential/view/DifferentialChangesetListView.php b/src/applications/differential/view/DifferentialChangesetListView.php
index 17ed42eac9..069fd2bc4b 100644
--- a/src/applications/differential/view/DifferentialChangesetListView.php
+++ b/src/applications/differential/view/DifferentialChangesetListView.php
@@ -1,348 +1,348 @@
 <?php
 
 final class DifferentialChangesetListView extends AphrontView {
 
   private $changesets = array();
   private $visibleChangesets = array();
   private $references = array();
   private $inlineURI;
   private $renderURI = '/differential/changeset/';
   private $whitespace;
 
   private $standaloneURI;
   private $leftRawFileURI;
   private $rightRawFileURI;
 
   private $symbolIndexes = array();
   private $repository;
   private $branch;
   private $diff;
   private $vsMap = array();
 
   private $title;
 
   public function setTitle($title) {
     $this->title = $title;
     return $this;
   }
   private function getTitle() {
     return $this->title;
   }
 
   public function setBranch($branch) {
     $this->branch = $branch;
     return $this;
   }
   private function getBranch() {
     return $this->branch;
   }
 
   public function setChangesets($changesets) {
     $this->changesets = $changesets;
     return $this;
   }
 
   public function setVisibleChangesets($visible_changesets) {
     $this->visibleChangesets = $visible_changesets;
     return $this;
   }
 
   public function setInlineCommentControllerURI($uri) {
     $this->inlineURI = $uri;
     return $this;
   }
 
   public function setRepository(PhabricatorRepository $repository) {
     $this->repository = $repository;
     return $this;
   }
 
   public function setDiff(DifferentialDiff $diff) {
     $this->diff = $diff;
     return $this;
   }
 
   public function setRenderingReferences(array $references) {
     $this->references = $references;
     return $this;
   }
 
   public function setSymbolIndexes(array $indexes) {
     $this->symbolIndexes = $indexes;
     return $this;
   }
 
   public function setRenderURI($render_uri) {
     $this->renderURI = $render_uri;
     return $this;
   }
 
   public function setWhitespace($whitespace) {
     $this->whitespace = $whitespace;
     return $this;
   }
 
   public function setVsMap(array $vs_map) {
     $this->vsMap = $vs_map;
     return $this;
   }
 
   public function getVsMap() {
     return $this->vsMap;
   }
 
   public function setStandaloneURI($uri) {
     $this->standaloneURI = $uri;
     return $this;
   }
 
   public function setRawFileURIs($l, $r) {
     $this->leftRawFileURI = $l;
     $this->rightRawFileURI = $r;
     return $this;
   }
 
   public function render() {
-    require_celerity_resource('differential-changeset-view-css');
+    $this->requireResource('differential-changeset-view-css');
 
     $changesets = $this->changesets;
 
     Javelin::initBehavior('differential-toggle-files', array(
       'pht' => array(
         'undo' => pht('Undo'),
         'collapsed' => pht('This file content has been collapsed.'))
       ));
     Javelin::initBehavior(
       'differential-dropdown-menus',
       array(
         'pht' => array(
           'Open in Editor' => pht('Open in Editor'),
           'Show Entire File' => pht('Show Entire File'),
           'Entire File Shown' => pht('Entire File Shown'),
           "Can't Toggle Unloaded File" => pht("Can't Toggle Unloaded File"),
           'Expand File' => pht('Expand File'),
           'Collapse File' => pht('Collapse File'),
           'Browse in Diffusion' => pht('Browse in Diffusion'),
           'View Standalone' => pht('View Standalone'),
           'Show Raw File (Left)' => pht('Show Raw File (Left)'),
           'Show Raw File (Right)' => pht('Show Raw File (Right)'),
           'Configure Editor' => pht('Configure Editor'),
         ),
       ));
 
     $output = array();
     $mapping = array();
     foreach ($changesets as $key => $changeset) {
       $file = $changeset->getFilename();
       $class = 'differential-changeset';
       if (!$this->inlineURI) {
         $class .= ' differential-changeset-noneditable';
       }
 
       $ref = $this->references[$key];
 
       $detail = new DifferentialChangesetDetailView();
 
       $view_options = $this->renderViewOptionsDropdown(
         $detail,
         $ref,
         $changeset);
 
       $detail->setChangeset($changeset);
       $detail->addButton($view_options);
       $detail->setSymbolIndex(idx($this->symbolIndexes, $key));
       $detail->setVsChangesetID(idx($this->vsMap, $changeset->getID()));
       $detail->setEditable(true);
 
       $uniq_id = 'diff-'.$changeset->getAnchorName();
       if (isset($this->visibleChangesets[$key])) {
         $load = 'Loading...';
         $mapping[$uniq_id] = $ref;
       } else {
         $load = javelin_tag(
           'a',
           array(
             'href' => '#'.$uniq_id,
             'meta' => array(
               'id' => $uniq_id,
               'ref' => $ref,
               'kill' => true,
             ),
             'sigil' => 'differential-load',
             'mustcapture' => true,
           ),
           pht('Load'));
       }
       $detail->appendChild(
         phutil_tag(
           'div',
           array(
             'id' => $uniq_id,
           ),
           phutil_tag('div', array('class' => 'differential-loading'), $load)));
       $output[] = $detail->render();
     }
 
-    require_celerity_resource('aphront-tooltip-css');
+    $this->requireResource('aphront-tooltip-css');
 
-    Javelin::initBehavior('differential-populate', array(
+    $this->initBehavior('differential-populate', array(
       'registry'    => $mapping,
       'whitespace'  => $this->whitespace,
       'uri'         => $this->renderURI,
     ));
 
-    Javelin::initBehavior('differential-show-more', array(
+    $this->initBehavior('differential-show-more', array(
       'uri' => $this->renderURI,
       'whitespace' => $this->whitespace,
     ));
 
-    Javelin::initBehavior('differential-comment-jump', array());
+    $this->initBehavior('differential-comment-jump', array());
 
     if ($this->inlineURI) {
       $undo_templates = $this->renderUndoTemplates();
 
       Javelin::initBehavior('differential-edit-inline-comments', array(
         'uri'             => $this->inlineURI,
         'undo_templates'  => $undo_templates,
         'stage'           => 'differential-review-stage',
       ));
     }
 
     $header = id(new PHUIHeaderView())
       ->setHeader($this->getTitle());
 
     $content = phutil_tag(
       'div',
       array(
         'class' => 'differential-review-stage',
         'id'    => 'differential-review-stage',
       ),
       $output);
 
     $object_box = id(new PHUIObjectBoxView())
       ->setHeader($header)
       ->appendChild($content);
 
     return $object_box;
   }
 
   /**
    * Render the "Undo" markup for the inline comment undo feature.
    */
   private function renderUndoTemplates() {
     $link = javelin_tag(
       'a',
       array(
         'href'  => '#',
         'sigil' => 'differential-inline-comment-undo',
       ),
       pht('Undo'));
 
     $div = phutil_tag(
       'div',
       array(
         'class' => 'differential-inline-undo',
       ),
       array('Changes discarded. ', $link));
 
     return array(
       'l' => phutil_tag('table', array(),
         phutil_tag('tr', array(), array(
           phutil_tag('th', array()),
           phutil_tag('td', array(), $div),
           phutil_tag('th', array()),
           phutil_tag('td', array('colspan' => 3)),
         ))),
 
       'r' => phutil_tag('table', array(),
         phutil_tag('tr', array(), array(
           phutil_tag('th', array()),
           phutil_tag('td', array()),
           phutil_tag('th', array()),
           phutil_tag('td', array('colspan' => 3), $div),
         ))),
     );
   }
 
   private function renderViewOptionsDropdown(
     DifferentialChangesetDetailView $detail,
     $ref,
     DifferentialChangeset $changeset) {
 
     $meta = array();
 
     $qparams = array(
       'ref'         => $ref,
       'whitespace'  => $this->whitespace,
     );
 
     if ($this->standaloneURI) {
       $uri = new PhutilURI($this->standaloneURI);
       $uri->setQueryParams($uri->getQueryParams() + $qparams);
       $meta['standaloneURI'] = (string)$uri;
     }
 
     $repository = $this->repository;
     if ($repository) {
       try {
         $meta['diffusionURI'] =
           (string)$repository->getDiffusionBrowseURIForPath(
             $this->user,
             $changeset->getAbsoluteRepositoryPath($repository, $this->diff),
             idx($changeset->getMetadata(), 'line:first'),
             $this->getBranch());
       } catch (DiffusionSetupException $e) {
         // Ignore
       }
     }
 
     $change = $changeset->getChangeType();
 
     if ($this->leftRawFileURI) {
       if ($change != DifferentialChangeType::TYPE_ADD) {
         $uri = new PhutilURI($this->leftRawFileURI);
         $uri->setQueryParams($uri->getQueryParams() + $qparams);
         $meta['leftURI'] = (string)$uri;
       }
     }
 
     if ($this->rightRawFileURI) {
       if ($change != DifferentialChangeType::TYPE_DELETE &&
           $change != DifferentialChangeType::TYPE_MULTICOPY) {
         $uri = new PhutilURI($this->rightRawFileURI);
         $uri->setQueryParams($uri->getQueryParams() + $qparams);
         $meta['rightURI'] = (string)$uri;
       }
     }
 
     $user = $this->user;
     if ($user && $repository) {
       $path = ltrim(
         $changeset->getAbsoluteRepositoryPath($repository, $this->diff),
         '/');
       $line = idx($changeset->getMetadata(), 'line:first', 1);
       $callsign = $repository->getCallsign();
       $editor_link = $user->loadEditorLink($path, $line, $callsign);
       if ($editor_link) {
         $meta['editor'] = $editor_link;
       } else {
         $meta['editorConfigure'] = '/settings/panel/display/';
       }
     }
 
     $meta['containerID'] = $detail->getID();
     $caret = phutil_tag('span', array('class' => 'caret'), '');
 
     return javelin_tag(
       'a',
       array(
         'class'   => 'button grey small dropdown',
         'meta'    => $meta,
         'href'    => idx($meta, 'detailURI', '#'),
         'target'  => '_blank',
         'sigil'   => 'differential-view-options',
       ),
       array(pht('View Options'), $caret));
   }
 
 }
diff --git a/src/applications/differential/view/DifferentialDiffTableOfContentsView.php b/src/applications/differential/view/DifferentialDiffTableOfContentsView.php
index 84fae42243..74391ef5f9 100644
--- a/src/applications/differential/view/DifferentialDiffTableOfContentsView.php
+++ b/src/applications/differential/view/DifferentialDiffTableOfContentsView.php
@@ -1,309 +1,309 @@
 <?php
 
 final class DifferentialDiffTableOfContentsView extends AphrontView {
 
   private $changesets = array();
   private $visibleChangesets = array();
   private $references = array();
   private $repository;
   private $diff;
   private $renderURI = '/differential/changeset/';
   private $revisionID;
   private $whitespace;
   private $unitTestData;
 
   public function setChangesets($changesets) {
     $this->changesets = $changesets;
     return $this;
   }
 
   public function setVisibleChangesets($visible_changesets) {
     $this->visibleChangesets = $visible_changesets;
     return $this;
   }
 
   public function setRenderingReferences(array $references) {
     $this->references = $references;
     return $this;
   }
 
   public function setRepository(PhabricatorRepository $repository) {
     $this->repository = $repository;
     return $this;
   }
 
   public function setDiff(DifferentialDiff $diff) {
     $this->diff = $diff;
     return $this;
   }
 
   public function setUnitTestData($unit_test_data) {
     $this->unitTestData = $unit_test_data;
     return $this;
   }
 
   public function setRevisionID($revision_id) {
     $this->revisionID = $revision_id;
     return $this;
   }
 
   public function setWhitespace($whitespace) {
     $this->whitespace = $whitespace;
     return $this;
   }
 
   public function render() {
 
-    require_celerity_resource('differential-core-view-css');
-    require_celerity_resource('differential-table-of-contents-css');
+    $this->requireResource('differential-core-view-css');
+    $this->requireResource('differential-table-of-contents-css');
 
     $rows = array();
 
     $coverage = array();
     if ($this->unitTestData) {
       $coverage_by_file = array();
       foreach ($this->unitTestData as $result) {
         $test_coverage = idx($result, 'coverage');
         if (!$test_coverage) {
           continue;
         }
         foreach ($test_coverage as $file => $results) {
           $coverage_by_file[$file][] = $results;
         }
       }
       foreach ($coverage_by_file as $file => $coverages) {
         $coverage[$file] = ArcanistUnitTestResult::mergeCoverage($coverages);
       }
     }
 
     $changesets = $this->changesets;
     $paths = array();
     foreach ($changesets as $id => $changeset) {
       $type = $changeset->getChangeType();
       $ftype = $changeset->getFileType();
       $ref = idx($this->references, $id);
       $display_file = $changeset->getDisplayFilename();
 
       $meta = null;
       if (DifferentialChangeType::isOldLocationChangeType($type)) {
         $away = $changeset->getAwayPaths();
         if (count($away) > 1) {
           $meta = array();
           if ($type == DifferentialChangeType::TYPE_MULTICOPY) {
             $meta[] = pht('Deleted after being copied to multiple locations:');
           } else {
             $meta[] = pht('Copied to multiple locations:');
           }
           foreach ($away as $path) {
             $meta[] = $path;
           }
           $meta = phutil_implode_html(phutil_tag('br'), $meta);
         } else {
           if ($type == DifferentialChangeType::TYPE_MOVE_AWAY) {
             $display_file = $this->renderRename(
               $display_file,
               reset($away),
               "\xE2\x86\x92");
           } else {
             $meta = pht('Copied to %s', reset($away));
           }
         }
       } else if ($type == DifferentialChangeType::TYPE_MOVE_HERE) {
         $old_file = $changeset->getOldFile();
         $display_file = $this->renderRename(
           $display_file,
           $old_file,
           "\xE2\x86\x90");
       } else if ($type == DifferentialChangeType::TYPE_COPY_HERE) {
         $meta = pht('Copied from %s', $changeset->getOldFile());
       }
 
       $link = $this->renderChangesetLink($changeset, $ref, $display_file);
 
       $line_count = $changeset->getAffectedLineCount();
       if ($line_count == 0) {
         $lines = null;
       } else {
         $lines = ' '.pht('(%d line(s))', $line_count);
       }
 
       $char = DifferentialChangeType::getSummaryCharacterForChangeType($type);
       $chartitle = DifferentialChangeType::getFullNameForChangeType($type);
       $desc = DifferentialChangeType::getShortNameForFileType($ftype);
       if ($desc) {
         $desc = '('.$desc.')';
       }
       $pchar =
         ($changeset->getOldProperties() === $changeset->getNewProperties())
           ? null
           : phutil_tag('span', array('title' => pht('Properties Changed')), 'M')
         ;
 
       $fname = $changeset->getFilename();
       $cov  = $this->renderCoverage($coverage, $fname);
       if ($cov === null) {
         $mcov = $cov = phutil_tag('em', array(), '-');
       } else {
         $mcov = phutil_tag(
           'div',
           array(
             'id' => 'differential-mcoverage-'.md5($fname),
             'class' => 'differential-mcoverage-loading',
           ),
           (isset($this->visibleChangesets[$id]) ? 'Loading...' : '?'));
       }
 
       $rows[] = phutil_tag('tr', array(), array(
         phutil_tag(
           'td',
           array('class' => 'differential-toc-char', 'title' => $chartitle),
           $char),
         phutil_tag('td', array('class' => 'differential-toc-prop'), $pchar),
         phutil_tag('td', array('class' => 'differential-toc-ftype'), $desc),
         phutil_tag(
           'td',
           array('class' => 'differential-toc-file'),
           array($link, $lines)),
         phutil_tag('td', array('class' => 'differential-toc-cov'), $cov),
         phutil_tag('td', array('class' => 'differential-toc-mcov'), $mcov),
       ));
       if ($meta) {
         $rows[] = phutil_tag('tr', array(), array(
           phutil_tag('td', array('colspan' => 3)),
           phutil_tag('td', array('class' => 'differential-toc-meta'), $meta),
         ));
       }
       if ($this->diff && $this->repository) {
         $paths[] =
           $changeset->getAbsoluteRepositoryPath($this->repository, $this->diff);
       }
     }
 
     $editor_link = null;
     if ($paths && $this->user) {
       $editor_link = $this->user->loadEditorLink(
         $paths,
         1, // line number
         $this->repository->getCallsign());
       if ($editor_link) {
         $editor_link =
           phutil_tag(
             'a',
             array(
               'href' => $editor_link,
               'class' => 'button differential-toc-edit-all',
             ),
             pht('Open All in Editor'));
       }
     }
 
     $reveal_link = javelin_tag(
         'a',
         array(
           'sigil' => 'differential-reveal-all',
           'mustcapture' => true,
           'class' => 'button differential-toc-reveal-all',
         ),
         pht('Show All Context'));
 
     $buttons = phutil_tag('tr', array(),
       phutil_tag('td', array('colspan' => 7),
         array($editor_link, $reveal_link)));
 
     $content = hsprintf(
       '%s'.
       '<div class="differential-toc differential-panel">'.
         '<table>'.
           '<tr>'.
             '<th></th>'.
             '<th></th>'.
             '<th></th>'.
             '<th>Path</th>'.
             '<th class="differential-toc-cov">%s</th>'.
             '<th class="differential-toc-mcov">%s</th>'.
           '</tr>'.
           '%s%s'.
         '</table>'.
       '</div>',
       id(new PhabricatorAnchorView())
         ->setAnchorName('toc')
         ->setNavigationMarker(true)
         ->render(),
       pht('Coverage (All)'),
       pht('Coverage (Touched)'),
       phutil_implode_html("\n", $rows),
       $buttons);
 
     return id(new PHUIObjectBoxView())
       ->setHeaderText(pht('Table of Contents'))
       ->appendChild($content);
   }
 
   private function renderRename($display_file, $other_file, $arrow) {
     $old = explode('/', $display_file);
     $new = explode('/', $other_file);
 
     $start = count($old);
     foreach ($old as $index => $part) {
       if (!isset($new[$index]) || $part != $new[$index]) {
         $start = $index;
         break;
       }
     }
 
     $end = count($old);
     foreach (array_reverse($old) as $from_end => $part) {
       $index = count($new) - $from_end - 1;
       if (!isset($new[$index]) || $part != $new[$index]) {
         $end = $from_end;
         break;
       }
     }
 
     $rename =
       '{'.
       implode('/', array_slice($old, $start, count($old) - $end - $start)).
       ' '.$arrow.' '.
       implode('/', array_slice($new, $start, count($new) - $end - $start)).
       '}';
 
     array_splice($new, $start, count($new) - $end - $start, $rename);
     return implode('/', $new);
   }
 
   private function renderCoverage(array $coverage, $file) {
     $info = idx($coverage, $file);
     if (!$info) {
       return null;
     }
 
     $not_covered = substr_count($info, 'U');
     $covered     = substr_count($info, 'C');
 
     if (!$not_covered && !$covered) {
       return null;
     }
 
     return sprintf('%d%%', 100 * ($covered / ($covered + $not_covered)));
   }
 
 
   private function renderChangesetLink(
     DifferentialChangeset $changeset,
     $ref,
     $display_file) {
 
     return javelin_tag(
       'a',
       array(
         'href' => '#'.$changeset->getAnchorName(),
         'meta' => array(
           'id' => 'diff-'.$changeset->getAnchorName(),
           'ref' => $ref,
         ),
         'sigil' => 'differential-load',
       ),
       $display_file);
   }
 
 }
diff --git a/src/applications/differential/view/DifferentialLocalCommitsView.php b/src/applications/differential/view/DifferentialLocalCommitsView.php
index 4321807139..d4fbe45431 100644
--- a/src/applications/differential/view/DifferentialLocalCommitsView.php
+++ b/src/applications/differential/view/DifferentialLocalCommitsView.php
@@ -1,145 +1,145 @@
 <?php
 
 final class DifferentialLocalCommitsView extends AphrontView {
 
   private $localCommits;
 
   public function setLocalCommits($local_commits) {
     $this->localCommits = $local_commits;
     return $this;
   }
 
   public function render() {
     $user = $this->user;
     if (!$user) {
       throw new Exception("Call setUser() before render()-ing this view.");
     }
 
     $local = $this->localCommits;
     if (!$local) {
       return null;
     }
 
-    require_celerity_resource('differential-local-commits-view-css');
+    $this->requireResource('differential-local-commits-view-css');
 
     $has_tree = false;
     $has_local = false;
 
     foreach ($local as $commit) {
       if (idx($commit, 'tree')) {
         $has_tree = true;
       }
       if (idx($commit, 'local')) {
         $has_local = true;
       }
     }
 
     $rows = array();
     $highlight = true;
     foreach ($local as $commit) {
       if ($highlight) {
         $class = 'alt';
         $highlight = false;
       } else {
         $class = '';
         $highlight = true;
       }
 
 
       $row = array();
       if (idx($commit, 'commit')) {
         $commit_hash = self::formatCommit($commit['commit']);
       } else if (isset($commit['rev'])) {
         $commit_hash = self::formatCommit($commit['rev']);
       } else {
         $commit_hash = null;
       }
       $row[] = phutil_tag('td', array(), $commit_hash);
 
       if ($has_tree) {
         $tree = idx($commit, 'tree');
         $tree = self::formatCommit($tree);
         $row[] = phutil_tag('td', array(), $tree);
       }
 
       if ($has_local) {
         $local_rev = idx($commit, 'local', null);
         $row[] = phutil_tag('td', array(), $local_rev);
       }
 
       $parents = idx($commit, 'parents', array());
       foreach ($parents as $k => $parent) {
         if (is_array($parent)) {
           $parent = idx($parent, 'rev');
         }
         $parents[$k] = self::formatCommit($parent);
       }
       $parents = phutil_implode_html(phutil_tag('br'), $parents);
       $row[] = phutil_tag('td', array(), $parents);
 
       $author = nonempty(
         idx($commit, 'user'),
         idx($commit, 'author'));
       $row[] = phutil_tag('td', array(), $author);
 
       $message = idx($commit, 'message');
 
       $summary = idx($commit, 'summary');
       $summary = phutil_utf8_shorten($summary, 80);
 
       $view = new AphrontMoreView();
       $view->setSome($summary);
 
       if ($message && (trim($summary) != trim($message))) {
         $view->setMore(phutil_escape_html_newlines($message));
       }
 
       $row[] = phutil_tag(
         'td',
         array(
           'class' => 'summary',
         ),
         $view->render());
 
       $date = nonempty(
         idx($commit, 'date'),
         idx($commit, 'time'));
       if ($date) {
         $date = phabricator_datetime($date, $user);
       }
       $row[] = phutil_tag('td', array(), $date);
 
       $rows[] = phutil_tag('tr', array('class' => $class), $row);
     }
 
 
     $headers = array();
     $headers[] = phutil_tag('th', array(), pht('Commit'));
     if ($has_tree) {
       $headers[] = phutil_tag('th', array(), pht('Tree'));
     }
     if ($has_local) {
       $headers[] = phutil_tag('th', array(), pht('Local'));
     }
     $headers[] = phutil_tag('th', array(), pht('Parents'));
     $headers[] = phutil_tag('th', array(), pht('Author'));
     $headers[] = phutil_tag('th', array(), pht('Summary'));
     $headers[] = phutil_tag('th', array(), pht('Date'));
 
     $headers = phutil_tag('tr', array(), $headers);
 
     $content = phutil_tag_div('differential-panel', phutil_tag(
       'table',
       array('class' => 'differential-local-commits-table'),
       array($headers, phutil_implode_html("\n", $rows))));
 
     return id(new PHUIObjectBoxView())
       ->setHeaderText(pht('Local Commits'))
       ->appendChild($content);
   }
 
   private static function formatCommit($commit) {
     return substr($commit, 0, 12);
   }
 
 }
diff --git a/src/applications/differential/view/DifferentialResultsTableView.php b/src/applications/differential/view/DifferentialResultsTableView.php
index f2be210728..226c68484c 100644
--- a/src/applications/differential/view/DifferentialResultsTableView.php
+++ b/src/applications/differential/view/DifferentialResultsTableView.php
@@ -1,115 +1,115 @@
 <?php
 
 final class DifferentialResultsTableView extends AphrontView {
 
   private $rows;
   private $showMoreString;
 
   public function setRows(array $rows) {
     $this->rows = $rows;
     return $this;
   }
 
   public function setShowMoreString($show_more_string) {
     $this->showMoreString = $show_more_string;
     return $this;
   }
 
   public function render() {
 
     $rows = array();
 
     $any_hidden = false;
     foreach ($this->rows as $row) {
 
       $style = idx($row, 'style');
       switch ($style) {
         case 'section':
           $cells = phutil_tag(
             'th',
             array(
               'colspan' => 2,
             ),
             idx($row, 'name'));
           break;
         default:
           $name = phutil_tag(
             'th',
             array(
             ),
             idx($row, 'name'));
           $value = phutil_tag(
             'td',
             array(
             ),
             idx($row, 'value'));
           $cells = array($name, $value);
           break;
       }
 
       $show = idx($row, 'show');
 
       $rows[] = javelin_tag(
         'tr',
         array(
           'style' => $show ? null : 'display: none',
           'sigil' => $show ? null : 'differential-results-row-toggle',
           'class' => 'differential-results-row-'.$style,
         ),
         $cells);
 
       if (!$show) {
         $any_hidden = true;
       }
     }
 
     if ($any_hidden) {
       $show_more = javelin_tag(
         'a',
         array(
           'href'        => '#',
           'mustcapture' => true,
         ),
         $this->showMoreString);
 
       $hide_more = javelin_tag(
         'a',
         array(
           'href'        => '#',
           'mustcapture' => true,
         ),
         'Hide');
 
       $rows[] = javelin_tag(
         'tr',
         array(
           'class' => 'differential-results-row-show',
           'sigil' => 'differential-results-row-show',
         ),
         phutil_tag('th', array('colspan' => 2), $show_more));
 
       $rows[] = javelin_tag(
         'tr',
         array(
           'class' => 'differential-results-row-show',
           'sigil' => 'differential-results-row-hide',
           'style' => 'display: none',
         ),
         phutil_tag('th', array('colspan' => 2), $hide_more));
 
-      Javelin::initBehavior('differential-show-field-details');
+      $this->initBehavior('differential-show-field-details');
     }
 
-    require_celerity_resource('differential-results-table-css');
+    $this->requireResource('differential-results-table-css');
 
     return javelin_tag(
       'table',
       array(
         'class' => 'differential-results-table',
         'sigil' => 'differential-results-table',
       ),
       $rows);
   }
 
 
 }
diff --git a/src/applications/differential/view/DifferentialRevisionCommentListView.php b/src/applications/differential/view/DifferentialRevisionCommentListView.php
index 86d0c76bfa..b03cee03db 100644
--- a/src/applications/differential/view/DifferentialRevisionCommentListView.php
+++ b/src/applications/differential/view/DifferentialRevisionCommentListView.php
@@ -1,198 +1,198 @@
 <?php
 
 final class DifferentialRevisionCommentListView extends AphrontView {
 
   private $comments;
   private $handles;
   private $inlines;
   private $changesets;
   private $target;
   private $versusDiffID;
   private $id;
 
   public function setComments(array $comments) {
     assert_instances_of($comments, 'DifferentialComment');
     $this->comments = $comments;
     return $this;
   }
 
   public function setInlineComments(array $inline_comments) {
     assert_instances_of($inline_comments, 'PhabricatorInlineCommentInterface');
     $this->inlines = $inline_comments;
     return $this;
   }
 
   public function setHandles(array $handles) {
     assert_instances_of($handles, 'PhabricatorObjectHandle');
     $this->handles = $handles;
     return $this;
   }
 
   public function setChangesets(array $changesets) {
     assert_instances_of($changesets, 'DifferentialChangeset');
     $this->changesets = $changesets;
     return $this;
   }
 
   public function setTargetDiff(DifferentialDiff $target) {
     $this->target = $target;
     return $this;
   }
 
   public function setVersusDiffID($diff_vs) {
     $this->versusDiffID = $diff_vs;
     return $this;
   }
 
   public function getID() {
     if (!$this->id) {
       $this->id = celerity_generate_unique_node_id();
     }
     return $this->id;
   }
 
   public function render() {
 
-    require_celerity_resource('differential-revision-comment-list-css');
+    $this->requireResource('differential-revision-comment-list-css');
 
     $engine = new PhabricatorMarkupEngine();
     $engine->setViewer($this->user);
     foreach ($this->comments as $comment) {
       $comment->giveFacebookSomeArbitraryDiff($this->target);
 
       $engine->addObject(
         $comment,
         DifferentialComment::MARKUP_FIELD_BODY);
     }
 
     foreach ($this->inlines as $inline) {
       $engine->addObject(
         $inline,
         PhabricatorInlineCommentInterface::MARKUP_FIELD_BODY);
     }
 
     $engine->process();
 
     $inlines = mgroup($this->inlines, 'getCommentID');
 
     $num = 1;
     $html = array();
     foreach ($this->comments as $comment) {
       $view = new DifferentialRevisionCommentView();
       $view->setComment($comment);
       $view->setUser($this->user);
       $view->setHandles($this->handles);
       $view->setMarkupEngine($engine);
       $view->setInlineComments(idx($inlines, $comment->getID(), array()));
       $view->setChangesets($this->changesets);
       $view->setTargetDiff($this->target);
       $view->setVersusDiffID($this->versusDiffID);
       if ($comment->getAction() == DifferentialAction::ACTION_SUMMARIZE) {
         $view->setAnchorName('summary');
       } else if ($comment->getAction() == DifferentialAction::ACTION_TESTPLAN) {
         $view->setAnchorName('test-plan');
       } else {
         $view->setAnchorName('comment-'.$num);
         $num++;
       }
 
       $html[] = $view->render();
     }
 
     $objs = array_reverse(array_values($this->comments));
     $html = array_reverse(array_values($html));
     $user = $this->user;
 
     $last_comment = null;
     // Find the most recent comment by the viewer.
     foreach ($objs as $position => $comment) {
       if ($user && ($comment->getAuthorPHID() == $user->getPHID())) {
         if ($last_comment === null) {
           $last_comment = $position;
         } else if ($last_comment == $position - 1) {
           // If the viewer made several comments in a row, show them all. This
           // is a spaz rule for epriestley.
           $last_comment = $position;
         }
       }
     }
 
     $header = array();
     $hidden = array();
     if ($last_comment !== null) {
       foreach ($objs as $position => $comment) {
         if (!$comment->getID()) {
           // These are synthetic comments with summary/test plan information.
           $header[] = $html[$position];
           unset($html[$position]);
           continue;
         }
         if ($position <= $last_comment) {
           // Always show comments after the viewer's last comment.
           continue;
         }
         if ($position < 3) {
           // Always show the 3 most recent comments.
           continue;
         }
         $hidden[] = $position;
       }
     }
 
     if (count($hidden) <= 3) {
       // Don't hide if there's not much to hide.
       $hidden = array();
     }
 
     $header = array_reverse($header);
 
 
     $hidden = array_select_keys($html, $hidden);
     $visible = array_diff_key($html, $hidden);
 
     $hidden = array_reverse($hidden);
     $visible = array_reverse($visible);
 
     if ($hidden) {
-      Javelin::initBehavior(
+      $this->initBehavior(
         'differential-show-all-comments',
         array(
           'markup' => implode("\n", $hidden),
         ));
       $hidden = javelin_tag(
         'div',
         array(
           'sigil' =>  "differential-all-comments-container",
         ),
         phutil_tag(
           'div',
           array(
             'class' => 'differential-older-comments-are-hidden',
           ),
           array(
             pht(
               '%s older comments are hidden.',
               new PhutilNumber(count($hidden))),
             ' ',
             javelin_tag(
               'a',
               array(
                 'href' => '#',
                 'mustcapture' => true,
                 'sigil' => 'differential-show-all-comments',
               ),
               pht('Show all comments.')),
           )));
     } else {
       $hidden = null;
     }
 
     return javelin_tag(
       'div',
       array(
         'class' => 'differential-comment-list',
         'id' => $this->getID(),
       ),
       array_merge($header, array($hidden), $visible));
   }
 }
diff --git a/src/applications/differential/view/DifferentialRevisionCommentView.php b/src/applications/differential/view/DifferentialRevisionCommentView.php
index 820954dfff..293d93a4a6 100644
--- a/src/applications/differential/view/DifferentialRevisionCommentView.php
+++ b/src/applications/differential/view/DifferentialRevisionCommentView.php
@@ -1,302 +1,302 @@
 <?php
 
 final class DifferentialRevisionCommentView extends AphrontView {
 
   private $comment;
   private $handles;
   private $markupEngine;
   private $preview;
   private $inlines;
   private $changesets;
   private $target;
   private $anchorName;
   private $versusDiffID;
 
   public function setComment($comment) {
     $this->comment = $comment;
     return $this;
   }
 
   public function setHandles(array $handles) {
     assert_instances_of($handles, 'PhabricatorObjectHandle');
     $this->handles = $handles;
     return $this;
   }
 
   public function setMarkupEngine(PhabricatorMarkupEngine $markup_engine) {
     $this->markupEngine = $markup_engine;
     return $this;
   }
 
   public function setPreview($preview) {
     $this->preview = $preview;
     return $this;
   }
 
   public function setInlineComments(array $inline_comments) {
     assert_instances_of($inline_comments, 'PhabricatorInlineCommentInterface');
     $this->inlines = $inline_comments;
     return $this;
   }
 
   public function setChangesets(array $changesets) {
     assert_instances_of($changesets, 'DifferentialChangeset');
     // Ship these in sorted by getSortKey() and keyed by ID... or else!
     $this->changesets = $changesets;
     return $this;
   }
 
   public function setTargetDiff($target) {
     $this->target = $target;
     return $this;
   }
 
   public function setVersusDiffID($diff_vs) {
     $this->versusDiffID = $diff_vs;
     return $this;
   }
 
   public function setAnchorName($anchor_name) {
     $this->anchorName = $anchor_name;
     return $this;
   }
 
   public function render() {
 
     if (!$this->user) {
       throw new Exception("Call setUser() before rendering!");
     }
 
-    require_celerity_resource('phabricator-remarkup-css');
-    require_celerity_resource('differential-revision-comment-css');
+    $this->requireResource('phabricator-remarkup-css');
+    $this->requireResource('differential-revision-comment-css');
 
     $comment = $this->comment;
 
     $action = $comment->getAction();
 
     $action_class = 'differential-comment-action-'.$action;
 
     $info = array();
 
     $content = $comment->getContent();
     $hide_comments = true;
     if (strlen(rtrim($content))) {
       $hide_comments = false;
 
       $content = $this->markupEngine->getOutput(
         $comment,
         PhabricatorInlineCommentInterface::MARKUP_FIELD_BODY);
 
       $content = phutil_tag_div('phabricator-remarkup', $content);
     }
 
     $inline_render = $this->renderInlineComments();
     if ($inline_render) {
       $hide_comments = false;
     }
 
     $author = $this->handles[$comment->getAuthorPHID()];
     $author_link = $author->renderLink();
 
     $metadata = $comment->getMetadata();
     $added_reviewers = idx(
       $metadata,
       DifferentialComment::METADATA_ADDED_REVIEWERS,
       array());
     $removed_reviewers = idx(
       $metadata,
       DifferentialComment::METADATA_REMOVED_REVIEWERS,
       array());
     $added_ccs = idx(
       $metadata,
       DifferentialComment::METADATA_ADDED_CCS,
       array());
 
     $verb = DifferentialAction::getActionPastTenseVerb($comment->getAction());
 
     $actions = array();
     // TODO: i18n
     switch ($comment->getAction()) {
       case DifferentialAction::ACTION_ADDCCS:
         $actions[] = hsprintf(
           "%s added CCs: %s.",
           $author_link,
           $this->renderHandleList($added_ccs));
         $added_ccs = null;
         break;
       case DifferentialAction::ACTION_ADDREVIEWERS:
         $actions[] = hsprintf(
           "%s added reviewers: %s.",
           $author_link,
           $this->renderHandleList($added_reviewers));
         $added_reviewers = null;
         break;
       case DifferentialAction::ACTION_UPDATE:
         $diff_id = idx($metadata, DifferentialComment::METADATA_DIFF_ID);
         if ($diff_id) {
           $diff_link = phutil_tag(
             'a',
             array(
               'href' => '/D'.$comment->getRevisionID().'?id='.$diff_id,
             ),
             'Diff #'.$diff_id);
           $actions[] = hsprintf(
             "%s updated this revision to %s.",
             $author_link,
             $diff_link);
         } else {
           $actions[] = hsprintf(
             "%s %s this revision.",
             $author_link,
             $verb);
         }
         break;
       default:
         $actions[] = hsprintf(
           "%s %s this revision.",
           $author_link,
           $verb);
         break;
     }
 
     if ($added_reviewers) {
       $actions[] = hsprintf(
         "%s added reviewers: %s.",
         $author_link,
         $this->renderHandleList($added_reviewers));
     }
 
     if ($removed_reviewers) {
       $actions[] = hsprintf(
         "%s removed reviewers: %s.",
         $author_link,
         $this->renderHandleList($removed_reviewers));
     }
 
     if ($added_ccs) {
       $actions[] = hsprintf(
         "%s added CCs: %s.",
         $author_link,
         $this->renderHandleList($added_ccs));
     }
 
     foreach ($actions as $key => $action) {
       $actions[$key] = phutil_tag('div', array(), $action);
     }
 
     $xaction_view = id(new PhabricatorTransactionView())
       ->setUser($this->user)
       ->setImageURI($author->getImageURI())
       ->setContentSource($comment->getContentSource())
       ->addClass($action_class)
       ->setActions($actions);
 
     if ($this->preview) {
       $xaction_view->setIsPreview($this->preview);
     } else {
       $xaction_view->setEpoch($comment->getDateCreated());
       if ($this->anchorName) {
         $anchor_text =
           'D'.$comment->getRevisionID().
           '#'.preg_replace('/^comment-/', '', $this->anchorName);
 
         $xaction_view->setAnchor($this->anchorName, $anchor_text);
       }
     }
 
     if (!$hide_comments) {
       $xaction_view->appendChild(phutil_tag_div(
         'differential-comment-core',
         array($content, $inline_render)));
     }
 
     return $xaction_view->render();
   }
 
   private function renderHandleList(array $phids) {
     $result = array();
     foreach ($phids as $phid) {
       $result[] = $this->handles[$phid]->renderLink();
     }
     return phutil_implode_html(', ', $result);
   }
 
   private function renderInlineComments() {
     if (!$this->inlines) {
       return null;
     }
 
     $inlines = $this->inlines;
     $changesets = $this->changesets;
     $inlines_by_changeset = mgroup($inlines, 'getChangesetID');
     $inlines_by_changeset = array_select_keys(
       $inlines_by_changeset,
       array_keys($this->changesets));
 
     $view = new PhabricatorInlineSummaryView();
     foreach ($inlines_by_changeset as $changeset_id => $inlines) {
       $changeset = $changesets[$changeset_id];
       $items = array();
       foreach ($inlines as $inline) {
 
         $on_target = ($this->target) &&
                      ($this->target->getID() == $changeset->getDiffID());
 
         $is_visible = false;
         if ($inline->getIsNewFile()) {
           // This comment is on the right side of the versus diff, and visible
           // on the left side of the page.
           if ($this->versusDiffID) {
             if ($changeset->getDiffID() == $this->versusDiffID) {
               $is_visible = true;
             }
           }
 
           // This comment is on the right side of the target diff, and visible
           // on the right side of the page.
           if ($on_target) {
             $is_visible = true;
           }
         } else {
           // Ths comment is on the left side of the target diff, and visible
           // on the left side of the page.
           if (!$this->versusDiffID) {
             if ($on_target) {
               $is_visible = true;
             }
           }
 
           // TODO: We still get one edge case wrong here, when we have a
           // versus diff and the file didn't exist in the old version. The
           // comment is visible because we show the left side of the target
           // diff when there's no corresponding file in the versus diff, but
           // we incorrectly link it off-page.
         }
 
         $item = array(
           'id'      => $inline->getID(),
           'line'    => $inline->getLineNumber(),
           'length'  => $inline->getLineLength(),
           'content' => $this->markupEngine->getOutput(
             $inline,
             DifferentialInlineComment::MARKUP_FIELD_BODY),
         );
 
         if (!$is_visible) {
           $diff_id = $changeset->getDiffID();
           $item['where'] = '(On Diff #'.$diff_id.')';
           $item['href'] =
             'D'.$this->comment->getRevisionID().
             '?id='.$diff_id.
             '#inline-'.$inline->getID();
         }
 
         $items[] = $item;
       }
       $view->addCommentGroup($changeset->getFilename(), $items);
     }
 
     return $view;
   }
 
 }
diff --git a/src/applications/differential/view/DifferentialRevisionDetailView.php b/src/applications/differential/view/DifferentialRevisionDetailView.php
index fad95b5ada..7e0f3c5de6 100644
--- a/src/applications/differential/view/DifferentialRevisionDetailView.php
+++ b/src/applications/differential/view/DifferentialRevisionDetailView.php
@@ -1,177 +1,177 @@
 <?php
 
 final class DifferentialRevisionDetailView extends AphrontView {
 
   private $revision;
   private $actions;
   private $auxiliaryFields = array();
   private $diff;
   private $uri;
 
   public function setURI($uri) {
     $this->uri = $uri;
     return $this;
   }
   public function getURI() {
     return $this->uri;
   }
 
   public function setDiff(DifferentialDiff $diff) {
     $this->diff = $diff;
     return $this;
   }
   private function getDiff() {
     return $this->diff;
   }
 
   public function setRevision(DifferentialRevision $revision) {
     $this->revision = $revision;
     return $this;
   }
 
   public function setActions(array $actions) {
     $this->actions = $actions;
     return $this;
   }
   private function getActions() {
     return $this->actions;
   }
 
   public function setAuxiliaryFields(array $fields) {
     assert_instances_of($fields, 'DifferentialFieldSpecification');
     $this->auxiliaryFields = $fields;
     return $this;
   }
 
   public function render() {
 
-    require_celerity_resource('differential-core-view-css');
+    $this->requireResource('differential-core-view-css');
 
     $revision = $this->revision;
     $user = $this->getUser();
 
     $header = $this->renderHeader($revision);
 
     $actions = id(new PhabricatorActionListView())
       ->setUser($user)
       ->setObject($revision)
       ->setObjectURI($this->getURI());
     foreach ($this->getActions() as $action) {
       $obj = id(new PhabricatorActionView())
         ->setIcon(idx($action, 'icon', 'edit'))
         ->setName($action['name'])
         ->setHref(idx($action, 'href'))
         ->setWorkflow(idx($action, 'sigil') == 'workflow')
         ->setRenderAsForm(!empty($action['instant']))
         ->setUser($user)
         ->setDisabled(idx($action, 'disabled', false));
       $actions->addAction($obj);
     }
 
     $properties = id(new PHUIPropertyListView())
       ->setUser($user)
       ->setObject($revision);
 
     $status = $revision->getStatus();
     $local_vcs = $this->getDiff()->getSourceControlSystem();
 
     $next_step = null;
     if ($status == ArcanistDifferentialRevisionStatus::ACCEPTED) {
       switch ($local_vcs) {
         case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
           $bookmark = $this->getDiff()->getBookmark();
           $next_step = ($bookmark != ''
             ? csprintf('arc land %s', $bookmark)
             : 'arc land');
           break;
 
         case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
           $branch = $this->getDiff()->getBranch();
           $next_step = ($branch != ''
             ? csprintf('arc land %s', $branch)
             : 'arc land');
           break;
 
         case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
           $next_step = 'arc commit';
           break;
       }
     }
     if ($next_step) {
       $next_step = phutil_tag('tt', array(), $next_step);
       $properties->addProperty(pht('Next Step'), $next_step);
     }
 
     foreach ($this->auxiliaryFields as $field) {
       $value = $field->renderValueForRevisionView();
       if ($value !== null) {
         $label = rtrim($field->renderLabelForRevisionView(), ':');
         $properties->addProperty($label, $value);
       }
     }
     $properties->setHasKeyboardShortcuts(true);
     $properties->setActionList($actions);
 
     $properties->invokeWillRenderEvent();
 
     if (strlen($revision->getSummary())) {
       $properties->addSectionHeader(
         pht('Summary'),
         PHUIPropertyListView::ICON_SUMMARY);
       $properties->addTextContent(
         PhabricatorMarkupEngine::renderOneObject(
           id(new PhabricatorMarkupOneOff())
             ->setPreserveLinebreaks(true)
             ->setContent($revision->getSummary()),
           'default',
           $user));
     }
 
     if (strlen($revision->getTestPlan())) {
       $properties->addSectionHeader(
         pht('Test Plan'),
         PHUIPropertyListView::ICON_TESTPLAN);
       $properties->addTextContent(
         PhabricatorMarkupEngine::renderOneObject(
           id(new PhabricatorMarkupOneOff())
             ->setPreserveLinebreaks(true)
             ->setContent($revision->getTestPlan()),
           'default',
           $user));
     }
 
     $object_box = id(new PHUIObjectBoxView())
       ->setHeader($header)
       ->addPropertyList($properties);
 
     return $object_box;
   }
 
   private function renderHeader(DifferentialRevision $revision) {
     $view = id(new PHUIHeaderView())
       ->setHeader($revision->getTitle($revision))
       ->setUser($this->getUser())
       ->setPolicyObject($revision);
 
     $status = $revision->getStatus();
     $status_name =
       DifferentialRevisionStatus::renderFullDescription($status);
 
     $view->addProperty(PHUIHeaderView::PROPERTY_STATUS, $status_name);
 
     return $view;
   }
 
   public static function renderTagForRevision(
     DifferentialRevision $revision) {
 
     $status = $revision->getStatus();
     $status_name =
       ArcanistDifferentialRevisionStatus::getNameForRevisionStatus($status);
 
     return id(new PhabricatorTagView())
       ->setType(PhabricatorTagView::TYPE_STATE)
       ->setName($status_name);
   }
 
 }
diff --git a/src/applications/differential/view/DifferentialRevisionListView.php b/src/applications/differential/view/DifferentialRevisionListView.php
index 86bab4dbc6..0addfa5b62 100644
--- a/src/applications/differential/view/DifferentialRevisionListView.php
+++ b/src/applications/differential/view/DifferentialRevisionListView.php
@@ -1,253 +1,253 @@
 <?php
 
 /**
  * Render a table of Differential revisions.
  */
 final class DifferentialRevisionListView extends AphrontView {
 
   private $revisions;
   private $flags = array();
   private $drafts = array();
   private $handles;
   private $fields;
   private $highlightAge;
   private $header;
   private $noDataString;
 
   public function setNoDataString($no_data_string) {
     $this->noDataString = $no_data_string;
     return $this;
   }
 
   public function setHeader($header) {
     $this->header = $header;
     return $this;
   }
 
   public function setFields(array $fields) {
     assert_instances_of($fields, 'DifferentialFieldSpecification');
     $this->fields = $fields;
     return $this;
   }
 
   public function setRevisions(array $revisions) {
     assert_instances_of($revisions, 'DifferentialRevision');
     $this->revisions = $revisions;
     return $this;
   }
 
   public function setHighlightAge($bool) {
     $this->highlightAge = $bool;
     return $this;
   }
 
   public function getRequiredHandlePHIDs() {
     $phids = array();
     foreach ($this->fields as $field) {
       foreach ($this->revisions as $revision) {
         $phids[] = $field->getRequiredHandlePHIDsForRevisionList($revision);
       }
     }
     return array_mergev($phids);
   }
 
   public function setHandles(array $handles) {
     assert_instances_of($handles, 'PhabricatorObjectHandle');
     $this->handles = $handles;
     return $this;
   }
 
   public function loadAssets() {
     $user = $this->user;
     if (!$user) {
       throw new Exception("Call setUser() before loadAssets()!");
     }
     if ($this->revisions === null) {
       throw new Exception("Call setRevisions() before loadAssets()!");
     }
 
     $this->flags = id(new PhabricatorFlagQuery())
       ->setViewer($user)
       ->withOwnerPHIDs(array($user->getPHID()))
       ->withObjectPHIDs(mpull($this->revisions, 'getPHID'))
       ->execute();
 
     $this->drafts = id(new DifferentialRevisionQuery())
       ->setViewer($user)
       ->withIDs(mpull($this->revisions, 'getID'))
       ->withDraftRepliesByAuthors(array($user->getPHID()))
       ->execute();
 
     return $this;
   }
 
   public function render() {
 
     $user = $this->user;
     if (!$user) {
       throw new Exception("Call setUser() before render()!");
     }
 
     $fresh = PhabricatorEnv::getEnvConfig('differential.days-fresh');
     if ($fresh) {
       $fresh = PhabricatorCalendarHoliday::getNthBusinessDay(
         time(),
         -$fresh);
     }
 
     $stale = PhabricatorEnv::getEnvConfig('differential.days-stale');
     if ($stale) {
       $stale = PhabricatorCalendarHoliday::getNthBusinessDay(
         time(),
         -$stale);
     }
 
-    Javelin::initBehavior('phabricator-tooltips', array());
-    require_celerity_resource('aphront-tooltip-css');
+    $this->initBehavior('phabricator-tooltips', array());
+    $this->requireResource('aphront-tooltip-css');
 
     $flagged = mpull($this->flags, null, 'getObjectPHID');
 
     foreach ($this->fields as $field) {
       $field->setHandles($this->handles);
     }
 
     $list = new PHUIObjectItemListView();
     $list->setCards(true);
 
     foreach ($this->revisions as $revision) {
       $item = id(new PHUIObjectItemView())
         ->setUser($user);
 
       $rev_fields = array();
       $icons = array();
 
       $phid = $revision->getPHID();
       if (isset($flagged[$phid])) {
         $flag = $flagged[$phid];
         $flag_class = PhabricatorFlagColor::getCSSClass($flag->getColor());
         $icons['flag'] = phutil_tag(
           'div',
           array(
             'class' => 'phabricator-flag-icon '.$flag_class,
           ),
           '');
       }
       if (array_key_exists($revision->getID(), $this->drafts)) {
         $icons['draft'] = true;
       }
 
       $modified = $revision->getDateModified();
 
       $status = $revision->getStatus();
       $show_age = ($fresh || $stale) &&
                   $this->highlightAge &&
                   !$revision->isClosed();
 
       $object_age = PHUIObjectItemView::AGE_FRESH;
       foreach ($this->fields as $field) {
         if ($show_age) {
           if ($field instanceof DifferentialDateModifiedFieldSpecification) {
             if ($stale && $modified < $stale) {
               $object_age = PHUIObjectItemView::AGE_OLD;
             } else if ($fresh && $modified < $fresh) {
               $object_age = PHUIObjectItemView::AGE_STALE;
             }
           }
         }
 
         $rev_header = $field->renderHeaderForRevisionList();
         $rev_fields[$rev_header] = $field
           ->renderValueForRevisionList($revision);
       }
 
       $status_name =
         ArcanistDifferentialRevisionStatus::getNameForRevisionStatus($status);
 
       if (isset($icons['flag'])) {
         $item->addHeadIcon($icons['flag']);
       }
 
       $item->setObjectName('D'.$revision->getID());
       $item->setHeader(phutil_tag('a',
         array('href' => '/D'.$revision->getID()),
         $revision->getTitle()));
 
       if (isset($icons['draft'])) {
         $draft = id(new PHUIIconView())
           ->setSpriteSheet(PHUIIconView::SPRITE_ICONS)
           ->setSpriteIcon('file-grey')
           ->addSigil('has-tooltip')
           ->setMetadata(
             array(
               'tip' => pht('Unsubmitted Comments'),
             ));
         $item->addAttribute($draft);
       }
 
       $item->addAttribute($status_name);
 
       // Author
       $author_handle = $this->handles[$revision->getAuthorPHID()];
       $item->addByline(pht('Author: %s', $author_handle->renderLink()));
 
       // Reviewers
       $item->addAttribute(pht('Reviewers: %s', $rev_fields['Reviewers']));
 
       $item->setEpoch($revision->getDateModified(), $object_age);
 
       // First remove the fields we already have
       $count = 7;
       $rev_fields = array_slice($rev_fields, $count);
 
       // Then add each one of them
       // TODO: Add render-to-foot-icon support
       foreach ($rev_fields as $header => $field) {
         $item->addAttribute(pht('%s: %s', $header, $field));
       }
 
       switch ($status) {
         case ArcanistDifferentialRevisionStatus::NEEDS_REVIEW:
           break;
         case ArcanistDifferentialRevisionStatus::NEEDS_REVISION:
           $item->setBarColor('red');
           break;
         case ArcanistDifferentialRevisionStatus::ACCEPTED:
           $item->setBarColor('green');
           break;
         case ArcanistDifferentialRevisionStatus::CLOSED:
           $item->setDisabled(true);
           break;
         case ArcanistDifferentialRevisionStatus::ABANDONED:
           $item->setBarColor('black');
           break;
       }
 
       $list->addItem($item);
     }
 
     $list->setHeader($this->header);
     $list->setNoDataString($this->noDataString);
 
     return $list;
   }
 
   public static function getDefaultFields(PhabricatorUser $user) {
     $selector = DifferentialFieldSelector::newSelector();
     $fields = $selector->getFieldSpecifications();
     foreach ($fields as $key => $field) {
       $field->setUser($user);
       if (!$field->shouldAppearOnRevisionList()) {
         unset($fields[$key]);
       }
     }
 
     if (!$fields) {
       throw new Exception(
         "Phabricator configuration has no fields that appear on the list ".
         "interface!");
     }
 
     return $selector->sortFieldsForRevisionList($fields);
   }
 
 }
diff --git a/src/applications/differential/view/DifferentialRevisionUpdateHistoryView.php b/src/applications/differential/view/DifferentialRevisionUpdateHistoryView.php
index e4a78860df..5029a3bb44 100644
--- a/src/applications/differential/view/DifferentialRevisionUpdateHistoryView.php
+++ b/src/applications/differential/view/DifferentialRevisionUpdateHistoryView.php
@@ -1,329 +1,329 @@
 <?php
 
 final class DifferentialRevisionUpdateHistoryView extends AphrontView {
 
   private $diffs = array();
   private $selectedVersusDiffID;
   private $selectedDiffID;
   private $selectedWhitespace;
 
   public function setDiffs(array $diffs) {
     assert_instances_of($diffs, 'DifferentialDiff');
     $this->diffs = $diffs;
     return $this;
   }
 
   public function setSelectedVersusDiffID($id) {
     $this->selectedVersusDiffID = $id;
     return $this;
   }
 
   public function setSelectedDiffID($id) {
     $this->selectedDiffID = $id;
     return $this;
   }
 
   public function setSelectedWhitespace($whitespace) {
     $this->selectedWhitespace = $whitespace;
     return $this;
   }
 
   public function render() {
 
-    require_celerity_resource('differential-core-view-css');
-    require_celerity_resource('differential-revision-history-css');
+    $this->requireResource('differential-core-view-css');
+    $this->requireResource('differential-revision-history-css');
 
     $data = array(
       array(
         'name' => 'Base',
         'id'   => null,
         'desc' => 'Base',
         'age'  => null,
         'obj'  => null,
       ),
     );
 
     $seq = 0;
     foreach ($this->diffs as $diff) {
       $data[] = array(
         'name' => 'Diff '.(++$seq),
         'id'   => $diff->getID(),
         'desc' => $diff->getDescription(),
         'age'  => $diff->getDateCreated(),
         'obj'  => $diff,
       );
     }
 
     $max_id = $diff->getID();
 
     $idx = 0;
     $rows = array();
     $disable = false;
     $radios = array();
     $last_base = null;
     foreach ($data as $row) {
 
       $diff = $row['obj'];
       $name = $row['name'];
       $id   = $row['id'];
 
       $old_class = null;
       $new_class = null;
 
       if ($id) {
         $new_checked = ($this->selectedDiffID == $id);
         $new = javelin_tag(
           'input',
           array(
             'type' => 'radio',
             'name' => 'id',
             'value' => $id,
             'checked' => $new_checked ? 'checked' : null,
             'sigil' => 'differential-new-radio',
           ));
         if ($new_checked) {
           $new_class = " revhistory-new-now";
           $disable = true;
         }
       } else {
         $new = null;
       }
 
       if ($max_id != $id) {
         $uniq = celerity_generate_unique_node_id();
         $old_checked = ($this->selectedVersusDiffID == $id);
         $old = phutil_tag(
           'input',
           array(
             'type' => 'radio',
             'name' => 'vs',
             'value' => $id,
             'id' => $uniq,
             'checked' => $old_checked ? 'checked' : null,
             'disabled' => $disable ? 'disabled' : null,
           ));
         $radios[] = $uniq;
         if ($old_checked) {
           $old_class = " revhistory-old-now";
         }
       } else {
         $old = null;
       }
 
       $desc = $row['desc'];
 
       if ($row['age']) {
         $age = phabricator_datetime($row['age'], $this->getUser());
       } else {
         $age = null;
       }
 
       if (++$idx % 2) {
         $class = 'alt';
       } else {
         $class = null;
       }
 
       $lint_attrs = array('class' => 'revhistory-star');
       $unit_attrs = array('class' => 'revhistory-star');
       if ($diff) {
         $lint = self::renderDiffLintStar($row['obj']);
         $unit = self::renderDiffUnitStar($row['obj']);
         $lint_attrs['title'] = self::getDiffLintMessage($diff);
         $unit_attrs['title'] = self::getDiffUnitMessage($diff);
         $base = $this->renderBaseRevision($diff);
       } else {
         $lint = null;
         $unit = null;
         $base = null;
       }
 
       if ($last_base !== null && $base !== $last_base) {
         // TODO: Render some kind of notice about rebases.
       }
       $last_base = $base;
 
       $id_link = phutil_tag(
         'a',
         array('href' => '/differential/diff/'.$id.'/'),
         $id);
       $rows[] = phutil_tag(
         'tr',
         array('class' => $class),
         array(
           phutil_tag('td', array('class' => 'revhistory-name'), $name),
           phutil_tag('td', array('class' => 'revhistory-id'), $id_link),
           phutil_tag('td', array('class' => 'revhistory-base'), $base),
           phutil_tag('td', array('class' => 'revhistory-desc'), $desc),
           phutil_tag('td', array('class' => 'revhistory-age'), $age),
           phutil_tag('td', $lint_attrs, $lint),
           phutil_tag('td', $unit_attrs, $unit),
           phutil_tag('td', array('class' => 'revhistory-old'.$old_class), $old),
           phutil_tag('td', array('class' => 'revhistory-new'.$new_class), $new),
         ));
     }
 
     Javelin::initBehavior(
       'differential-diff-radios',
       array(
         'radios' => $radios,
       ));
 
     $options = array(
       DifferentialChangesetParser::WHITESPACE_IGNORE_FORCE => 'Ignore All',
       DifferentialChangesetParser::WHITESPACE_IGNORE_ALL => 'Ignore Most',
       DifferentialChangesetParser::WHITESPACE_IGNORE_TRAILING =>
         'Ignore Trailing',
       DifferentialChangesetParser::WHITESPACE_SHOW_ALL => 'Show All',
     );
 
     foreach ($options as $value => $label) {
       $options[$value] = phutil_tag(
         'option',
         array(
           'value' => $value,
           'selected' => ($value == $this->selectedWhitespace)
           ? 'selected'
           : null,
         ),
         $label);
     }
     $select = phutil_tag('select', array('name' => 'whitespace'), $options);
 
     array_unshift($rows, phutil_tag('tr', array(), array(
       phutil_tag('th', array(), pht('Diff')),
       phutil_tag('th', array(), pht('ID')),
       phutil_tag('th', array(), pht('Base')),
       phutil_tag('th', array(), pht('Description')),
       phutil_tag('th', array(), pht('Created')),
       phutil_tag('th', array(), pht('Lint')),
       phutil_tag('th', array(), pht('Unit')),
     )));
 
     $label = pht('Whitespace Changes: %s', $select);
 
     $content = phutil_tag_div(
       'differential-revision-history differential-panel',
       phutil_tag(
         'form',
         array('action' => '#toc'),
         phutil_tag(
           'table',
           array('class' => 'differential-revision-history-table'), array(
             phutil_implode_html("\n", $rows),
             phutil_tag('tr', array(), phutil_tag(
               'td',
               array('colspan' => 9, 'class' => 'diff-differ-submit'),
               array(
                 phutil_tag('label', array(), $label),
                 phutil_tag('button', array(), pht('Show Diff')),
               )))
           ))));
 
     return id(new PHUIObjectBoxView())
       ->setHeaderText(pht('Revision Update History'))
       ->appendChild($content);
   }
 
   const STAR_NONE = 'none';
   const STAR_OKAY = 'okay';
   const STAR_WARN = 'warn';
   const STAR_FAIL = 'fail';
   const STAR_SKIP = 'skip';
 
   public static function renderDiffLintStar(DifferentialDiff $diff) {
     static $map = array(
       DifferentialLintStatus::LINT_NONE => self::STAR_NONE,
       DifferentialLintStatus::LINT_OKAY => self::STAR_OKAY,
       DifferentialLintStatus::LINT_WARN => self::STAR_WARN,
       DifferentialLintStatus::LINT_FAIL => self::STAR_FAIL,
       DifferentialLintStatus::LINT_SKIP => self::STAR_SKIP,
       DifferentialLintStatus::LINT_POSTPONED => self::STAR_SKIP
     );
 
     $star = idx($map, $diff->getLintStatus(), self::STAR_FAIL);
 
     return self::renderDiffStar($star);
   }
 
   public static function renderDiffUnitStar(DifferentialDiff $diff) {
     static $map = array(
       DifferentialUnitStatus::UNIT_NONE => self::STAR_NONE,
       DifferentialUnitStatus::UNIT_OKAY => self::STAR_OKAY,
       DifferentialUnitStatus::UNIT_WARN => self::STAR_WARN,
       DifferentialUnitStatus::UNIT_FAIL => self::STAR_FAIL,
       DifferentialUnitStatus::UNIT_SKIP => self::STAR_SKIP,
       DifferentialUnitStatus::UNIT_POSTPONED => self::STAR_SKIP,
     );
 
     $star = idx($map, $diff->getUnitStatus(), self::STAR_FAIL);
 
     return self::renderDiffStar($star);
   }
 
   public static function getDiffLintMessage(DifferentialDiff $diff) {
     switch ($diff->getLintStatus()) {
       case DifferentialLintStatus::LINT_NONE:
         return 'No Linters Available';
       case DifferentialLintStatus::LINT_OKAY:
         return 'Lint OK';
       case DifferentialLintStatus::LINT_WARN:
         return 'Lint Warnings';
       case DifferentialLintStatus::LINT_FAIL:
         return 'Lint Errors';
       case DifferentialLintStatus::LINT_SKIP:
         return 'Lint Skipped';
       case DifferentialLintStatus::LINT_POSTPONED:
         return 'Lint Postponed';
     }
     return '???';
   }
 
   public static function getDiffUnitMessage(DifferentialDiff $diff) {
     switch ($diff->getUnitStatus()) {
       case DifferentialUnitStatus::UNIT_NONE:
         return 'No Unit Test Coverage';
       case DifferentialUnitStatus::UNIT_OKAY:
         return 'Unit Tests OK';
       case DifferentialUnitStatus::UNIT_WARN:
         return 'Unit Test Warnings';
       case DifferentialUnitStatus::UNIT_FAIL:
         return 'Unit Test Errors';
       case DifferentialUnitStatus::UNIT_SKIP:
         return 'Unit Tests Skipped';
       case DifferentialUnitStatus::UNIT_POSTPONED:
         return 'Unit Tests Postponed';
     }
     return '???';
   }
 
   private static function renderDiffStar($star) {
     $class = 'diff-star-'.$star;
     return phutil_tag(
       'span',
       array('class' => $class),
       "\xE2\x98\x85");
   }
 
   private function renderBaseRevision(DifferentialDiff $diff) {
     switch ($diff->getSourceControlSystem()) {
       case 'git':
         $base = $diff->getSourceControlBaseRevision();
         if (strpos($base, '@') === false) {
           return substr($base, 0, 7);
         } else {
           // The diff is from git-svn
           $base = explode('@', $base);
           $base = last($base);
           return $base;
         }
       case 'svn':
         $base = $diff->getSourceControlBaseRevision();
         $base = explode('@', $base);
         $base = last($base);
         return $base;
       default:
         return null;
     }
   }
 }
diff --git a/src/infrastructure/celerity/CelerityResourceMap.php b/src/infrastructure/celerity/CelerityResourceMap.php
index ebaa4fb9a6..c9beffc2d3 100644
--- a/src/infrastructure/celerity/CelerityResourceMap.php
+++ b/src/infrastructure/celerity/CelerityResourceMap.php
@@ -1,239 +1,243 @@
 <?php
 
 /**
  * Interface to the static resource map, which is a graph of available
  * resources, resource dependencies, and packaging information. You generally do
  * not need to invoke it directly; instead, you call higher-level Celerity APIs
  * and it uses the resource map to satisfy your requests.
  */
 final class CelerityResourceMap {
 
   private static $instances = array();
 
   private $resources;
   private $symbolMap;
   private $requiresMap;
   private $packageMap;
   private $nameMap;
   private $hashMap;
 
   public function __construct(CelerityResources $resources) {
     $this->resources = $resources;
 
     $map = $resources->loadMap();
     $this->symbolMap = idx($map, 'symbols', array());
     $this->requiresMap = idx($map, 'requires', array());
     $this->packageMap = idx($map, 'packages', array());
     $this->nameMap = idx($map, 'names', array());
 
     // We derive these reverse maps at runtime.
 
     $this->hashMap = array_flip($this->nameMap);
     $this->componentMap = array();
     foreach ($this->packageMap as $package_name => $symbols) {
       foreach ($symbols as $symbol) {
         $this->componentMap[$symbol] = $package_name;
       }
     }
   }
 
   public static function getNamedInstance($name) {
     if (empty(self::$instances[$name])) {
       $resources_list = CelerityPhysicalResources::getAll();
       if (empty($resources_list[$name])) {
         throw new Exception(
           pht(
             'No resource source exists with name "%s"!', $name));
       }
 
       $instance = new CelerityResourceMap($resources_list[$name]);
       self::$instances[$name] = $instance;
     }
 
     return self::$instances[$name];
   }
 
   public function getPackagedNamesForSymbols(array $symbols) {
     $resolved = $this->resolveResources($symbols);
     return $this->packageResources($resolved);
   }
 
   private function resolveResources(array $symbols) {
     $map = array();
     foreach ($symbols as $symbol) {
       if (!empty($map[$symbol])) {
         continue;
       }
       $this->resolveResource($map, $symbol);
     }
 
     return $map;
   }
 
   private function resolveResource(array &$map, $symbol) {
     if (empty($this->symbolMap[$symbol])) {
       throw new Exception(
         pht(
           'Attempting to resolve unknown resource, "%s".',
           $symbol));
     }
 
     $hash = $this->symbolMap[$symbol];
 
     $map[$symbol] = $hash;
 
     if (isset($this->requiresMap[$hash])) {
       $requires = $this->requiresMap[$hash];
     } else {
       $requires = array();
     }
 
     foreach ($requires as $required_symbol) {
       if (!empty($map[$required_symbol])) {
         continue;
       }
       $this->resolveResource($map, $required_symbol);
     }
   }
 
   private function packageResources(array $resolved_map) {
     $packaged = array();
     $handled = array();
     foreach ($resolved_map as $symbol => $hash) {
       if (isset($handled[$symbol])) {
         continue;
       }
 
       if (empty($this->componentMap[$symbol])) {
         $packaged[] = $this->hashMap[$hash];
       } else {
         $package_name = $this->componentMap[$symbol];
         $packaged[] = $package_name;
 
         $package_symbols = $this->packageMap[$package_name];
         foreach ($package_symbols as $package_symbol) {
           $handled[$package_symbol] = true;
         }
       }
     }
 
     return $packaged;
   }
 
   public function getResourceDataForName($resource_name) {
     return $this->resources->getResourceData($resource_name);
   }
 
   public function getResourceNamesForPackageName($package_name) {
     $package_symbols = idx($this->packageMap, $package_name);
     if (!$package_symbols) {
       return null;
     }
 
     $resource_names = array();
     foreach ($package_symbols as $symbol) {
       $resource_names[] = $this->hashMap[$this->symbolMap[$symbol]];
     }
 
     return $resource_names;
   }
 
 
   /**
    * Get the epoch timestamp of the last modification time of a symbol.
    *
    * @param string Resource symbol to lookup.
    * @return int Epoch timestamp of last resource modification.
    */
   public function getModifiedTimeForName($name) {
     if ($this->isPackageResource($name)) {
       $names = array();
       foreach ($this->packageMap[$name] as $symbol) {
         $names[] = $this->getResourceNameForSymbol($symbol);
       }
     } else {
       $names = array($name);
     }
 
     $mtime = 0;
     foreach ($names as $name) {
       $mtime = max($mtime, $this->resources->getResourceModifiedTime($name));
     }
 
     return $mtime;
   }
 
 
   /**
    * Return the absolute URI for the resource associated with a symbol. This
    * method is fairly low-level and ignores packaging.
    *
    * @param string Resource symbol to lookup.
    * @return string|null Resource URI, or null if the symbol is unknown.
    */
   public function getURIForSymbol($symbol) {
     $hash = idx($this->symbolMap, $symbol);
     return $this->getURIForHash($hash);
   }
 
 
   /**
    * Return the absolute URI for the resource associated with a resource name.
    * This method is fairly low-level and ignores packaging.
    *
    * @param string Resource name to lookup.
    * @return string|null  Resource URI, or null if the name is unknown.
    */
   public function getURIForName($name) {
     $hash = idx($this->nameMap, $name);
     return $this->getURIForHash($hash);
   }
 
 
   /**
    * Return the absolute URI for a resource, identified by hash.
    * This method is fairly low-level and ignores packaging.
    *
    * @param string Resource hash to lookup.
    * @return string|null Resource URI, or null if the hash is unknown.
    */
   private function getURIForHash($hash) {
     if ($hash === null) {
       return null;
     }
     return $this->resources->getResourceURI($hash, $this->hashMap[$hash]);
   }
 
 
   /**
    * Return the resource symbols required by a named resource.
    *
    * @param string Resource name to lookup.
    * @return list<string>|null  List of required symbols, or null if the name
    *                            is unknown.
    */
   public function getRequiredSymbolsForName($name) {
     $hash = idx($this->symbolMap, $name);
     if ($hash === null) {
       return null;
     }
     return idx($this->requiresMap, $hash, array());
   }
 
 
   /**
    * Return the resource name for a given symbol.
    *
    * @param string Resource symbol to lookup.
    * @return string|null Resource name, or null if the symbol is unknown.
    */
   public function getResourceNameForSymbol($symbol) {
     $hash = idx($this->symbolMap, $symbol);
     return idx($this->hashMap, $hash);
   }
 
   public function isPackageResource($name) {
     return isset($this->packageMap[$name]);
   }
 
+  public function getResourceTypeForName($name) {
+    return $this->resources->getResourceType($name);
+  }
+
 }
diff --git a/src/infrastructure/celerity/CelerityStaticResourceResponse.php b/src/infrastructure/celerity/CelerityStaticResourceResponse.php
index b52274f323..21ad591e87 100644
--- a/src/infrastructure/celerity/CelerityStaticResourceResponse.php
+++ b/src/infrastructure/celerity/CelerityStaticResourceResponse.php
@@ -1,256 +1,304 @@
 <?php
 
 /**
  * Tracks and resolves dependencies the page declares with
  * @{function:require_celerity_resource}, and then builds appropriate HTML or
  * Ajax responses.
  *
  * @group celerity
  */
 final class CelerityStaticResourceResponse {
 
   private $symbols = array();
   private $needsResolve = true;
   private $resolved;
   private $packaged;
   private $metadata = array();
   private $metadataBlock = 0;
   private $behaviors = array();
   private $hasRendered = array();
 
   public function __construct() {
     if (isset($_REQUEST['__metablock__'])) {
       $this->metadataBlock = (int)$_REQUEST['__metablock__'];
     }
   }
 
   public function addMetadata($metadata) {
     $id = count($this->metadata);
     $this->metadata[$id] = $metadata;
     return $this->metadataBlock.'_'.$id;
   }
 
   public function getMetadataBlock() {
     return $this->metadataBlock;
   }
 
   /**
    * Register a behavior for initialization. NOTE: if $config is empty,
    * a behavior will execute only once even if it is initialized multiple times.
    * If $config is nonempty, the behavior will be invoked once for each config.
    */
-  public function initBehavior($behavior, array $config = array()) {
-    $this->requireResource('javelin-behavior-'.$behavior);
+  public function initBehavior(
+    $behavior,
+    array $config = array(),
+    $source_name = 'phabricator') {
+
+    $this->requireResource('javelin-behavior-'.$behavior, $source_name);
 
     if (empty($this->behaviors[$behavior])) {
       $this->behaviors[$behavior] = array();
     }
 
     if ($config) {
       $this->behaviors[$behavior][] = $config;
     }
 
     return $this;
   }
 
-  public function requireResource($symbol) {
-    $this->symbols[$symbol] = true;
+  public function requireResource($symbol, $source_name) {
+    if (isset($this->symbols[$source_name][$symbol])) {
+      return $this;
+    }
+
+    // Verify that the resource exists.
+    $map = CelerityResourceMap::getNamedInstance($source_name);
+    $name = $map->getResourceNameForSymbol($symbol);
+    if ($name === null) {
+      throw new Exception(
+        pht(
+          'No resource with symbol "%s" exists in source "%s"!',
+          $symbol,
+          $source_name));
+    }
+
+    $this->symbols[$source_name][$symbol] = true;
     $this->needsResolve = true;
+
     return $this;
   }
 
   private function resolveResources() {
     if ($this->needsResolve) {
-      $map = CelerityResourceMap::getNamedInstance('phabricator');
+      $this->packaged = array();
+      foreach ($this->symbols as $source_name => $symbols_map) {
+        $symbols = array_keys($symbols_map);
 
-      $symbols = array_keys($this->symbols);
-      $this->packaged = $map->getPackagedNamesForSymbols($symbols);
+        $map = CelerityResourceMap::getNamedInstance($source_name);
+        $packaged = $map->getPackagedNamesForSymbols($symbols);
 
+        $this->packaged[$source_name] = $packaged;
+      }
       $this->needsResolve = false;
     }
     return $this;
   }
 
   public function renderSingleResource($symbol, $source_name) {
     $map = CelerityResourceMap::getNamedInstance($source_name);
     $packaged = $map->getPackagedNamesForSymbols(array($symbol));
-    return $this->renderPackagedResources($packaged);
+    return $this->renderPackagedResources($map, $packaged);
   }
 
   public function renderResourcesOfType($type) {
     $this->resolveResources();
 
-    $resources = array();
-    foreach ($this->packaged as $name) {
-      $resource_type = CelerityResourceTransformer::getResourceType($name);
-      if ($resource_type == $type) {
-        $resources[] = $name;
+    $result = array();
+    foreach ($this->packaged as $source_name => $resource_names) {
+      $map = CelerityResourceMap::getNamedInstance($source_name);
+
+      $resources_of_type = array();
+      foreach ($resource_names as $resource_name) {
+        $resource_type = $map->getResourceTypeForName($resource_name);
+        if ($resource_type == $type) {
+          $resources_of_type[] = $resource_name;
+        }
       }
+
+      $result[] = $this->renderPackagedResources($map, $resources_of_type);
     }
 
-    return $this->renderPackagedResources($resources);
+    return phutil_implode_html('', $result);
   }
 
-  private function renderPackagedResources(array $resources) {
+  private function renderPackagedResources(
+    CelerityResourceMap $map,
+    array $resources) {
+
     $output = array();
     foreach ($resources as $name) {
       if (isset($this->hasRendered[$name])) {
         continue;
       }
       $this->hasRendered[$name] = true;
 
-      $output[] = $this->renderResource($name);
-      $output[] = "\n";
+      $output[] = $this->renderResource($map, $name);
     }
-    return phutil_implode_html('', $output);
+
+    return $output;
   }
 
-  private function renderResource($name) {
-    $uri = $this->getURI($name);
-    $type = CelerityResourceTransformer::getResourceType($name);
+  private function renderResource(
+    CelerityResourceMap $map,
+    $name) {
+
+    $uri = $this->getURI($map, $name);
+    $type = $map->getResourceTypeForName($name);
+
     switch ($type) {
       case 'css':
         return phutil_tag(
           'link',
           array(
             'rel'   => 'stylesheet',
             'type'  => 'text/css',
             'href'  => $uri,
           ));
       case 'js':
         return phutil_tag(
           'script',
           array(
             'type'  => 'text/javascript',
             'src'   => $uri,
           ),
           '');
     }
-    throw new Exception("Unable to render resource.");
+
+    throw new Exception(
+      pht(
+        'Unable to render resource "%s", which has unknown type "%s".',
+        $name,
+        $type));
   }
 
   public function renderHTMLFooter() {
     $data = array();
     if ($this->metadata) {
       $json_metadata = AphrontResponse::encodeJSONForHTTPResponse(
         $this->metadata);
       $this->metadata = array();
     } else {
       $json_metadata = '{}';
     }
     // Even if there is no metadata on the page, Javelin uses the mergeData()
     // call to start dispatching the event queue.
     $data[] = 'JX.Stratcom.mergeData('.$this->metadataBlock.', '.
                                        $json_metadata.');';
 
     $onload = array();
     if ($this->behaviors) {
       $behaviors = $this->behaviors;
       $this->behaviors = array();
 
       $higher_priority_names = array(
         'refresh-csrf',
         'aphront-basic-tokenizer',
         'dark-console',
         'history-install',
       );
 
       $higher_priority_behaviors = array_select_keys(
         $behaviors,
         $higher_priority_names);
 
       foreach ($higher_priority_names as $name) {
         unset($behaviors[$name]);
       }
 
       $behavior_groups = array(
         $higher_priority_behaviors,
         $behaviors);
 
       foreach ($behavior_groups as $group) {
         if (!$group) {
           continue;
         }
         $group_json = AphrontResponse::encodeJSONForHTTPResponse(
           $group);
         $onload[] = 'JX.initBehaviors('.$group_json.')';
       }
     }
 
     if ($onload) {
       foreach ($onload as $func) {
         $data[] = 'JX.onload(function(){'.$func.'});';
       }
     }
 
     if ($data) {
       $data = implode("\n", $data);
       return self::renderInlineScript($data);
     } else {
       return '';
     }
   }
 
   public static function renderInlineScript($data) {
     if (stripos($data, '</script>') !== false) {
       throw new Exception(
         'Literal </script> is not allowed inside inline script.');
     }
     if (strpos($data, '<!') !== false) {
       throw new Exception('Literal <! is not allowed inside inline script.');
     }
     // We don't use <![CDATA[ ]]> because it is ignored by HTML parsers. We
     // would need to send the document with XHTML content type.
     return phutil_tag(
       'script',
       array('type' => 'text/javascript'),
       phutil_safe_html($data));
   }
 
   public function buildAjaxResponse($payload, $error = null) {
     $response = array(
       'error'   => $error,
       'payload' => $payload,
     );
 
     if ($this->metadata) {
       $response['javelin_metadata'] = $this->metadata;
       $this->metadata = array();
     }
 
     if ($this->behaviors) {
       $response['javelin_behaviors'] = $this->behaviors;
       $this->behaviors = array();
     }
 
     $this->resolveResources();
     $resources = array();
-    foreach ($this->packaged as $resource) {
-      $resources[] = $this->getURI($resource);
+    foreach ($this->packaged as $source_name => $resource_names) {
+      $map = CelerityResourceMap::getNamedInstance($source_name);
+      foreach ($resource_names as $resource_name) {
+        $resources[] = $this->getURI($map, $resource_name);
+      }
     }
     if ($resources) {
       $response['javelin_resources'] = $resources;
     }
 
     return $response;
   }
 
-  private function getURI($name) {
-    $map = CelerityResourceMap::getNamedInstance('phabricator');
+  private function getURI(
+    CelerityResourceMap $map,
+    $name) {
+
     $uri = $map->getURIForName($name);
 
     // In developer mode, we dump file modification times into the URI. When a
     // page is reloaded in the browser, any resources brought in by Ajax calls
     // do not trigger revalidation, so without this it's very difficult to get
     // changes to Ajaxed-in CSS to work (you must clear your cache or rerun
     // the map script). In production, we can assume the map script gets run
     // after changes, and safely skip this.
     if (PhabricatorEnv::getEnvConfig('phabricator.developer-mode')) {
       $mtime = $map->getModifiedTimeForName($name);
       $uri = preg_replace('@^/res/@', '/res/'.$mtime.'T/', $uri);
     }
 
     return PhabricatorEnv::getCDNURI($uri);
   }
 
 }
diff --git a/src/infrastructure/celerity/api.php b/src/infrastructure/celerity/api.php
index 8b04b49fd3..c10b7a1d40 100644
--- a/src/infrastructure/celerity/api.php
+++ b/src/infrastructure/celerity/api.php
@@ -1,61 +1,61 @@
 <?php
 
 /**
  * Include a CSS or JS static resource by name. This function records a
  * dependency for the current page, so when a response is generated it can be
  * included. You can call this method from any context, and it is recommended
  * you invoke it as close to the actual dependency as possible so that page
  * dependencies are minimized.
  *
  * For more information, see @{article:Adding New CSS and JS}.
  *
  * @param string Name of the celerity module to include. This is whatever you
  *               annotated as "@provides" in the file.
  * @return void
  *
  * @group celerity
  */
-function require_celerity_resource($symbol) {
+function require_celerity_resource($symbol, $source_name = 'phabricator') {
   $response = CelerityAPI::getStaticResourceResponse();
-  $response->requireResource($symbol);
+  $response->requireResource($symbol, $source_name);
 }
 
 
 /**
  * Generate a node ID which is guaranteed to be unique for the current page,
  * even across Ajax requests. You should use this method to generate IDs for
  * nodes which require a uniqueness guarantee.
  *
  * @return string A string appropriate for use as an 'id' attribute on a DOM
  *                node. It is guaranteed to be unique for the current page, even
  *                if the current request is a subsequent Ajax request.
  *
  * @group celerity
  */
 function celerity_generate_unique_node_id() {
   static $uniq = 0;
   $response = CelerityAPI::getStaticResourceResponse();
   $block = $response->getMetadataBlock();
 
   return 'UQ'.$block.'_'.($uniq++);
 }
 
 
 /**
  * Get the versioned URI for a raw resource, like an image.
  *
  * @param   string  Path to the raw image.
  * @return  string  Versioned path to the image, if one is available.
  *
  * @group celerity
  */
 function celerity_get_resource_uri($resource, $source = 'phabricator') {
   $map = CelerityResourceMap::getNamedInstance($source);
 
   $uri = $map->getURIForName($resource);
   if ($uri) {
     return $uri;
   }
 
   return $resource;
 }
diff --git a/src/view/AphrontView.php b/src/view/AphrontView.php
index 33bddb701e..788ea50ab3 100644
--- a/src/view/AphrontView.php
+++ b/src/view/AphrontView.php
@@ -1,145 +1,162 @@
 <?php
 
 /**
  * @task children   Managing Children
  */
 abstract class AphrontView extends Phobject
   implements PhutilSafeHTMLProducerInterface {
 
   protected $user;
   protected $children = array();
 
 
 /* -(  Configuration  )------------------------------------------------------ */
 
 
   /**
    * @task config
    */
   public function setUser(PhabricatorUser $user) {
     $this->user = $user;
     return $this;
   }
 
 
   /**
    * @task config
    */
   protected function getUser() {
     return $this->user;
   }
 
 
 /* -(  Managing Children  )-------------------------------------------------- */
 
 
   /**
    * Test if this View accepts children.
    *
    * By default, views accept children, but subclases may override this method
    * to prevent children from being appended. Doing so will cause
    * @{method:appendChild} to throw exceptions instead of appending children.
    *
    * @return bool   True if the View should accept children.
    * @task children
    */
   protected function canAppendChild() {
     return true;
   }
 
 
   /**
    * Append a child to the list of children.
    *
    * This method will only work if the view supports children, which is
    * determined by @{method:canAppendChild}.
    *
    * @param  wild   Something renderable.
    * @return this
    */
   final public function appendChild($child) {
     if (!$this->canAppendChild()) {
       $class = get_class($this);
       throw new Exception(
         pht("View '%s' does not support children.", $class));
     }
 
     $this->children[] = $child;
 
     return $this;
   }
 
 
   /**
    * Produce children for rendering.
    *
    * Historically, this method reduced children to a string representation,
    * but it no longer does.
    *
    * @return wild Renderable children.
    * @task
    */
   final protected function renderChildren() {
     return $this->children;
   }
 
 
   /**
    * Test if an element has no children.
    *
    * @return bool True if this element has children.
    * @task children
    */
   final public function hasChildren() {
     if ($this->children) {
       $this->children = $this->reduceChildren($this->children);
     }
     return (bool)$this->children;
   }
 
 
   /**
    * Reduce effectively-empty lists of children to be actually empty. This
    * recursively removes `null`, `''`, and `array()` from the list of children
    * so that @{method:hasChildren} can more effectively align with expectations.
    *
    * NOTE: Because View children are not rendered, a View which renders down
    * to nothing will not be reduced by this method.
    *
    * @param   list<wild>  Renderable children.
    * @return  list<wild>  Reduced list of children.
    * @task children
    */
   private function reduceChildren(array $children) {
     foreach ($children as $key => $child) {
       if ($child === null) {
         unset($children[$key]);
       } else if ($child === '') {
         unset($children[$key]);
       } else if (is_array($child)) {
         $child = $this->reduceChildren($child);
         if ($child) {
           $children[$key] = $child;
         } else {
           unset($children[$key]);
         }
       }
     }
     return $children;
   }
 
+  public function getDefaultResourceSource() {
+    return 'phabricator';
+  }
+
+  public function requireResource($symbol) {
+    $response = CelerityAPI::getStaticResourceResponse();
+    $response->requireResource($symbol, $this->getDefaultResourceSource());
+    return $this;
+  }
+
+  public function initBehavior($name, $config = array()) {
+    Javelin::initBehavior(
+      $name,
+      $config,
+      $this->getDefaultResourceSource());
+  }
+
 
 /* -(  Rendering  )---------------------------------------------------------- */
 
 
   abstract public function render();
 
 
 /* -(  PhutilSafeHTMLProducerInterface  )------------------------------------ */
 
 
   public function producePhutilSafeHTML() {
     return $this->render();
   }
 
 }