1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-11-22 23:02:42 +01:00

Use a tokenizer, not a gigantic poorly-ordered "<select />", to choose repositories in Owners

Summary: Depends on D19190. Fixes T12590. Ref T13099. Replaces the barely-usable, gigantic, poorly ordered "<select />" control with a tokenizer. Attempts to fix various minor issues.

Test Plan:
  - Edited paths: include/exclude paths, from different repositories, different actual paths.
  - Used "Add New Path" to add rows, got repository selector prepopulated with last value.
  - Used "remove".
  - Used validation typeahead, got reasonable behaviors?

The error behavior if you delete the repository for a path is a little sketchy still, but roughly okay.

Maniphest Tasks: T13099, T12590

Differential Revision: https://secure.phabricator.com/D19191
This commit is contained in:
epriestley 2018-03-07 17:29:06 -08:00
parent b41a0e6ddd
commit a4cc1373d3
8 changed files with 310 additions and 198 deletions

View file

@ -10,7 +10,7 @@ return array(
'conpherence.pkg.css' => 'e68cf1fa', 'conpherence.pkg.css' => 'e68cf1fa',
'conpherence.pkg.js' => '15191c65', 'conpherence.pkg.js' => '15191c65',
'core.pkg.css' => '2fa91e14', 'core.pkg.css' => '2fa91e14',
'core.pkg.js' => 'e4d73c62', 'core.pkg.js' => '32bb68e9',
'darkconsole.pkg.js' => '1f9a31bc', 'darkconsole.pkg.js' => '1f9a31bc',
'differential.pkg.css' => '113e692c', 'differential.pkg.css' => '113e692c',
'differential.pkg.js' => 'f6d809c0', 'differential.pkg.js' => 'f6d809c0',
@ -85,7 +85,7 @@ return array(
'rsrc/css/application/maniphest/task-edit.css' => 'fda62a9b', 'rsrc/css/application/maniphest/task-edit.css' => 'fda62a9b',
'rsrc/css/application/maniphest/task-summary.css' => '11cc5344', 'rsrc/css/application/maniphest/task-summary.css' => '11cc5344',
'rsrc/css/application/objectselector/object-selector.css' => '85ee8ce6', 'rsrc/css/application/objectselector/object-selector.css' => '85ee8ce6',
'rsrc/css/application/owners/owners-path-editor.css' => '2f00933b', 'rsrc/css/application/owners/owners-path-editor.css' => '9c136c29',
'rsrc/css/application/paste/paste.css' => '9fcc9773', 'rsrc/css/application/paste/paste.css' => '9fcc9773',
'rsrc/css/application/people/people-picture-menu-item.css' => 'a06f7f34', 'rsrc/css/application/people/people-picture-menu-item.css' => 'a06f7f34',
'rsrc/css/application/people/people-profile.css' => '4df76faf', 'rsrc/css/application/people/people-profile.css' => '4df76faf',
@ -268,7 +268,7 @@ return array(
'rsrc/externals/javelin/lib/control/typeahead/source/TypeaheadCompositeSource.js' => '503e17fd', 'rsrc/externals/javelin/lib/control/typeahead/source/TypeaheadCompositeSource.js' => '503e17fd',
'rsrc/externals/javelin/lib/control/typeahead/source/TypeaheadOnDemandSource.js' => '013ffff9', 'rsrc/externals/javelin/lib/control/typeahead/source/TypeaheadOnDemandSource.js' => '013ffff9',
'rsrc/externals/javelin/lib/control/typeahead/source/TypeaheadPreloadedSource.js' => '54f314a0', 'rsrc/externals/javelin/lib/control/typeahead/source/TypeaheadPreloadedSource.js' => '54f314a0',
'rsrc/externals/javelin/lib/control/typeahead/source/TypeaheadSource.js' => '0fcf201c', 'rsrc/externals/javelin/lib/control/typeahead/source/TypeaheadSource.js' => 'ab9e0a82',
'rsrc/externals/javelin/lib/control/typeahead/source/TypeaheadStaticSource.js' => '6c0e62fa', 'rsrc/externals/javelin/lib/control/typeahead/source/TypeaheadStaticSource.js' => '6c0e62fa',
'rsrc/favicons/apple-touch-icon-114x114.png' => '12a24178', 'rsrc/favicons/apple-touch-icon-114x114.png' => '12a24178',
'rsrc/favicons/apple-touch-icon-120x120.png' => '0d1543c7', 'rsrc/favicons/apple-touch-icon-120x120.png' => '0d1543c7',
@ -418,13 +418,13 @@ return array(
'rsrc/js/application/files/behavior-launch-icon-composer.js' => '48086888', 'rsrc/js/application/files/behavior-launch-icon-composer.js' => '48086888',
'rsrc/js/application/harbormaster/behavior-harbormaster-log.js' => '191b4909', 'rsrc/js/application/harbormaster/behavior-harbormaster-log.js' => '191b4909',
'rsrc/js/application/herald/HeraldRuleEditor.js' => 'dca75c0e', 'rsrc/js/application/herald/HeraldRuleEditor.js' => 'dca75c0e',
'rsrc/js/application/herald/PathTypeahead.js' => '78039abe', 'rsrc/js/application/herald/PathTypeahead.js' => '662e9cea',
'rsrc/js/application/herald/herald-rule-editor.js' => '7ebaeed3', 'rsrc/js/application/herald/herald-rule-editor.js' => '7ebaeed3',
'rsrc/js/application/maniphest/behavior-batch-selector.js' => 'ad54037e', 'rsrc/js/application/maniphest/behavior-batch-selector.js' => 'ad54037e',
'rsrc/js/application/maniphest/behavior-line-chart.js' => 'e4232876', 'rsrc/js/application/maniphest/behavior-line-chart.js' => 'e4232876',
'rsrc/js/application/maniphest/behavior-list-edit.js' => 'a9f88de2', 'rsrc/js/application/maniphest/behavior-list-edit.js' => 'a9f88de2',
'rsrc/js/application/maniphest/behavior-subpriorityeditor.js' => '71237763', 'rsrc/js/application/maniphest/behavior-subpriorityeditor.js' => '71237763',
'rsrc/js/application/owners/OwnersPathEditor.js' => '52b9cbc4', 'rsrc/js/application/owners/OwnersPathEditor.js' => 'c96502cf',
'rsrc/js/application/owners/owners-path-editor.js' => '7a68dda3', 'rsrc/js/application/owners/owners-path-editor.js' => '7a68dda3',
'rsrc/js/application/passphrase/passphrase-credential-control.js' => '3cb0b2fc', 'rsrc/js/application/passphrase/passphrase-credential-control.js' => '3cb0b2fc',
'rsrc/js/application/pholio/behavior-pholio-mock-edit.js' => 'bee502c8', 'rsrc/js/application/pholio/behavior-pholio-mock-edit.js' => 'bee502c8',
@ -534,7 +534,7 @@ return array(
'rsrc/js/phuix/PHUIXButtonView.js' => '8a91e1ac', 'rsrc/js/phuix/PHUIXButtonView.js' => '8a91e1ac',
'rsrc/js/phuix/PHUIXDropdownMenu.js' => '04b2ae03', 'rsrc/js/phuix/PHUIXDropdownMenu.js' => '04b2ae03',
'rsrc/js/phuix/PHUIXExample.js' => '68af71ca', 'rsrc/js/phuix/PHUIXExample.js' => '68af71ca',
'rsrc/js/phuix/PHUIXFormControl.js' => '16ad6224', 'rsrc/js/phuix/PHUIXFormControl.js' => '210a16c1',
'rsrc/js/phuix/PHUIXIconView.js' => 'bff6884b', 'rsrc/js/phuix/PHUIXIconView.js' => 'bff6884b',
), ),
'symbols' => array( 'symbols' => array(
@ -744,7 +744,7 @@ return array(
'javelin-typeahead-normalizer' => '185bbd53', 'javelin-typeahead-normalizer' => '185bbd53',
'javelin-typeahead-ondemand-source' => '013ffff9', 'javelin-typeahead-ondemand-source' => '013ffff9',
'javelin-typeahead-preloaded-source' => '54f314a0', 'javelin-typeahead-preloaded-source' => '54f314a0',
'javelin-typeahead-source' => '0fcf201c', 'javelin-typeahead-source' => 'ab9e0a82',
'javelin-typeahead-static-source' => '6c0e62fa', 'javelin-typeahead-static-source' => '6c0e62fa',
'javelin-uri' => 'c989ade3', 'javelin-uri' => 'c989ade3',
'javelin-util' => '93cc50d6', 'javelin-util' => '93cc50d6',
@ -764,10 +764,10 @@ return array(
'maniphest-task-edit-css' => 'fda62a9b', 'maniphest-task-edit-css' => 'fda62a9b',
'maniphest-task-summary-css' => '11cc5344', 'maniphest-task-summary-css' => '11cc5344',
'multirow-row-manager' => 'b5d57730', 'multirow-row-manager' => 'b5d57730',
'owners-path-editor' => '52b9cbc4', 'owners-path-editor' => 'c96502cf',
'owners-path-editor-css' => '2f00933b', 'owners-path-editor-css' => '9c136c29',
'paste-css' => '9fcc9773', 'paste-css' => '9fcc9773',
'path-typeahead' => '78039abe', 'path-typeahead' => '662e9cea',
'people-picture-menu-item-css' => 'a06f7f34', 'people-picture-menu-item-css' => 'a06f7f34',
'people-profile-css' => '4df76faf', 'people-profile-css' => '4df76faf',
'phabricator-action-list-view-css' => '0bcd9a45', 'phabricator-action-list-view-css' => '0bcd9a45',
@ -888,7 +888,7 @@ return array(
'phuix-autocomplete' => '7fa5c915', 'phuix-autocomplete' => '7fa5c915',
'phuix-button-view' => '8a91e1ac', 'phuix-button-view' => '8a91e1ac',
'phuix-dropdown-menu' => '04b2ae03', 'phuix-dropdown-menu' => '04b2ae03',
'phuix-form-control-view' => '16ad6224', 'phuix-form-control-view' => '210a16c1',
'phuix-icon-view' => 'bff6884b', 'phuix-icon-view' => 'bff6884b',
'policy-css' => '957ea14c', 'policy-css' => '957ea14c',
'policy-edit-css' => '815c66f7', 'policy-edit-css' => '815c66f7',
@ -998,20 +998,10 @@ return array(
'javelin-install', 'javelin-install',
'javelin-util', 'javelin-util',
), ),
'0fcf201c' => array(
'javelin-install',
'javelin-util',
'javelin-dom',
'javelin-typeahead-normalizer',
),
'15d5ff71' => array( '15d5ff71' => array(
'aphront-typeahead-control-css', 'aphront-typeahead-control-css',
'phui-tag-view-css', 'phui-tag-view-css',
), ),
'16ad6224' => array(
'javelin-install',
'javelin-dom',
),
'17bb8539' => array( '17bb8539' => array(
'javelin-behavior', 'javelin-behavior',
'javelin-stratcom', 'javelin-stratcom',
@ -1061,6 +1051,10 @@ return array(
'javelin-install', 'javelin-install',
'javelin-dom', 'javelin-dom',
), ),
'210a16c1' => array(
'javelin-install',
'javelin-dom',
),
'2290aeef' => array( '2290aeef' => array(
'javelin-install', 'javelin-install',
'javelin-dom', 'javelin-dom',
@ -1337,14 +1331,6 @@ return array(
'javelin-vector', 'javelin-vector',
'javelin-typeahead-static-source', 'javelin-typeahead-static-source',
), ),
'52b9cbc4' => array(
'multirow-row-manager',
'javelin-install',
'path-typeahead',
'javelin-dom',
'javelin-util',
'phabricator-prefab',
),
'54b612ba' => array( '54b612ba' => array(
'javelin-color', 'javelin-color',
'javelin-install', 'javelin-install',
@ -1439,6 +1425,14 @@ return array(
'javelin-workflow', 'javelin-workflow',
'javelin-dom', 'javelin-dom',
), ),
'662e9cea' => array(
'javelin-install',
'javelin-typeahead',
'javelin-dom',
'javelin-request',
'javelin-typeahead-ondemand-source',
'javelin-util',
),
'66a6def1' => array( '66a6def1' => array(
'javelin-behavior', 'javelin-behavior',
'javelin-dom', 'javelin-dom',
@ -1542,14 +1536,6 @@ return array(
'javelin-request', 'javelin-request',
'javelin-util', 'javelin-util',
), ),
'78039abe' => array(
'javelin-install',
'javelin-typeahead',
'javelin-dom',
'javelin-request',
'javelin-typeahead-ondemand-source',
'javelin-util',
),
'7927a7d3' => array( '7927a7d3' => array(
'javelin-behavior', 'javelin-behavior',
'javelin-quicksand', 'javelin-quicksand',
@ -1799,6 +1785,12 @@ return array(
'javelin-util', 'javelin-util',
'phabricator-busy', 'phabricator-busy',
), ),
'ab9e0a82' => array(
'javelin-install',
'javelin-util',
'javelin-dom',
'javelin-typeahead-normalizer',
),
'acd29eee' => array( 'acd29eee' => array(
'javelin-behavior', 'javelin-behavior',
'javelin-stratcom', 'javelin-stratcom',
@ -1974,6 +1966,15 @@ return array(
'javelin-install', 'javelin-install',
'javelin-util', 'javelin-util',
), ),
'c96502cf' => array(
'multirow-row-manager',
'javelin-install',
'path-typeahead',
'javelin-dom',
'javelin-util',
'phabricator-prefab',
'phuix-form-control-view',
),
'c989ade3' => array( 'c989ade3' => array(
'javelin-install', 'javelin-install',
'javelin-util', 'javelin-util',

View file

@ -45,19 +45,6 @@ final class DiffusionPathValidateController extends DiffusionController {
'valid' => (bool)$valid, 'valid' => (bool)$valid,
); );
if (!$valid) {
$branch = $drequest->getBranch();
if ($branch) {
$message = pht('Not found in %s', $branch);
} else {
$message = pht('Not found at %s', 'HEAD');
}
} else {
$message = pht('OK');
}
$output['message'] = $message;
return id(new AphrontAjaxResponse())->setContent($output); return id(new AphrontAjaxResponse())->setContent($output);
} }
} }

View file

@ -27,7 +27,7 @@ final class PhabricatorOwnersPathsController
$path_refs = array(); $path_refs = array();
foreach ($paths as $key => $path) { foreach ($paths as $key => $path) {
if (!isset($repos[$key])) { if (!isset($repos[$key]) || !strlen($repos[$key])) {
throw new Exception( throw new Exception(
pht( pht(
'No repository PHID for path "%s"!', 'No repository PHID for path "%s"!',
@ -70,17 +70,39 @@ final class PhabricatorOwnersPathsController
$path_refs = mpull($paths, 'getRef'); $path_refs = mpull($paths, 'getRef');
} }
$repos = id(new PhabricatorRepositoryQuery()) $template = new AphrontTokenizerTemplateView();
->setViewer($viewer)
->execute();
$repo_map = array(); $datasource = id(new DiffusionRepositoryDatasource())
foreach ($repos as $key => $repo) { ->setViewer($viewer);
$monogram = $repo->getMonogram();
$name = $repo->getName(); $tokenizer_spec = array(
$repo_map[$repo->getPHID()] = "{$monogram} {$name}"; 'markup' => $template->render(),
'config' => array(
'src' => $datasource->getDatasourceURI(),
'browseURI' => $datasource->getBrowseURI(),
'placeholder' => $datasource->getPlaceholderText(),
'limit' => 1,
),
);
foreach ($path_refs as $key => $path_ref) {
$path_refs[$key]['repositoryValue'] = $datasource->getWireTokens(
array(
$path_ref['repositoryPHID'],
));
} }
asort($repos);
$icon_test = id(new PHUIIconView())
->setIcon('fa-spinner grey')
->setTooltip(pht('Validating...'));
$icon_okay = id(new PHUIIconView())
->setIcon('fa-check-circle green')
->setTooltip(pht('Path Exists in Repository'));
$icon_fail = id(new PHUIIconView())
->setIcon('fa-question-circle-o red')
->setTooltip(pht('Path Not Found On Default Branch'));
$template = new AphrontTypeaheadTemplateView(); $template = new AphrontTypeaheadTemplateView();
$template = $template->render(); $template = $template->render();
@ -91,12 +113,20 @@ final class PhabricatorOwnersPathsController
'root' => 'path-editor', 'root' => 'path-editor',
'table' => 'paths', 'table' => 'paths',
'add_button' => 'addpath', 'add_button' => 'addpath',
'repositories' => $repo_map,
'input_template' => $template, 'input_template' => $template,
'pathRefs' => $path_refs, 'pathRefs' => $path_refs,
'completeURI' => '/diffusion/services/path/complete/', 'completeURI' => '/diffusion/services/path/complete/',
'validateURI' => '/diffusion/services/path/validate/', 'validateURI' => '/diffusion/services/path/validate/',
'repositoryTokenizerSpec' => $tokenizer_spec,
'icons' => array(
'test' => hsprintf('%s', $icon_test),
'okay' => hsprintf('%s', $icon_okay),
'fail' => hsprintf('%s', $icon_fail),
),
'modeOptions' => array(
0 => pht('Include'),
1 => pht('Exclude'),
),
)); ));
require_celerity_resource('owners-path-editor-css'); require_celerity_resource('owners-path-editor-css');

View file

@ -3,7 +3,8 @@
*/ */
.owners-path-editor-table { .owners-path-editor-table {
margin: 10px; margin: 10px 0;
width: 100%;
} }
.owners-path-editor-table td { .owners-path-editor-table td {
@ -11,27 +12,38 @@
vertical-align: middle; vertical-align: middle;
} }
.owners-path-editor-table select.owners-repo { .owners-path-editor-table td.owners-path-mode-control {
width: 150px; width: 180px;
} }
.owners-path-editor-table input { .owners-path-editor-table td.owners-path-mode-control select {
width: 400px; width: 100%;
} }
.owners-path-editor-table div.error-display { .owners-path-editor-table td.owners-path-repo-control {
padding: 4px 12px 0; width: 280px;
} }
.owners-path-editor-table div.validating { .owners-path-editor-table td.owners-path-path-control {
color: {$greytext}; width: auto;
} }
.owners-path-editor-table div.invalid { .owners-path-editor-table td.owners-path-path-control input {
color: #aa0000; width: 100%;
} }
.owners-path-editor-table div.valid { .owners-path-editor-table td.owners-path-path-control .jx-typeahead-results a {
color: #00aa00; padding: 4px;
font-weight: bold; }
.owners-path-editor-table td.owners-path-icon-control {
width: 18px;
}
.owners-path-editor-table td.remove-column {
width: 100px;
}
.owners-path-editor-table td.remove-column a {
display: block;
} }

View file

@ -9,8 +9,7 @@
JX.install('TypeaheadSource', { JX.install('TypeaheadSource', {
construct : function() { construct : function() {
this._raw = {}; this.resetResults();
this._lookup = {};
this.setNormalizer(JX.TypeaheadNormalizer.normalize); this.setNormalizer(JX.TypeaheadNormalizer.normalize);
this._excludeIDs = {}; this._excludeIDs = {};
}, },
@ -359,6 +358,12 @@ JX.install('TypeaheadSource', {
} }
return str.split(/\s+/g); return str.split(/\s+/g);
}, },
resetResults: function() {
this._raw = {};
this._lookup = {};
},
_defaultTransformer : function(object) { _defaultTransformer : function(object) {
return { return {
name : object[0], name : object[0],

View file

@ -11,7 +11,7 @@
JX.install('PathTypeahead', { JX.install('PathTypeahead', {
construct : function(config) { construct : function(config) {
this._repositorySelect = config.repo_select; this._repositoryTokenizer = config.repositoryTokenizer;
this._hardpoint = config.hardpoint; this._hardpoint = config.hardpoint;
this._input = config.path_input; this._input = config.path_input;
this._completeURI = config.completeURI; this._completeURI = config.completeURI;
@ -19,14 +19,14 @@ JX.install('PathTypeahead', {
this._errorDisplay = config.error_display; this._errorDisplay = config.error_display;
this._textInputValues = {}; this._textInputValues = {};
this._icons = config.icons;
this._initializeDatasource(); this._initializeDatasource();
this._initializeTypeahead(this._input); this._initializeTypeahead(this._input);
}, },
members : { members : {
/* _repositoryTokenizer : null,
* DOM <select> elem for choosing the repository of a path.
*/
_repositorySelect : null,
/* /*
* DOM parent div "hardpoint" to be passed to the JX.Typeahead. * DOM parent div "hardpoint" to be passed to the JX.Typeahead.
*/ */
@ -79,31 +79,28 @@ JX.install('PathTypeahead', {
*/ */
start : function() { start : function() {
if (this._typeahead.getValue()) { if (this._typeahead.getValue()) {
this._textInputValues[this._repositorySelect.value] = var phid = this._getRepositoryPHID();
this._typeahead.getValue(); if (phid) {
this._textInputValues[phid] = this._typeahead.getValue();
}
} }
this._typeahead.listen( this._typeahead.listen(
'change', 'change',
JX.bind(this, function(value) { JX.bind(this, function(value) {
this._textInputValues[this._repositorySelect.value] = value; var phid = this._getRepositoryPHID();
this._validate(); if (phid) {
})); this._textInputValues[phid] = value;
}
this._typeahead.listen( this._validate();
'choose',
JX.bind(this, function() {
setTimeout(JX.bind(this._typeahead, this._typeahead.refresh), 0);
})); }));
var repo_set_input = JX.bind(this, this._onrepochange); var repo_set_input = JX.bind(this, this._onrepochange);
this._typeahead.listen('start', repo_set_input); this._typeahead.listen('start', repo_set_input);
JX.DOM.listen(
this._repositorySelect, this._repositoryTokenizer.listen('change', repo_set_input);
'change',
null,
repo_set_input);
this._typeahead.start(); this._typeahead.start();
this._validate(); this._validate();
@ -115,13 +112,18 @@ JX.install('PathTypeahead', {
this._textInputValues); this._textInputValues);
this._datasource.setAuxiliaryData( this._datasource.setAuxiliaryData(
{repositoryPHID : this._repositorySelect.value} {
); repositoryPHID: this._getRepositoryPHID()
});
// Since we've changed the repository, reset the results.
this._datasource.resetResults();
}, },
_setPathInputBasedOnRepository : function(typeahead, lookup) { _setPathInputBasedOnRepository : function(typeahead, lookup) {
if (lookup[this._repositorySelect.value]) { var phid = this._getRepositoryPHID();
typeahead.setValue(lookup[this._repositorySelect.value]); if (phid && lookup[phid]) {
typeahead.setValue(lookup[phid]);
} else { } else {
typeahead.setValue('/'); typeahead.setValue('/');
} }
@ -147,9 +149,24 @@ JX.install('PathTypeahead', {
return ('' + str).replace(/[\/]+/g, '\/'); return ('' + str).replace(/[\/]+/g, '\/');
}, },
_getRepositoryPHID: function() {
var tokens = this._repositoryTokenizer.getTokens();
var keys = JX.keys(tokens);
if (keys.length) {
return keys[0];
}
return null;
},
_validate : function() { _validate : function() {
var repo_phid = this._getRepositoryPHID();
if (!repo_phid) {
return;
}
var input = this._input; var input = this._input;
var repo_id = this._repositorySelect.value;
var input_value = input.value; var input_value = input.value;
var error_display = this._errorDisplay; var error_display = this._errorDisplay;
@ -165,33 +182,32 @@ JX.install('PathTypeahead', {
var validation_request = new JX.Request( var validation_request = new JX.Request(
this._validateURI, this._validateURI,
function(payload) { JX.bind(this, function(payload) {
// Don't change validation display state if the input has been // Don't change validation display state if the input has been
// changed since we started validation // changed since we started validation
if (input.value === input_value) { if (input.value !== input_value) {
return;
}
if (payload.valid) { if (payload.valid) {
JX.DOM.alterClass(error_display, 'invalid', false); JX.DOM.setContent(error_display, JX.$H(this._icons.okay));
JX.DOM.alterClass(error_display, 'valid', true);
} else { } else {
JX.DOM.alterClass(error_display, 'invalid', true); JX.DOM.setContent(error_display, JX.$H(this._icons.fail));
JX.DOM.alterClass(error_display, 'valid', false);
} }
JX.DOM.setContent(error_display, payload.message); }));
}
});
validation_request.listen('finally', function() { validation_request.listen('finally', function() {
JX.DOM.alterClass(error_display, 'validating', false);
this._validationInflight = null; this._validationInflight = null;
}); });
validation_request.setData( validation_request.setData(
{ {
repositoryPHID : repo_id, repositoryPHID : repo_phid,
path : input_value path : input_value
}); });
this._validationInflight = validation_request; this._validationInflight = validation_request;
JX.DOM.setContent(error_display, JX.$H(this._icons.test));
validation_request.setTimeout(750); validation_request.setTimeout(750);
validation_request.send(); validation_request.send();

View file

@ -5,6 +5,7 @@
* javelin-dom * javelin-dom
* javelin-util * javelin-util
* phabricator-prefab * phabricator-prefab
* phuix-form-control-view
* @provides owners-path-editor * @provides owners-path-editor
* @javelin * @javelin
*/ */
@ -23,11 +24,13 @@ JX.install('OwnersPathEditor', {
JX.bind(this, this._onaddpath)); JX.bind(this, this._onaddpath));
this._count = 0; this._count = 0;
this._repositories = config.repositories;
this._inputTemplate = config.input_template; this._inputTemplate = config.input_template;
this._repositoryTokenizerSpec = config.repositoryTokenizerSpec;
this._completeURI = config.completeURI; this._completeURI = config.completeURI;
this._validateURI = config.validateURI; this._validateURI = config.validateURI;
this._icons = config.icons;
this._modeOptions = config.modeOptions;
this._initializePaths(config.pathRefs); this._initializePaths(config.pathRefs);
}, },
@ -37,12 +40,6 @@ JX.install('OwnersPathEditor', {
*/ */
_rowManager : null, _rowManager : null,
/*
* Array of objects with 'name' and 'repo_id' keys for
* selecting the repository of a path.
*/
_repositories : null,
/* /*
* How many rows have been created, for form name generation. * How many rows have been created, for form name generation.
*/ */
@ -65,6 +62,8 @@ JX.install('OwnersPathEditor', {
* default for future rows. * default for future rows.
*/ */
_lastRepositoryChoice : null, _lastRepositoryChoice : null,
_icons: null,
_modeOptions: null,
/* /*
* Initialize with 0 or more rows. * Initialize with 0 or more rows.
@ -85,68 +84,34 @@ JX.install('OwnersPathEditor', {
addPath : function(path_ref) { addPath : function(path_ref) {
// Smart default repository. See _lastRepositoryChoice. // Smart default repository. See _lastRepositoryChoice.
if (path_ref) { if (path_ref) {
this._lastRepositoryChoice = path_ref.repositoryPHID; this._lastRepositoryChoice = path_ref.repositoryValue;
} } else {
path_ref = path_ref || {}; path_ref = {
repositoryValue: this._lastRepositoryChoice || {}
var selected_repository = path_ref.repositoryPHID ||
this._lastRepositoryChoice;
var options = this._buildRepositoryOptions(selected_repository);
var attrs = {
name : 'repo[' + this._count + ']',
className : 'owners-repo'
}; };
var repo_select = JX.$N('select', attrs, options); }
JX.DOM.listen(repo_select, 'change', null, JX.bind(this, function() { var repo = this._newRepoCell(path_ref.repositoryValue);
this._lastRepositoryChoice = repo_select.value; var path = this._newPathCell(path_ref.display);
})); var icon = this._newIconCell();
var mode_cell = this._newModeCell(path_ref.excluded);
var repo_cell = JX.$N('td', {}, repo_select);
var typeahead_cell = JX.$N(
'td',
JX.$H(this._inputTemplate));
// Text input for path.
var path_input = JX.DOM.find(typeahead_cell, 'input');
JX.copy(
path_input,
{
value : path_ref.display || '',
name : 'path[' + this._count + ']'
});
// The Typeahead requires a display div called hardpoint.
var hardpoint = JX.DOM.find(
typeahead_cell,
'div',
'typeahead-hardpoint');
var error_display = JX.$N(
'div',
{
className : 'error-display validating'
},
'Validating...');
var error_display_cell = JX.$N('td', {}, error_display);
var exclude = JX.Prefab.renderSelect(
{'0' : 'Include', '1' : 'Exclude'},
path_ref.excluded,
{name : 'exclude[' + this._count + ']'});
var exclude_cell = JX.$N('td', {}, exclude);
var row = this._rowManager.addRow( var row = this._rowManager.addRow(
[exclude_cell, repo_cell, typeahead_cell, error_display_cell]); [
mode_cell,
repo.cell,
path.cell,
icon.cell
]);
new JX.PathTypeahead({ new JX.PathTypeahead({
repo_select : repo_select, repositoryTokenizer: repo.tokenizer,
path_input : path_input, path_input : path.input,
hardpoint : hardpoint, hardpoint : path.hardpoint,
error_display : error_display, error_display : icon.cell,
completeURI : this._completeURI, completeURI : this._completeURI,
validateURI : this._validateURI validateURI : this._validateURI,
icons: this._icons
}).start(); }).start();
this._count++; this._count++;
@ -158,20 +123,109 @@ JX.install('OwnersPathEditor', {
this.addPath(); this.addPath();
}, },
/** _newModeCell: function(value) {
* Helper to build the options for the repository choice dropdown. var options = this._modeOptions;
*/
_buildRepositoryOptions : function(selected) { var name = 'exclude[' + this._count + ']';
var repos = this._repositories;
var result = []; var control = JX.Prefab.renderSelect(options, value, {name: name});
for (var k in repos) {
var attr = { return JX.$N(
value : k, 'td',
selected : (selected == k) {
className: 'owners-path-mode-control'
},
control);
},
_newRepoCell: function(value) {
var repo_control = new JX.PHUIXFormControl()
.setControl('tokenizer', this._repositoryTokenizerSpec)
.setValue(value);
var repo_tokenizer = repo_control.getTokenizer();
var name = 'repo[' + this._count + ']';
function get_phid() {
var phids = repo_control.getValue();
if (!phids.length) {
return null;
}
return phids[0];
}
var input = JX.$N(
'input',
{
type: 'hidden',
name: name,
value: get_phid()
});
repo_tokenizer.listen('change', JX.bind(this, function() {
this._lastRepositoryChoice = repo_tokenizer.getTokens();
input.value = get_phid();
}));
var cell = JX.$N(
'td',
{
className: 'owners-path-repo-control'
},
[
repo_control.getRawInputNode(),
input
]);
return {
cell: cell,
tokenizer: repo_tokenizer
};
},
_newPathCell: function(value) {
var path_cell = JX.$N(
'td',
{
className: 'owners-path-path-control'
},
JX.$H(this._inputTemplate));
var path_input = JX.DOM.find(path_cell, 'input');
JX.copy(
path_input,
{
value: value || '',
name: 'path[' + this._count + ']'
});
var hardpoint = JX.DOM.find(
path_cell,
'div',
'typeahead-hardpoint');
return {
cell: path_cell,
input: path_input,
hardpoint: hardpoint
};
},
_newIconCell: function() {
var cell = JX.$N(
'td',
{
className: 'owners-path-icon-control'
});
return {
cell: cell
}; };
result.push(JX.$N('option', attr, repos[k]));
}
return result;
} }
} }
}); });

View file

@ -15,6 +15,7 @@ JX.install('PHUIXFormControl', {
_valueSetCallback: null, _valueSetCallback: null,
_valueGetCallback: null, _valueGetCallback: null,
_rawInputNode: null, _rawInputNode: null,
_tokenizer: null,
setLabel: function(label) { setLabel: function(label) {
JX.DOM.setContent(this._getLabelNode(), label); JX.DOM.setContent(this._getLabelNode(), label);
@ -70,6 +71,7 @@ JX.install('PHUIXFormControl', {
this._valueGetCallback = input.get; this._valueGetCallback = input.get;
this._valueSetCallback = input.set; this._valueSetCallback = input.set;
this._rawInputNode = input.node; this._rawInputNode = input.node;
this._tokenizer = input.tokenizer || null;
return this; return this;
}, },
@ -87,6 +89,10 @@ JX.install('PHUIXFormControl', {
return this._rawInputNode; return this._rawInputNode;
}, },
getTokenizer: function() {
return this._tokenizer;
},
getNode: function() { getNode: function() {
if (!this._node) { if (!this._node) {
@ -168,7 +174,8 @@ JX.install('PHUIXFormControl', {
return { return {
node: build.node, node: build.node,
get: get_value, get: get_value,
set: set_value set: set_value,
tokenizer: build.tokenizer
}; };
}, },