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; | |||||
| } | } | ||||
| } | } | ||||
| }); | }); | ||||