mirror of
https://we.phorge.it/source/phorge.git
synced 2024-12-22 21:40:55 +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:
parent
b41a0e6ddd
commit
a4cc1373d3
8 changed files with 310 additions and 198 deletions
|
@ -10,7 +10,7 @@ return array(
|
|||
'conpherence.pkg.css' => 'e68cf1fa',
|
||||
'conpherence.pkg.js' => '15191c65',
|
||||
'core.pkg.css' => '2fa91e14',
|
||||
'core.pkg.js' => 'e4d73c62',
|
||||
'core.pkg.js' => '32bb68e9',
|
||||
'darkconsole.pkg.js' => '1f9a31bc',
|
||||
'differential.pkg.css' => '113e692c',
|
||||
'differential.pkg.js' => 'f6d809c0',
|
||||
|
@ -85,7 +85,7 @@ return array(
|
|||
'rsrc/css/application/maniphest/task-edit.css' => 'fda62a9b',
|
||||
'rsrc/css/application/maniphest/task-summary.css' => '11cc5344',
|
||||
'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/people/people-picture-menu-item.css' => 'a06f7f34',
|
||||
'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/TypeaheadOnDemandSource.js' => '013ffff9',
|
||||
'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/favicons/apple-touch-icon-114x114.png' => '12a24178',
|
||||
'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/harbormaster/behavior-harbormaster-log.js' => '191b4909',
|
||||
'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/maniphest/behavior-batch-selector.js' => 'ad54037e',
|
||||
'rsrc/js/application/maniphest/behavior-line-chart.js' => 'e4232876',
|
||||
'rsrc/js/application/maniphest/behavior-list-edit.js' => 'a9f88de2',
|
||||
'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/passphrase/passphrase-credential-control.js' => '3cb0b2fc',
|
||||
'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/PHUIXDropdownMenu.js' => '04b2ae03',
|
||||
'rsrc/js/phuix/PHUIXExample.js' => '68af71ca',
|
||||
'rsrc/js/phuix/PHUIXFormControl.js' => '16ad6224',
|
||||
'rsrc/js/phuix/PHUIXFormControl.js' => '210a16c1',
|
||||
'rsrc/js/phuix/PHUIXIconView.js' => 'bff6884b',
|
||||
),
|
||||
'symbols' => array(
|
||||
|
@ -744,7 +744,7 @@ return array(
|
|||
'javelin-typeahead-normalizer' => '185bbd53',
|
||||
'javelin-typeahead-ondemand-source' => '013ffff9',
|
||||
'javelin-typeahead-preloaded-source' => '54f314a0',
|
||||
'javelin-typeahead-source' => '0fcf201c',
|
||||
'javelin-typeahead-source' => 'ab9e0a82',
|
||||
'javelin-typeahead-static-source' => '6c0e62fa',
|
||||
'javelin-uri' => 'c989ade3',
|
||||
'javelin-util' => '93cc50d6',
|
||||
|
@ -764,10 +764,10 @@ return array(
|
|||
'maniphest-task-edit-css' => 'fda62a9b',
|
||||
'maniphest-task-summary-css' => '11cc5344',
|
||||
'multirow-row-manager' => 'b5d57730',
|
||||
'owners-path-editor' => '52b9cbc4',
|
||||
'owners-path-editor-css' => '2f00933b',
|
||||
'owners-path-editor' => 'c96502cf',
|
||||
'owners-path-editor-css' => '9c136c29',
|
||||
'paste-css' => '9fcc9773',
|
||||
'path-typeahead' => '78039abe',
|
||||
'path-typeahead' => '662e9cea',
|
||||
'people-picture-menu-item-css' => 'a06f7f34',
|
||||
'people-profile-css' => '4df76faf',
|
||||
'phabricator-action-list-view-css' => '0bcd9a45',
|
||||
|
@ -888,7 +888,7 @@ return array(
|
|||
'phuix-autocomplete' => '7fa5c915',
|
||||
'phuix-button-view' => '8a91e1ac',
|
||||
'phuix-dropdown-menu' => '04b2ae03',
|
||||
'phuix-form-control-view' => '16ad6224',
|
||||
'phuix-form-control-view' => '210a16c1',
|
||||
'phuix-icon-view' => 'bff6884b',
|
||||
'policy-css' => '957ea14c',
|
||||
'policy-edit-css' => '815c66f7',
|
||||
|
@ -998,20 +998,10 @@ return array(
|
|||
'javelin-install',
|
||||
'javelin-util',
|
||||
),
|
||||
'0fcf201c' => array(
|
||||
'javelin-install',
|
||||
'javelin-util',
|
||||
'javelin-dom',
|
||||
'javelin-typeahead-normalizer',
|
||||
),
|
||||
'15d5ff71' => array(
|
||||
'aphront-typeahead-control-css',
|
||||
'phui-tag-view-css',
|
||||
),
|
||||
'16ad6224' => array(
|
||||
'javelin-install',
|
||||
'javelin-dom',
|
||||
),
|
||||
'17bb8539' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-stratcom',
|
||||
|
@ -1061,6 +1051,10 @@ return array(
|
|||
'javelin-install',
|
||||
'javelin-dom',
|
||||
),
|
||||
'210a16c1' => array(
|
||||
'javelin-install',
|
||||
'javelin-dom',
|
||||
),
|
||||
'2290aeef' => array(
|
||||
'javelin-install',
|
||||
'javelin-dom',
|
||||
|
@ -1337,14 +1331,6 @@ return array(
|
|||
'javelin-vector',
|
||||
'javelin-typeahead-static-source',
|
||||
),
|
||||
'52b9cbc4' => array(
|
||||
'multirow-row-manager',
|
||||
'javelin-install',
|
||||
'path-typeahead',
|
||||
'javelin-dom',
|
||||
'javelin-util',
|
||||
'phabricator-prefab',
|
||||
),
|
||||
'54b612ba' => array(
|
||||
'javelin-color',
|
||||
'javelin-install',
|
||||
|
@ -1439,6 +1425,14 @@ return array(
|
|||
'javelin-workflow',
|
||||
'javelin-dom',
|
||||
),
|
||||
'662e9cea' => array(
|
||||
'javelin-install',
|
||||
'javelin-typeahead',
|
||||
'javelin-dom',
|
||||
'javelin-request',
|
||||
'javelin-typeahead-ondemand-source',
|
||||
'javelin-util',
|
||||
),
|
||||
'66a6def1' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-dom',
|
||||
|
@ -1542,14 +1536,6 @@ return array(
|
|||
'javelin-request',
|
||||
'javelin-util',
|
||||
),
|
||||
'78039abe' => array(
|
||||
'javelin-install',
|
||||
'javelin-typeahead',
|
||||
'javelin-dom',
|
||||
'javelin-request',
|
||||
'javelin-typeahead-ondemand-source',
|
||||
'javelin-util',
|
||||
),
|
||||
'7927a7d3' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-quicksand',
|
||||
|
@ -1799,6 +1785,12 @@ return array(
|
|||
'javelin-util',
|
||||
'phabricator-busy',
|
||||
),
|
||||
'ab9e0a82' => array(
|
||||
'javelin-install',
|
||||
'javelin-util',
|
||||
'javelin-dom',
|
||||
'javelin-typeahead-normalizer',
|
||||
),
|
||||
'acd29eee' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-stratcom',
|
||||
|
@ -1974,6 +1966,15 @@ return array(
|
|||
'javelin-install',
|
||||
'javelin-util',
|
||||
),
|
||||
'c96502cf' => array(
|
||||
'multirow-row-manager',
|
||||
'javelin-install',
|
||||
'path-typeahead',
|
||||
'javelin-dom',
|
||||
'javelin-util',
|
||||
'phabricator-prefab',
|
||||
'phuix-form-control-view',
|
||||
),
|
||||
'c989ade3' => array(
|
||||
'javelin-install',
|
||||
'javelin-util',
|
||||
|
|
|
@ -45,19 +45,6 @@ final class DiffusionPathValidateController extends DiffusionController {
|
|||
'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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,7 +27,7 @@ final class PhabricatorOwnersPathsController
|
|||
|
||||
$path_refs = array();
|
||||
foreach ($paths as $key => $path) {
|
||||
if (!isset($repos[$key])) {
|
||||
if (!isset($repos[$key]) || !strlen($repos[$key])) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'No repository PHID for path "%s"!',
|
||||
|
@ -70,17 +70,39 @@ final class PhabricatorOwnersPathsController
|
|||
$path_refs = mpull($paths, 'getRef');
|
||||
}
|
||||
|
||||
$repos = id(new PhabricatorRepositoryQuery())
|
||||
->setViewer($viewer)
|
||||
->execute();
|
||||
$template = new AphrontTokenizerTemplateView();
|
||||
|
||||
$repo_map = array();
|
||||
foreach ($repos as $key => $repo) {
|
||||
$monogram = $repo->getMonogram();
|
||||
$name = $repo->getName();
|
||||
$repo_map[$repo->getPHID()] = "{$monogram} {$name}";
|
||||
$datasource = id(new DiffusionRepositoryDatasource())
|
||||
->setViewer($viewer);
|
||||
|
||||
$tokenizer_spec = array(
|
||||
'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 = $template->render();
|
||||
|
@ -88,15 +110,23 @@ final class PhabricatorOwnersPathsController
|
|||
Javelin::initBehavior(
|
||||
'owners-path-editor',
|
||||
array(
|
||||
'root' => 'path-editor',
|
||||
'table' => 'paths',
|
||||
'add_button' => 'addpath',
|
||||
'repositories' => $repo_map,
|
||||
'input_template' => $template,
|
||||
'pathRefs' => $path_refs,
|
||||
|
||||
'completeURI' => '/diffusion/services/path/complete/',
|
||||
'validateURI' => '/diffusion/services/path/validate/',
|
||||
'root' => 'path-editor',
|
||||
'table' => 'paths',
|
||||
'add_button' => 'addpath',
|
||||
'input_template' => $template,
|
||||
'pathRefs' => $path_refs,
|
||||
'completeURI' => '/diffusion/services/path/complete/',
|
||||
'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');
|
||||
|
|
|
@ -3,7 +3,8 @@
|
|||
*/
|
||||
|
||||
.owners-path-editor-table {
|
||||
margin: 10px;
|
||||
margin: 10px 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.owners-path-editor-table td {
|
||||
|
@ -11,27 +12,38 @@
|
|||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.owners-path-editor-table select.owners-repo {
|
||||
width: 150px;
|
||||
.owners-path-editor-table td.owners-path-mode-control {
|
||||
width: 180px;
|
||||
}
|
||||
|
||||
.owners-path-editor-table input {
|
||||
width: 400px;
|
||||
.owners-path-editor-table td.owners-path-mode-control select {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.owners-path-editor-table div.error-display {
|
||||
padding: 4px 12px 0;
|
||||
.owners-path-editor-table td.owners-path-repo-control {
|
||||
width: 280px;
|
||||
}
|
||||
|
||||
.owners-path-editor-table div.validating {
|
||||
color: {$greytext};
|
||||
.owners-path-editor-table td.owners-path-path-control {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.owners-path-editor-table div.invalid {
|
||||
color: #aa0000;
|
||||
.owners-path-editor-table td.owners-path-path-control input {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.owners-path-editor-table div.valid {
|
||||
color: #00aa00;
|
||||
font-weight: bold;
|
||||
.owners-path-editor-table td.owners-path-path-control .jx-typeahead-results a {
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
|
|
|
@ -9,8 +9,7 @@
|
|||
|
||||
JX.install('TypeaheadSource', {
|
||||
construct : function() {
|
||||
this._raw = {};
|
||||
this._lookup = {};
|
||||
this.resetResults();
|
||||
this.setNormalizer(JX.TypeaheadNormalizer.normalize);
|
||||
this._excludeIDs = {};
|
||||
},
|
||||
|
@ -359,6 +358,12 @@ JX.install('TypeaheadSource', {
|
|||
}
|
||||
return str.split(/\s+/g);
|
||||
},
|
||||
|
||||
resetResults: function() {
|
||||
this._raw = {};
|
||||
this._lookup = {};
|
||||
},
|
||||
|
||||
_defaultTransformer : function(object) {
|
||||
return {
|
||||
name : object[0],
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
|
||||
JX.install('PathTypeahead', {
|
||||
construct : function(config) {
|
||||
this._repositorySelect = config.repo_select;
|
||||
this._repositoryTokenizer = config.repositoryTokenizer;
|
||||
this._hardpoint = config.hardpoint;
|
||||
this._input = config.path_input;
|
||||
this._completeURI = config.completeURI;
|
||||
|
@ -19,14 +19,14 @@ JX.install('PathTypeahead', {
|
|||
this._errorDisplay = config.error_display;
|
||||
this._textInputValues = {};
|
||||
|
||||
this._icons = config.icons;
|
||||
|
||||
this._initializeDatasource();
|
||||
this._initializeTypeahead(this._input);
|
||||
},
|
||||
members : {
|
||||
/*
|
||||
* DOM <select> elem for choosing the repository of a path.
|
||||
*/
|
||||
_repositorySelect : null,
|
||||
_repositoryTokenizer : null,
|
||||
|
||||
/*
|
||||
* DOM parent div "hardpoint" to be passed to the JX.Typeahead.
|
||||
*/
|
||||
|
@ -79,31 +79,28 @@ JX.install('PathTypeahead', {
|
|||
*/
|
||||
start : function() {
|
||||
if (this._typeahead.getValue()) {
|
||||
this._textInputValues[this._repositorySelect.value] =
|
||||
this._typeahead.getValue();
|
||||
var phid = this._getRepositoryPHID();
|
||||
if (phid) {
|
||||
this._textInputValues[phid] = this._typeahead.getValue();
|
||||
}
|
||||
}
|
||||
|
||||
this._typeahead.listen(
|
||||
'change',
|
||||
JX.bind(this, function(value) {
|
||||
this._textInputValues[this._repositorySelect.value] = value;
|
||||
this._validate();
|
||||
}));
|
||||
var phid = this._getRepositoryPHID();
|
||||
if (phid) {
|
||||
this._textInputValues[phid] = value;
|
||||
}
|
||||
|
||||
this._typeahead.listen(
|
||||
'choose',
|
||||
JX.bind(this, function() {
|
||||
setTimeout(JX.bind(this._typeahead, this._typeahead.refresh), 0);
|
||||
this._validate();
|
||||
}));
|
||||
|
||||
var repo_set_input = JX.bind(this, this._onrepochange);
|
||||
|
||||
this._typeahead.listen('start', repo_set_input);
|
||||
JX.DOM.listen(
|
||||
this._repositorySelect,
|
||||
'change',
|
||||
null,
|
||||
repo_set_input);
|
||||
|
||||
this._repositoryTokenizer.listen('change', repo_set_input);
|
||||
|
||||
this._typeahead.start();
|
||||
this._validate();
|
||||
|
@ -115,13 +112,18 @@ JX.install('PathTypeahead', {
|
|||
this._textInputValues);
|
||||
|
||||
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) {
|
||||
if (lookup[this._repositorySelect.value]) {
|
||||
typeahead.setValue(lookup[this._repositorySelect.value]);
|
||||
var phid = this._getRepositoryPHID();
|
||||
if (phid && lookup[phid]) {
|
||||
typeahead.setValue(lookup[phid]);
|
||||
} else {
|
||||
typeahead.setValue('/');
|
||||
}
|
||||
|
@ -147,9 +149,24 @@ JX.install('PathTypeahead', {
|
|||
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() {
|
||||
var repo_phid = this._getRepositoryPHID();
|
||||
if (!repo_phid) {
|
||||
return;
|
||||
}
|
||||
|
||||
var input = this._input;
|
||||
var repo_id = this._repositorySelect.value;
|
||||
var input_value = input.value;
|
||||
var error_display = this._errorDisplay;
|
||||
|
||||
|
@ -165,33 +182,32 @@ JX.install('PathTypeahead', {
|
|||
|
||||
var validation_request = new JX.Request(
|
||||
this._validateURI,
|
||||
function(payload) {
|
||||
JX.bind(this, function(payload) {
|
||||
// Don't change validation display state if the input has been
|
||||
// changed since we started validation
|
||||
if (input.value === input_value) {
|
||||
if (payload.valid) {
|
||||
JX.DOM.alterClass(error_display, 'invalid', false);
|
||||
JX.DOM.alterClass(error_display, 'valid', true);
|
||||
} else {
|
||||
JX.DOM.alterClass(error_display, 'invalid', true);
|
||||
JX.DOM.alterClass(error_display, 'valid', false);
|
||||
}
|
||||
JX.DOM.setContent(error_display, payload.message);
|
||||
if (input.value !== input_value) {
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
if (payload.valid) {
|
||||
JX.DOM.setContent(error_display, JX.$H(this._icons.okay));
|
||||
} else {
|
||||
JX.DOM.setContent(error_display, JX.$H(this._icons.fail));
|
||||
}
|
||||
}));
|
||||
|
||||
validation_request.listen('finally', function() {
|
||||
JX.DOM.alterClass(error_display, 'validating', false);
|
||||
this._validationInflight = null;
|
||||
});
|
||||
|
||||
validation_request.setData(
|
||||
{
|
||||
repositoryPHID : repo_id,
|
||||
repositoryPHID : repo_phid,
|
||||
path : input_value
|
||||
});
|
||||
|
||||
this._validationInflight = validation_request;
|
||||
JX.DOM.setContent(error_display, JX.$H(this._icons.test));
|
||||
|
||||
validation_request.setTimeout(750);
|
||||
validation_request.send();
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
* javelin-dom
|
||||
* javelin-util
|
||||
* phabricator-prefab
|
||||
* phuix-form-control-view
|
||||
* @provides owners-path-editor
|
||||
* @javelin
|
||||
*/
|
||||
|
@ -23,11 +24,13 @@ JX.install('OwnersPathEditor', {
|
|||
JX.bind(this, this._onaddpath));
|
||||
|
||||
this._count = 0;
|
||||
this._repositories = config.repositories;
|
||||
this._inputTemplate = config.input_template;
|
||||
this._repositoryTokenizerSpec = config.repositoryTokenizerSpec;
|
||||
|
||||
this._completeURI = config.completeURI;
|
||||
this._validateURI = config.validateURI;
|
||||
this._icons = config.icons;
|
||||
this._modeOptions = config.modeOptions;
|
||||
|
||||
this._initializePaths(config.pathRefs);
|
||||
},
|
||||
|
@ -37,12 +40,6 @@ JX.install('OwnersPathEditor', {
|
|||
*/
|
||||
_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.
|
||||
*/
|
||||
|
@ -65,6 +62,8 @@ JX.install('OwnersPathEditor', {
|
|||
* default for future rows.
|
||||
*/
|
||||
_lastRepositoryChoice : null,
|
||||
_icons: null,
|
||||
_modeOptions: null,
|
||||
|
||||
/*
|
||||
* Initialize with 0 or more rows.
|
||||
|
@ -85,68 +84,34 @@ JX.install('OwnersPathEditor', {
|
|||
addPath : function(path_ref) {
|
||||
// Smart default repository. See _lastRepositoryChoice.
|
||||
if (path_ref) {
|
||||
this._lastRepositoryChoice = path_ref.repositoryPHID;
|
||||
this._lastRepositoryChoice = path_ref.repositoryValue;
|
||||
} else {
|
||||
path_ref = {
|
||||
repositoryValue: this._lastRepositoryChoice || {}
|
||||
};
|
||||
}
|
||||
path_ref = path_ref || {};
|
||||
|
||||
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() {
|
||||
this._lastRepositoryChoice = repo_select.value;
|
||||
}));
|
||||
|
||||
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 repo = this._newRepoCell(path_ref.repositoryValue);
|
||||
var path = this._newPathCell(path_ref.display);
|
||||
var icon = this._newIconCell();
|
||||
var mode_cell = this._newModeCell(path_ref.excluded);
|
||||
|
||||
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({
|
||||
repo_select : repo_select,
|
||||
path_input : path_input,
|
||||
hardpoint : hardpoint,
|
||||
error_display : error_display,
|
||||
repositoryTokenizer: repo.tokenizer,
|
||||
path_input : path.input,
|
||||
hardpoint : path.hardpoint,
|
||||
error_display : icon.cell,
|
||||
completeURI : this._completeURI,
|
||||
validateURI : this._validateURI
|
||||
validateURI : this._validateURI,
|
||||
icons: this._icons
|
||||
}).start();
|
||||
|
||||
this._count++;
|
||||
|
@ -158,20 +123,109 @@ JX.install('OwnersPathEditor', {
|
|||
this.addPath();
|
||||
},
|
||||
|
||||
/**
|
||||
* Helper to build the options for the repository choice dropdown.
|
||||
*/
|
||||
_buildRepositoryOptions : function(selected) {
|
||||
var repos = this._repositories;
|
||||
var result = [];
|
||||
for (var k in repos) {
|
||||
var attr = {
|
||||
value : k,
|
||||
selected : (selected == k)
|
||||
};
|
||||
result.push(JX.$N('option', attr, repos[k]));
|
||||
_newModeCell: function(value) {
|
||||
var options = this._modeOptions;
|
||||
|
||||
var name = 'exclude[' + this._count + ']';
|
||||
|
||||
var control = JX.Prefab.renderSelect(options, value, {name: name});
|
||||
|
||||
return JX.$N(
|
||||
'td',
|
||||
{
|
||||
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];
|
||||
}
|
||||
return result;
|
||||
|
||||
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
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
});
|
||||
|
|
|
@ -15,6 +15,7 @@ JX.install('PHUIXFormControl', {
|
|||
_valueSetCallback: null,
|
||||
_valueGetCallback: null,
|
||||
_rawInputNode: null,
|
||||
_tokenizer: null,
|
||||
|
||||
setLabel: function(label) {
|
||||
JX.DOM.setContent(this._getLabelNode(), label);
|
||||
|
@ -70,6 +71,7 @@ JX.install('PHUIXFormControl', {
|
|||
this._valueGetCallback = input.get;
|
||||
this._valueSetCallback = input.set;
|
||||
this._rawInputNode = input.node;
|
||||
this._tokenizer = input.tokenizer || null;
|
||||
|
||||
return this;
|
||||
},
|
||||
|
@ -87,6 +89,10 @@ JX.install('PHUIXFormControl', {
|
|||
return this._rawInputNode;
|
||||
},
|
||||
|
||||
getTokenizer: function() {
|
||||
return this._tokenizer;
|
||||
},
|
||||
|
||||
getNode: function() {
|
||||
if (!this._node) {
|
||||
|
||||
|
@ -168,7 +174,8 @@ JX.install('PHUIXFormControl', {
|
|||
return {
|
||||
node: build.node,
|
||||
get: get_value,
|
||||
set: set_value
|
||||
set: set_value,
|
||||
tokenizer: build.tokenizer
|
||||
};
|
||||
},
|
||||
|
||||
|
|
Loading…
Reference in a new issue