diff --git a/externals/javelin b/externals/javelin index e919289619..14e180d5d3 160000 --- a/externals/javelin +++ b/externals/javelin @@ -1 +1 @@ -Subproject commit e91928961926c1a81247cd9d7bdcdee251f18cfa +Subproject commit 14e180d5d316d496886730cb6a41d1b36de16ec1 diff --git a/src/__celerity_resource_map__.php b/src/__celerity_resource_map__.php index c128030f52..6667ce0c5c 100644 --- a/src/__celerity_resource_map__.php +++ b/src/__celerity_resource_map__.php @@ -357,7 +357,7 @@ celerity_register_resource_map(array( ), 'javelin-behavior-aphront-basic-tokenizer' => array( - 'uri' => '/res/9be30797/rsrc/js/application/core/behavior-tokenizer.js', + 'uri' => '/res/69a085d3/rsrc/js/application/core/behavior-tokenizer.js', 'type' => 'js', 'requires' => array( @@ -368,6 +368,7 @@ celerity_register_resource_map(array( 4 => 'javelin-typeahead-ondemand-source', 5 => 'javelin-dom', 6 => 'javelin-stratcom', + 7 => 'javelin-util', ), 'disk' => '/rsrc/js/application/core/behavior-tokenizer.js', ), @@ -2007,6 +2008,22 @@ celerity_register_resource_map(array( 'uri' => '/res/pkg/2e291441/differential.pkg.js', 'type' => 'js', ), + '3e7cc9b3' => + array( + 'name' => 'typeahead.pkg.js', + 'symbols' => + array( + 0 => 'javelin-typeahead', + 1 => 'javelin-typeahead-normalizer', + 2 => 'javelin-typeahead-source', + 3 => 'javelin-typeahead-preloaded-source', + 4 => 'javelin-typeahead-ondemand-source', + 5 => 'javelin-tokenizer', + 6 => 'javelin-behavior-aphront-basic-tokenizer', + ), + 'uri' => '/res/pkg/3e7cc9b3/typeahead.pkg.js', + 'type' => 'js', + ), '4fbae2af' => array( 'name' => 'javelin.pkg.js', @@ -2036,22 +2053,6 @@ celerity_register_resource_map(array( 'uri' => '/res/pkg/61f9d480/diffusion.pkg.css', 'type' => 'css', ), - 'ae7ea233' => - array( - 'name' => 'typeahead.pkg.js', - 'symbols' => - array( - 0 => 'javelin-typeahead', - 1 => 'javelin-typeahead-normalizer', - 2 => 'javelin-typeahead-source', - 3 => 'javelin-typeahead-preloaded-source', - 4 => 'javelin-typeahead-ondemand-source', - 5 => 'javelin-tokenizer', - 6 => 'javelin-behavior-aphront-basic-tokenizer', - ), - 'uri' => '/res/pkg/ae7ea233/typeahead.pkg.js', - 'type' => 'js', - ), 31583232 => array( 'name' => 'maniphest.pkg.css', @@ -2114,7 +2115,7 @@ celerity_register_resource_map(array( 'differential-table-of-contents-css' => '09c86840', 'diffusion-commit-view-css' => '61f9d480', 'javelin-behavior' => '4fbae2af', - 'javelin-behavior-aphront-basic-tokenizer' => 'ae7ea233', + 'javelin-behavior-aphront-basic-tokenizer' => '3e7cc9b3', 'javelin-behavior-aphront-drag-and-drop' => '2e291441', 'javelin-behavior-aphront-drag-and-drop-textarea' => '2e291441', 'javelin-behavior-aphront-form-disable-on-submit' => '95944588', @@ -2146,12 +2147,12 @@ celerity_register_resource_map(array( 'javelin-mask' => '95944588', 'javelin-request' => '4fbae2af', 'javelin-stratcom' => '4fbae2af', - 'javelin-tokenizer' => 'ae7ea233', - 'javelin-typeahead' => 'ae7ea233', - 'javelin-typeahead-normalizer' => 'ae7ea233', - 'javelin-typeahead-ondemand-source' => 'ae7ea233', - 'javelin-typeahead-preloaded-source' => 'ae7ea233', - 'javelin-typeahead-source' => 'ae7ea233', + 'javelin-tokenizer' => '3e7cc9b3', + 'javelin-typeahead' => '3e7cc9b3', + 'javelin-typeahead-normalizer' => '3e7cc9b3', + 'javelin-typeahead-ondemand-source' => '3e7cc9b3', + 'javelin-typeahead-preloaded-source' => '3e7cc9b3', + 'javelin-typeahead-source' => '3e7cc9b3', 'javelin-uri' => '4fbae2af', 'javelin-util' => '4fbae2af', 'javelin-vector' => '4fbae2af', diff --git a/src/applications/differential/field/specification/ccs/DifferentialCCsFieldSpecification.php b/src/applications/differential/field/specification/ccs/DifferentialCCsFieldSpecification.php index 55a62bacb3..4ee59b9dc2 100644 --- a/src/applications/differential/field/specification/ccs/DifferentialCCsFieldSpecification.php +++ b/src/applications/differential/field/specification/ccs/DifferentialCCsFieldSpecification.php @@ -1,7 +1,7 @@ setLabel('CC') ->setName('cc') + ->setUser($this->getUser()) ->setDatasource('/typeahead/common/mailable/') ->setValue($cc_map); } diff --git a/src/applications/differential/field/specification/reviewers/DifferentialReviewersFieldSpecification.php b/src/applications/differential/field/specification/reviewers/DifferentialReviewersFieldSpecification.php index 91d3908477..a4f7ad2144 100644 --- a/src/applications/differential/field/specification/reviewers/DifferentialReviewersFieldSpecification.php +++ b/src/applications/differential/field/specification/reviewers/DifferentialReviewersFieldSpecification.php @@ -86,6 +86,7 @@ final class DifferentialReviewersFieldSpecification return id(new AphrontFormTokenizerControl()) ->setLabel('Reviewers') ->setName('reviewers') + ->setUser($this->getUser()) ->setDatasource('/typeahead/common/users/') ->setValue($reviewer_map) ->setError($this->error); diff --git a/src/applications/typeahead/controller/common/PhabricatorTypeaheadCommonDatasourceController.php b/src/applications/typeahead/controller/common/PhabricatorTypeaheadCommonDatasourceController.php index 86c67d5299..a19a4551d2 100644 --- a/src/applications/typeahead/controller/common/PhabricatorTypeaheadCommonDatasourceController.php +++ b/src/applications/typeahead/controller/common/PhabricatorTypeaheadCommonDatasourceController.php @@ -129,6 +129,7 @@ class PhabricatorTypeaheadCommonDatasourceController $user->getUsername().' ('.$user->getRealName().')', '/p/'.$user->getUsername(), $user->getPHID(), + $user->getUsername(), ); } } @@ -162,6 +163,7 @@ class PhabricatorTypeaheadCommonDatasourceController 'r'.$repo->getCallsign().' ('.$repo->getName().')', '/diffusion/'.$repo->getCallsign().'/', $repo->getPHID(), + 'r'.$repo->getCallsign(), ); } } diff --git a/src/view/form/control/tokenizer/AphrontFormTokenizerControl.php b/src/view/form/control/tokenizer/AphrontFormTokenizerControl.php index c45e768383..bef0b2827c 100644 --- a/src/view/form/control/tokenizer/AphrontFormTokenizerControl.php +++ b/src/view/form/control/tokenizer/AphrontFormTokenizerControl.php @@ -1,7 +1,7 @@ datasource = $datasource; @@ -41,6 +42,11 @@ class AphrontFormTokenizerControl extends AphrontFormControl { return $this; } + public function setUser($user) { + $this->user = $user; + return $this; + } + protected function renderInput() { $name = $this->getName(); $values = nonempty($this->getValue(), array()); @@ -56,6 +62,11 @@ class AphrontFormTokenizerControl extends AphrontFormControl { $template->setID($id); $template->setValue($values); + $username = null; + if ($this->user) { + $username = $this->user->getUsername(); + } + if (!$this->disableBehavior) { Javelin::initBehavior('aphront-basic-tokenizer', array( 'id' => $id, @@ -63,6 +74,7 @@ class AphrontFormTokenizerControl extends AphrontFormControl { 'value' => $values, 'limit' => $this->limit, 'ondemand' => PhabricatorEnv::getEnvConfig('tokenizer.ondemand'), + 'username' => $username, )); } diff --git a/webroot/rsrc/js/application/core/behavior-tokenizer.js b/webroot/rsrc/js/application/core/behavior-tokenizer.js index a33b6b8673..fe02ac4a67 100644 --- a/webroot/rsrc/js/application/core/behavior-tokenizer.js +++ b/webroot/rsrc/js/application/core/behavior-tokenizer.js @@ -7,6 +7,7 @@ * javelin-typeahead-ondemand-source * javelin-dom * javelin-stratcom + * javelin-util */ JX.behavior('aphront-basic-tokenizer', function(config) { @@ -19,6 +20,57 @@ JX.behavior('aphront-basic-tokenizer', function(config) { datasource = new JX.TypeaheadPreloadedSource(config.src); } + // Sort results so that the viewing user always comes up first; after that, + // prefer unixname matches to realname matches. + + var sort_handler = function(value, list, cmp) { + var priority_hits = {}; + var self_hits = {}; + + var tokens = this.tokenize(value); + + for (var ii = 0; ii < list.length; ii++) { + var item = list[ii]; + if (!item.priority) { + continue; + } + + if (config.username && item.priority == config.username) { + self_hits[item.id] = true; + } + + for (var jj = 0; jj < tokens.length; jj++) { + if (item.priority.substr(0, tokens[jj].length) == tokens[jj]) { + priority_hits[item.id] = true; + } + } + } + + list.sort(function(u, v) { + if (self_hits[u.id] != self_hits[v.id]) { + return self_hits[v.id] ? 1 : -1; + } + + if (priority_hits[u.id] != priority_hits[v.id]) { + return priority_hits[v.id] ? 1 : -1; + } + + return cmp(u, v); + }); + }; + + datasource.setSortHandler(JX.bind(datasource, sort_handler)); + datasource.setTransformer( + function(object) { + return { + name : object[0], + display : object[0], + uri : object[1], + id : object[2], + priority : object[3] + }; + }); + var typeahead = new JX.Typeahead( root, JX.DOM.find(root, 'input', 'tokenizer-input')); @@ -38,4 +90,5 @@ JX.behavior('aphront-basic-tokenizer', function(config) { JX.Stratcom.addData(root, {'tokenizer' : tokenizer}); tokenizer.start(); + });