Changeset View
Changeset View
Standalone View
Standalone View
webroot/rsrc/js/application/owners/OwnersPathEditor.js
/** | /** | ||||
* @requires multirow-row-manager | * @requires multirow-row-manager | ||||
* javelin-install | * javelin-install | ||||
* path-typeahead | * path-typeahead | ||||
* 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 | ||||
*/ | */ | ||||
JX.install('OwnersPathEditor', { | JX.install('OwnersPathEditor', { | ||||
construct : function(config) { | construct : function(config) { | ||||
var root = JX.$(config.root); | var root = JX.$(config.root); | ||||
this._rowManager = new JX.MultirowRowManager( | this._rowManager = new JX.MultirowRowManager( | ||||
JX.DOM.find(root, 'table', config.table)); | JX.DOM.find(root, 'table', config.table)); | ||||
JX.DOM.listen( | JX.DOM.listen( | ||||
JX.DOM.find(root, 'a', config.add_button), | JX.DOM.find(root, 'a', config.add_button), | ||||
'click', | 'click', | ||||
null, | null, | ||||
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); | ||||
}, | }, | ||||
members : { | members : { | ||||
/* | /* | ||||
* MultirowRowManager for controlling add/remove behavior | * MultirowRowManager for controlling add/remove behavior | ||||
*/ | */ | ||||
_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. | ||||
*/ | */ | ||||
_count : 0, | _count : 0, | ||||
/* | /* | ||||
* URL for the typeahead datasource. | * URL for the typeahead datasource. | ||||
*/ | */ | ||||
_completeURI : null, | _completeURI : null, | ||||
/* | /* | ||||
* URL for path validation requests. | * URL for path validation requests. | ||||
*/ | */ | ||||
_validateURI : null, | _validateURI : null, | ||||
/* | /* | ||||
* Template typeahead markup to be copied per row. | * Template typeahead markup to be copied per row. | ||||
*/ | */ | ||||
_inputTemplate : null, | _inputTemplate : null, | ||||
/* | /* | ||||
* Most packages will be in one repository, so remember whenever | * Most packages will be in one repository, so remember whenever | ||||
* the user chooses a repository, and use that repository as the | * the user chooses a repository, and use that repository as the | ||||
* 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. | ||||
* Adds one initial row if none are given. | * Adds one initial row if none are given. | ||||
*/ | */ | ||||
_initializePaths : function(path_refs) { | _initializePaths : function(path_refs) { | ||||
for (var k in path_refs) { | for (var k in path_refs) { | ||||
this.addPath(path_refs[k]); | this.addPath(path_refs[k]); | ||||
} | } | ||||
if (!JX.keys(path_refs).length) { | if (!JX.keys(path_refs).length) { | ||||
this.addPath(); | this.addPath(); | ||||
} | } | ||||
}, | }, | ||||
/* | /* | ||||
* Build a row. | * Build a row. | ||||
*/ | */ | ||||
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 = { | |||||
repositoryValue: this._lastRepositoryChoice || {} | |||||
}; | |||||
} | } | ||||
path_ref = path_ref || {}; | |||||
var selected_repository = path_ref.repositoryPHID || | var repo = this._newRepoCell(path_ref.repositoryValue); | ||||
this._lastRepositoryChoice; | var path = this._newPathCell(path_ref.display); | ||||
var options = this._buildRepositoryOptions(selected_repository); | var icon = this._newIconCell(); | ||||
var attrs = { | var mode_cell = this._newModeCell(path_ref.excluded); | ||||
name : 'repo[' + this._count + ']', | |||||
className : 'owners-repo' | var row = this._rowManager.addRow( | ||||
}; | [ | ||||
var repo_select = JX.$N('select', attrs, options); | mode_cell, | ||||
repo.cell, | |||||
path.cell, | |||||
icon.cell | |||||
]); | |||||
new JX.PathTypeahead({ | |||||
repositoryTokenizer: repo.tokenizer, | |||||
path_input : path.input, | |||||
hardpoint : path.hardpoint, | |||||
error_display : icon.cell, | |||||
completeURI : this._completeURI, | |||||
validateURI : this._validateURI, | |||||
icons: this._icons | |||||
}).start(); | |||||
this._count++; | |||||
return row; | |||||
}, | |||||
_onaddpath : function(e) { | |||||
e.kill(); | |||||
this.addPath(); | |||||
}, | |||||
_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]; | |||||
} | |||||
var input = JX.$N( | |||||
'input', | |||||
{ | |||||
type: 'hidden', | |||||
name: name, | |||||
value: get_phid() | |||||
}); | |||||
JX.DOM.listen(repo_select, 'change', null, JX.bind(this, function() { | repo_tokenizer.listen('change', JX.bind(this, function() { | ||||
this._lastRepositoryChoice = repo_select.value; | this._lastRepositoryChoice = repo_tokenizer.getTokens(); | ||||
input.value = get_phid(); | |||||
})); | })); | ||||
var repo_cell = JX.$N('td', {}, repo_select); | var cell = JX.$N( | ||||
var typeahead_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', | 'td', | ||||
{ | |||||
className: 'owners-path-path-control' | |||||
}, | |||||
JX.$H(this._inputTemplate)); | JX.$H(this._inputTemplate)); | ||||
// Text input for path. | var path_input = JX.DOM.find(path_cell, 'input'); | ||||
var path_input = JX.DOM.find(typeahead_cell, 'input'); | |||||
JX.copy( | JX.copy( | ||||
path_input, | path_input, | ||||
{ | { | ||||
value : path_ref.display || '', | value: value || '', | ||||
name : 'path[' + this._count + ']' | name: 'path[' + this._count + ']' | ||||
}); | }); | ||||
// The Typeahead requires a display div called hardpoint. | |||||
var hardpoint = JX.DOM.find( | var hardpoint = JX.DOM.find( | ||||
typeahead_cell, | path_cell, | ||||
'div', | 'div', | ||||
'typeahead-hardpoint'); | 'typeahead-hardpoint'); | ||||
var error_display = JX.$N( | return { | ||||
'div', | cell: path_cell, | ||||
{ | input: path_input, | ||||
className : 'error-display validating' | hardpoint: hardpoint | ||||
}, | }; | ||||
'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( | |||||
[exclude_cell, repo_cell, typeahead_cell, error_display_cell]); | |||||
new JX.PathTypeahead({ | |||||
repo_select : repo_select, | |||||
path_input : path_input, | |||||
hardpoint : hardpoint, | |||||
error_display : error_display, | |||||
completeURI : this._completeURI, | |||||
validateURI : this._validateURI | |||||
}).start(); | |||||
this._count++; | |||||
return row; | |||||
}, | }, | ||||
_onaddpath : function(e) { | _newIconCell: function() { | ||||
e.kill(); | var cell = JX.$N( | ||||
this.addPath(); | 'td', | ||||
}, | { | ||||
className: 'owners-path-icon-control' | |||||
}); | |||||
/** | return { | ||||
* Helper to build the options for the repository choice dropdown. | cell: cell | ||||
*/ | |||||
_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])); | |||||
} | |||||
return result; | |||||
} | } | ||||
} | } | ||||
}); | }); |