define('jira/field/assignee-picker', ['jira/util/formatter', 'jira/ajs/select/scrollable-single-select', 'jira/ajs/select/suggestions/assignee-suggest-handler', 'jira/ajs/list/group-descriptor', 'jira/ajs/list/item-descriptor', 'wrm/context-path', 'aui/params', 'jquery', 'underscore'], function (formatter, ScrollableSingleSelect, AssigneeSuggestHandler, GroupDescriptor, ItemDescriptor, wrmContextPath, params, $, _) { 'use strict'; var contextPath = wrmContextPath(); function getAssigneeGroupSearch(suggestions) { return _.find(suggestions, function (suggestion) { return suggestion.id() === 'assignee-group-search'; }); } /** * A single-select list for selecting Assignees. Assignees in the list are in two groups: * * - Suggestions: local data, consisting of recent assignees for the issue and current * user, plus the reporter * - Search: AJAX data, found from all user assignable for the current project * * @class AssigneePicker * @extends SingleSelect */ return ScrollableSingleSelect.extend({ /** * @param {Object} options * @constructs */ init: function init(options) { var instance = this; var element = options.element instanceof $ ? options.element : $(options.element); instance._pagination = { lastQuery: null, numberOfShowingItems: 0, allResultsDisplayed: false, requestNextPagePromise: null }; // Returns the data sent to the server for the AJAX search function data(query) { params.actionDescriptorId = undefined; AJS.populateParameters(); return { username: query, projectKeys: params.projectKeys, issueKey: params.assigneeEditIssueKey, actionDescriptorId: params.actionDescriptorId, maxResults: 50, startAt: instance._pagination.numberOfShowingItems === 0 ? 0 : instance._pagination.numberOfShowingItems + 1 }; } function formatResponse(response) { var ret = []; if (response.length) { // Search results var groupDescriptor = new GroupDescriptor({ weight: 1, // index of group in dropdown id: "assignee-group-search", uniqueItemScope: 'container', replace: true, // Allow subsequent calls to replace model items label: formatter.I18n.getText("assignee.picker.group.search") }); for (var i = 0, len = response.length; i < len; i++) { var user = response[i]; var username = user.name; var displayName = user.displayName; var emailAddress = user.emailAddress; var label = displayName; if (emailAddress) { label += ' - ' + emailAddress; } label += ' (' + username + ')'; groupDescriptor.addItem(new ItemDescriptor({ value: username, fieldText: displayName, label: label, allowDuplicate: false, icon: user.avatarUrls['16x16'] })); } ret.push(groupDescriptor); } return ret; } options = $.extend(true, {}, { submitInputVal: true, showDropdownButton: !!element.data('show-dropdown-button'), errorMessage: formatter.I18n.getText("assignee.picker.invalid.user"), localDataGroupId: 'assignee-group-suggested', content: "mixed", suggestionAtTop: true, removeDuplicates: true, ajaxOptions: { url: function url() { //reset the assigneeEditIssueKey param, so that when we go from an quickedit dialog to a quick create dialog for //example the value isn't set! params.assigneeEditIssueKey = undefined; AJS.populateParameters(); var path = params.assigneeEditIssueKey ? 'search' : 'multiProjectSearch'; return contextPath + "/rest/api/latest/user/assignable/" + path; }, query: true, // keep going back to the server for each keystroke minQueryLength: 0, data: data, formatResponse: formatResponse } }, options); if (options.editValue == "-1") { // override value in "data-editValue" for SingleSelect._setOptions -> getOptionsFromAttributes() // so on error the "Automatic" is selected instead of verbatim "-1" string in SingleSelect._setInitState options.editValue = false; element.val("-1"); } this._super(options); this.suggestionsHandler = new AssigneeSuggestHandler(this.options, this.model); }, /** * Reset pagination info when the query is changed. Increase numberOfShowingItems when receive results from server. */ requestSuggestions: function requestSuggestions(force) { if (this._pagination.lastQuery != this.getQueryVal()) { this._resetPagination(); } return this._super(force).done(_.bind(function (suggestions) { var group = getAssigneeGroupSearch(suggestions); if (group) { var groupItems = group.items(); if (groupItems && groupItems.length) { this._pagination.numberOfShowingItems += groupItems.length; } } return suggestions; }, this)); }, /** * When the list is scrolled to bottom, send request for next items and append results to current list */ scrolledToBottomHandler: function scrolledToBottomHandler() { if ((!this._pagination.requestNextPagePromise || this._pagination.requestNextPagePromise.state() === "resolved") && !this._pagination.allResultsDisplayed) { this._pagination.requestNextPagePromise = this.requestSuggestions(true).done(_.bind(function (suggestions) { var group = getAssigneeGroupSearch(suggestions); if (group) { var groupItems = group.items(); if (groupItems && groupItems.length) { this.listController.appendToGroupDescriptor('assignee-group-search', groupItems); this.listController.addNextPage(); } else { this._pagination.allResultsDisplayed = true; } } else { this._pagination.allResultsDisplayed = true; } }, this)); } }, /** * Only handle when the query is changed */ _handleCharacterInput: function _handleCharacterInput(force) { if (this._pagination.lastQuery != this.getQueryVal()) { this._super(force); } else if (force) { this.showSuggestions(); } }, /** * Handle the case where the entry was deleted - on blur, set the Assignee to 'Automatic'. */ handleFreeInput: function handleFreeInput(value) { if ("" === $.trim(value || this.$field.val())) { this.setSelection(this.model.getDescriptor("-1")); } else { this._super(value); } this._resetPagination(); }, /** * Assignee Picker is a special case as we have