(function () {
var Ext = window.Ext4 || window.Ext;
/**
* A picker that lets you select a project from any workspace.
*
* @example
* Ext.create('Ext.Container', {
* items: [{
* xtype: 'rallyprojectpicker'
* }],
* renderTo: Ext.getBody().dom
* });
*
*/
Ext.define('Rally.ui.picker.project.ProjectPicker', {
extend: 'Ext.form.field.Picker',
alias: 'widget.rallyprojectpicker',
requires: [
'Rally.data.wsapi.Model'
],
componentCls: 'rui-triggerfield rui-project-picker',
placeholderCls: 'rui-multi-object-placeholder',
editable: false,
pickerOffset: [0, -1],
matchFieldWidth: false,
config: {
/**
* @cfg {String}
* Text shown during initial load
*/
loadingText: 'Loading...',
/**
* @cfg {Boolean}
* If true, displays the projects the current user has most recently viewed.
*/
showMostRecentlyUsedProjects: true,
/**
* @cfg {Boolean}
* If true, displays the project scope up and down checkboxes below the project tree.
* Also, the field's value becomes a map, e.g.,
* {
* project: projectRecord,
* projectScopeUp: false,
* projectScopeDown: true
* }
*/
showProjectScopeUpAndDown: false,
/**
* @cfg {String}
* If workspace _ref is supplied, we overwrite topLevelModel and topLevelStoreConfig
* so that we only show projects under that workspace. We also do not show the top level workspace.
*/
workspace: undefined
},
constructor: function (config) {
this.mergeConfig(config);
this.callParent([this.config]);
},
initComponent: function () {
this.callParent(arguments);
this.addEvents(
/**
* @event
* Fired when the scope has changed.
*/
'change',
/**
* @event
* Fired if an invalid project is set on this field.
*/
'invalidvalue'
);
this.tree = this._createProjectTree();
},
/**
* @private
* Required to override when extending Ext.form.field.Picker
*/
createPicker: function () {
var items = [];
if (this.getShowMostRecentlyUsedProjects()) {
this.recents = this._createMostRecentlyUsedProjects();
items.push(this.recents);
}
items.push(this.tree);
if (this.getShowProjectScopeUpAndDown()) {
this.projectScopeUpDownField = this._createProjectScopeUpDownField();
items.push(this.projectScopeUpDownField);
}
this.picker = Ext.widget('container', {
cls: 'rui-project-picker-container',
itemId: 'projectPickerContainer',
floating: true,
shadow: false,
hidden: true,
minWidth: 250,
items: items
});
return this.picker;
},
_createProjectTree: function () {
return Ext.create('Rally.ui.tree.ProjectTree', {
workspace: this.getWorkspace(),
autoLoadTopLevel: false,
listeners: {
scope: this,
itemselected: this._treeItemSelected
}
});
},
_createMostRecentlyUsedProjects: function () {
return Ext.create('Rally.ui.picker.project.MostRecentlyUsedProjects', {
workspaceObjectID: Rally.environment.getContext().getWorkspace().ObjectID,
listeners: {
select: this._mostRecentlyUsedProjectSelected,
scope: this
}
});
},
_createProjectScopeUpDownField: function () {
return Ext.create('Rally.ui.picker.project.ProjectScopeUpDownField', {
value: {
projectScopeUp: this.projectScopeUp,
projectScopeDown: this.projectScopeDown
},
listeners: {
change: function (field, value) {
this.projectScopeUp = value.projectScopeUp;
this.projectScopeDown = value.projectScopeDown;
this.fireEvent('change', this, this.getValue());
},
scope: this
}
});
},
_mostRecentlyUsedProjectSelected: function (project) {
this.setSelectedRecord(project);
this.fireEvent('select');
this.collapse();
},
_treeItemSelected: function (treeItem) {
var record = treeItem.getRecord();
this.setSelectedRecord(record);
this.fireEvent('select');
this.collapse();
},
applyShowProjectScopeUpAndDown: function (value, oldValue) {
return value && Rally.environment.getContext().getSubscription().ProjectHierarchyEnabled;
},
/**
* Set the value for the picker.
* @param value {Object/Rally.data.wsapi.Model/String} A scope object (with project ref, scope up and down), a project ref String, or a project record.
*/
setValue: function (value) {
if (value instanceof Rally.data.wsapi.Model) {
this.setSelectedRecord(value);
return;
}
if (Ext.isString(value)) {
this.setValueForProjectRef(value);
} else if (Ext.isObject(value)) {
if (this.getShowProjectScopeUpAndDown()) {
this.projectScopeUp = value.projectScopeUp;
this.projectScopeDown = value.projectScopeDown;
if (this.projectScopeUpDownField) {
this.projectScopeUpDownField.setValue({
projectScopeUp: value.projectScopeUp,
projectScopeDown: value.projectScopeDown
});
}
}
this.setValueForProjectRef(value.project);
}
},
/**
* Overridden to return a scope object if #showProjectScopeUpAndDown is enabled, or the project ref if not.
* Call #getSelectedRecord if you need the fully hydrated project record.
*/
getValue: function () {
var ref = this.getSelectedRecord() ? this.getSelectedRecord().get('_ref') : undefined;
if (this.getShowProjectScopeUpAndDown()) {
return {
project: ref,
projectScopeUp: this.projectScopeUp,
projectScopeDown: this.projectScopeDown
};
} else {
return ref;
}
},
/**
* Given a ref string, loads the record and configures the project picker with it as the value.
* @param ref ref of a project
*/
setValueForProjectRef: function (ref) {
Rally.data.ModelFactory.getModel({
type: 'Project',
success: function (model) {
var oid = Rally.util.Ref.getOidFromRef(ref);
model.load(oid, {
success: function (record) {
this.setValue(record);
},
failure: function () {
//if the project doesn't exist or we don't have access to it.
//let our owner handle the error situation.
this.fireEvent('invalidvalue');
},
fetch: ['Name', 'Workspace'],
scope: this
});
},
scope: this
});
},
/**
* Set the text displayed in the picker text input.
* Does not set the value of the field, only what's displayed.
* @param value
*/
setDisplayValue: function (value) {
if (this.inputEl && this.inputEl.dom) {
this.inputEl.dom.value = value;
}
},
/**
* Sets the project show, however does not auto expand to the project.
* @param {Rally.data.wsapi.Model} record
*/
setSelectedRecord: function (record) {
this.projectRecord = record;
var displayValue = record && record.get('Name') || '';
this.setDisplayValue(displayValue);
this.fireEvent('change', this, this.getValue());
},
/**
* Get the selected project
* @return {Rally.data.wsapi.Model}
*/
getSelectedRecord: function () {
return this.projectRecord;
},
/**
* @override
* Overridden to wait for tree load before showing
*/
expand: function () {
if (this.isLoaded) {
this.removePlaceholderText();
this.callParent(arguments);
} else {
this.setPlaceholderText(this.getLoadingText());
this.tree.loadTopLevel().then({
success: function () {
this.isLoaded = true;
this.expand();
},
scope: this
});
}
},
setPlaceholderText: function (text) {
var inputEl = this.inputEl;
inputEl.addCls(this.placeholderCls);
this.prePlaceholderText = inputEl.dom.value;
inputEl.dom.value = text;
},
removePlaceholderText: function () {
var inputEl = this.inputEl;
if (inputEl.hasCls(this.placeholderCls)) {
inputEl.removeCls(this.placeholderCls);
inputEl.dom.value = this.prePlaceholderText;
}
},
getSubmitData: function () {
var data;
if (!this.disabled && this.submitValue) {
data = {};
data[this.getName()] = this.getValue();
}
return data;
},
destroy: function () {
if (this.tree) {
this.tree.destroy();
}
if (this.recents) {
this.recents.destroy();
}
if (this.projectScopeUpDownField) {
this.projectScopeUpDownField.destroy();
}
this.callParent(arguments);
}
});
})();