Files
awx/awx/ui/static/js/helpers/search.js
Chris Houseknecht da766edd52 Inventory refactor bug
Calculating initial container heights and rowsize on the inventory page was not working correctly on first page load. Issue was misalignment of function declaration and injector. Also needed to add e.stopPropagtion() call in the search helper. Without it the group search refresh gets called multiple times whenever the host list is refreshed.
2014-06-04 11:45:11 -04:00

526 lines
29 KiB
JavaScript

/*********************************************
* Copyright (c) 2014 AnsibleWorks, Inc.
*
* SearchHelper
*
* All the parts for controlling the search widget on
* related collections.
*
* SearchInit({
* scope: <scope>,
* set: <model name (i.e. organizations) used in ng-repeat>
* url: <default api url used to load data>
* list: <list object used by ListGenerator>
* });
*
*/
'use strict';
angular.module('SearchHelper', ['RestServices', 'Utilities', 'RefreshHelper'])
.factory('SearchInit', ['Alert', 'Rest', 'Refresh', '$location', 'GetBasePath', 'Empty', '$timeout', 'Wait', 'Store',
function (Alert, Rest, Refresh, $location, GetBasePath, Empty, $timeout, Wait, Store) {
return function (params) {
var scope = params.scope,
set = params.set,
defaultUrl = params.url,
list = params.list,
iterator = (params.iterator) ? params.iterator : list.iterator,
setWidgets = (params.setWidgets === false) ? false : true,
sort_order = params.sort_order || '',
widgets, i, modifier;
function setDefaults(widget) {
// Set default values
var f, fld, fka, modifier;
modifier = (widget === undefined || widget === 1) ? '' : widget;
scope[iterator + 'SearchField' + modifier] = '';
scope[iterator + 'SearchFieldLabel' + modifier] = '';
for (fld in list.fields) {
if (list.fields[fld].searchWidget === undefined && widget === 1 ||
list.fields[fld].searchWidget === widget) {
if (list.fields[fld].key) {
if (list.fields[fld].sourceModel) {
fka = list.fields[fld].sourceModel + '__' + list.fields[fld].sourceField;
sort_order = (list.fields[fld].desc) ? '-' + fka : fka;
} else {
sort_order = (list.fields[fld].desc) ? '-' + fld : fld;
}
if (list.fields[fld].searchable === undefined || list.fields[fld].searchable === true) {
scope[iterator + 'SearchField' + modifier] = fld;
scope[iterator + 'SearchFieldLabel' + modifier] = list.fields[fld].label;
}
break;
}
}
}
// Default the search field to 'defaultSearchField', if one exists
for (fld in list.fields) {
if (list.fields[fld].searchWidget === undefined && widget === 1 ||
list.fields[fld].searchWidget === widget) {
if (list.fields[fld].defaultSearchField) {
scope[iterator + 'SearchField' + modifier] = fld;
scope[iterator + 'SearchFieldLabel' + modifier] = list.fields[fld].label;
}
}
}
// A field marked as key may not be 'searchable', and there might not be a 'defaultSearchField',
// so find the first searchable field.
if (Empty(scope[iterator + 'SearchField' + modifier])) {
for (fld in list.fields) {
if (list.fields[fld].searchWidget === undefined && widget === 1 ||
list.fields[fld].searchWidget === widget) {
if (list.fields[fld].searchable === undefined || list.fields[fld].searchable === true) {
scope[iterator + 'SearchField' + modifier] = fld;
scope[iterator + 'SearchFieldLabel' + modifier] = list.fields[fld].label;
break;
}
}
}
}
scope[iterator + 'SearchType' + modifier] = 'icontains';
scope[iterator + 'SearchTypeLabel' + modifier] = 'Contains';
scope[iterator + 'SearchParams' + modifier] = '';
scope[iterator + 'SearchValue' + modifier] = '';
scope[iterator + 'SelectShow' + modifier] = false; // show/hide the Select
scope[iterator + 'HideSearchType' + modifier] = false;
scope[iterator + 'InputDisable' + modifier] = false;
scope[iterator + 'ExtraParms' + modifier] = '';
scope[iterator + 'ShowStartBtn' + modifier] = true;
scope[iterator + 'HideAllStartBtn' + modifier] = false;
if (list.fields[scope[iterator + 'SearchField' + modifier]] &&
list.fields[scope[iterator + 'SearchField' + modifier]].searchPlaceholder) {
if (scope[list.fields[scope[iterator + 'SearchField' + modifier]].searchPlaceholder]) {
// if set to a scope variable
scope[iterator + 'SearchPlaceholder' + modifier] = scope[list.fields[scope[iterator + 'SearchField' + modifier]].searchPlaceholder];
} else {
// Set to a string value in the list definition
scope[iterator + 'SearchPlaceholder' + modifier] = list.fields[scope[iterator + 'SearchField' + modifier]].searchPlaceholder;
}
} else {
// Default value
scope[iterator + 'SearchPlaceholder' + modifier] = 'Search';
}
scope[iterator + 'InputDisable' + modifier] =
(list.fields[scope[iterator + 'SearchField' + modifier]] &&
list.fields[scope[iterator + 'SearchField' + modifier]].searchObject === 'all') ? true : false;
f = scope[iterator + 'SearchField' + modifier];
if (list.fields[f]) {
if (list.fields[f].searchType && (list.fields[f].searchType === 'boolean' ||
list.fields[f].searchType === 'select')) {
scope[iterator + 'SelectShow' + modifier] = true;
scope[iterator + 'SearchSelectOpts' + modifier] = list.fields[f].searchOptions;
}
if (list.fields[f].searchType && list.fields[f].searchType === 'int') {
scope[iterator + 'HideSearchType' + modifier] = true;
}
if (list.fields[f].searchType && list.fields[f].searchType === 'gtzero') {
scope[iterator + 'InputHide' + modifier] = true;
}
}
}
if (setWidgets) {
// Set default values for each search widget on the page
widgets = (list.searchWidgets) ? list.searchWidgets : 1;
for (i = 1; i <= widgets; i++) {
modifier = (i === 1) ? '' : i;
if ($('#search-widget-container' + modifier)) {
setDefaults(i);
}
}
}
scope[iterator + '_current_search_params'] = {
set: set,
defaultUrl: defaultUrl,
list: list,
iterator: iterator,
sort_order: sort_order
};
Store(iterator + '_current_search_params', scope[iterator + '_current_search_params']);
Store('CurrentSearchParams', scope[iterator + '_current_search_params']); // Keeping this around for activity stream
// Functions to handle search widget changes
scope.setSearchField = function (iterator, fld, label, widget) {
var modifier = (widget === undefined || widget === 1) ? '' : widget;
scope[iterator + 'SearchFieldLabel' + modifier] = label;
scope[iterator + 'SearchField' + modifier] = fld;
scope[iterator + 'SearchValue' + modifier] = '';
scope[iterator + 'SelectShow' + modifier] = false;
scope[iterator + 'HideSearchType' + modifier] = false;
scope[iterator + 'InputHide' + modifier] = false;
scope[iterator + 'SearchType' + modifier] = 'icontains';
scope[iterator + 'InputDisable' + modifier] = (list.fields[fld].searchObject === 'all') ? true : false;
scope[iterator + 'ShowStartBtn' + modifier] = true;
if (list.fields[scope[iterator + 'SearchField' + modifier]] &&
list.fields[scope[iterator + 'SearchField' + modifier]].searchPlaceholder) {
if (scope[list.fields[scope[iterator + 'SearchField' + modifier]].searchPlaceholder]) {
// if set to a scope variable
scope[iterator + 'SearchPlaceholder' + modifier] = scope[list.fields[scope[iterator + 'SearchField' +
modifier]].searchPlaceholder];
} else {
// Set to a string value in the list definition
scope[iterator + 'SearchPlaceholder' + modifier] = list.fields[scope[iterator + 'SearchField' +
modifier]].searchPlaceholder;
}
} else {
// Default value
scope[iterator + 'SearchPlaceholder' + modifier] = 'Search';
}
if (list.fields[fld].searchType && list.fields[fld].searchType === 'gtzero') {
scope[iterator + "InputDisable" + modifier] = true;
scope[iterator + 'ShowStartBtn' + modifier] = false;
scope.search(iterator);
} else if (list.fields[fld].searchSingleValue) {
// Query a specific attribute for one specific value
// searchSingleValue: true
// searchType: 'boolean|int|etc.'
// searchValue: < value to match for boolean use 'true'|'false' >
scope[iterator + 'InputDisable' + modifier] = true;
scope[iterator + "SearchValue" + modifier] = list.fields[fld].searchValue;
// For boolean type, SearchValue must be an object
if (list.fields[fld].searchType === 'boolean' && list.fields[fld].searchValue === 'true') {
scope[iterator + "SearchSelectValue" + modifier] = {
value: 1
};
} else if (list.fields[fld].searchType === 'boolean' && list.fields[fld].searchValue === 'false') {
scope[iterator + "SearchSelectValue" + modifier] = {
value: 0
};
} else {
scope[iterator + "SearchSelectValue" + modifier] = {
value: list.fields[fld].searchValue
};
}
scope[iterator + 'ShowStartBtn' + modifier] = false;
} else if (list.fields[fld].searchType === 'in') {
scope[iterator + "SearchType" + modifier] = 'in';
scope[iterator + "SearchValue" + modifier] = list.fields[fld].searchValue;
scope[iterator + "InputDisable" + modifier] = true;
scope[iterator + 'ShowStartBtn' + modifier] = false;
} else if (list.fields[fld].searchType && (list.fields[fld].searchType === 'boolean' ||
list.fields[fld].searchType === 'select' || list.fields[fld].searchType === 'select_or')) {
scope[iterator + 'SelectShow' + modifier] = true;
scope[iterator + 'SearchSelectOpts' + modifier] = list.fields[fld].searchOptions;
scope[iterator + 'SearchType' + modifier] = '';
} else if (list.fields[fld].searchType && list.fields[fld].searchType === 'int') {
//scope[iterator + 'HideSearchType' + modifier] = true;
scope[iterator + 'SearchType' + modifier] = 'int';
} else if (list.fields[fld].searchType && list.fields[fld].searchType === 'isnull') {
scope[iterator + 'SearchType' + modifier] = 'isnull';
scope[iterator + 'InputDisable' + modifier] = true;
scope[iterator + 'SearchValue' + modifier] = 'true';
scope[iterator + 'ShowStartBtn' + modifier] = false;
}
scope.search(iterator);
};
scope.resetSearch = function (iterator) {
// Respdond to click of reset button
var i,
widgets = (list.searchWidgets) ? list.searchWidgets : 1;
for (i = 1; i <= widgets; i++) {
// Clear each search widget
setDefaults(i);
}
// Force removal of search keys from the URL
window.location = '/#' + $location.path();
scope.search(iterator);
};
if (scope.removeDoSearch) {
scope.removeDoSearch();
}
scope.removeDoSearch = scope.$on('doSearch', function (e, iterator, page, load, calcOnly, deferWaitStop) {
//
// Execute the search
//
var url = (calcOnly) ? '' : defaultUrl,
connect;
if (!calcOnly) {
scope[iterator + 'Loading'] = (load === undefined || load === true) ? true : false;
scope[iterator + 'Page'] = (page) ? parseInt(page) - 1 : 0;
}
//finalize and execute the query
if (scope[iterator + 'SearchParams']) {
if (/\/$/.test(url)) {
url += '?' + scope[iterator + 'SearchParams'];
} else {
url += '&' + scope[iterator + 'SearchParams'];
}
}
connect = (/\/$/.test(url)) ? '?' : '&';
url += (scope[iterator + '_page_size']) ? connect + 'page_size=' + scope[iterator + '_page_size'] : "";
if (page) {
connect = (/\/$/.test(url)) ? '?' : '&';
url += connect + 'page=' + page;
}
if (scope[iterator + 'ExtraParms']) {
connect = (/\/$/.test(url)) ? '?' : '&';
url += connect + scope[iterator + 'ExtraParms'];
}
url = url.replace(/\&\&/g, '&');
if (calcOnly) {
scope.$emit('searchParamsReady', url);
}
else if (defaultUrl && !/undefined/.test(url)) {
Refresh({
scope: scope,
set: set,
iterator: iterator,
url: url,
deferWaitStop: deferWaitStop
});
}
e.stopPropagation();
});
if (scope.removePrepareSearch) {
scope.removePrepareSearch();
}
scope.removePrepareSearch = scope.$on('prepareSearch', function (e, iterator, page, load, calcOnly, deferWaitStop) {
//
// Start building the search key/value pairs. This will process each search widget, if the
// selected field is an object type (used on activity stream).
//
Wait('start');
scope[iterator + 'SearchParams'] = '';
var i, modifier,
widgets = (list.searchWidgets) ? list.searchWidgets : 1;
for (i = 1; i <= widgets; i++) {
modifier = (i === 1) ? '' : i;
if ($('#search-widget-container' + modifier)) {
if (list.fields[scope[iterator + 'SearchField' + modifier]] &&
list.fields[scope[iterator + 'SearchField' + modifier]].searchObject) {
// Search field of object type
if (list.fields[scope[iterator + 'SearchField' + modifier]].searchObject !== 'all') {
// An object type is selected
scope[iterator + 'HideAllStartBtn' + modifier] = false;
if (scope[iterator + 'SearchValue' + modifier]) {
// A search value was entered
scope[iterator + 'ShowStartBtn' + modifier] = false;
if (list.fields[scope[iterator + 'SearchField' + modifier]].searchOnID) {
scope[iterator + 'SearchParams'] += '&' +
list.fields[scope[iterator + 'SearchField' + modifier]].searchObject +
'__id=' + scope[iterator + 'SearchValue' + modifier];
} else {
scope[iterator + 'SearchParams'] += '&' +
list.fields[scope[iterator + 'SearchField' + modifier]].searchObject +
'__name__icontains=' +
scope[iterator + 'SearchValue' + modifier];
}
} else {
// Search value is empty
scope[iterator + 'ShowStartBtn' + modifier] = true;
scope[iterator + 'SearchParams'] += '&' +
list.fields[scope[iterator + 'SearchField' + modifier]].searchField +
'=' + list.fields[scope[iterator + 'SearchField' + modifier]].searchObject;
}
} else {
// Object Type set to All
scope[iterator + 'HideAllStartBtn' + modifier] = true;
}
}
}
}
e.stopPropagation();
scope.$emit('prepareSearch2', iterator, page, load, calcOnly, deferWaitStop);
});
if (scope.removePrepareSearch2) {
scope.removePrepareSearch2();
}
scope.removePrepareSearch2 = scope.$on('prepareSearch2', function (e, iterator, page, load, calcOnly, deferWaitStop) {
// Continue building the search by examining the remaining search widgets. If we're looking at activity_stream,
// there's more than one.
var i, modifier,
widgets = (list.searchWidgets) ? list.searchWidgets : 1;
for (i = 1; i <= widgets; i++) {
modifier = (i === 1) ? '' : i;
scope[iterator + 'HoldInput' + modifier] = true;
if ($('#search-widget-container' + modifier) &&
list.fields[scope[iterator + 'SearchField' + modifier]] && !list.fields[scope[iterator + 'SearchField' + modifier]].searchObject) {
// if the search widget exists and its value is not an object, add its parameters to the query
if (scope[iterator + 'SearchValue' + modifier]) {
// if user typed a value in the input box, show the reset link
scope[iterator + 'ShowStartBtn' + modifier] = false;
} else {
scope[iterator + 'ShowStartBtn' + modifier] = true;
}
if ((!scope[iterator + 'SelectShow' + modifier] && !Empty(scope[iterator + 'SearchValue' + modifier])) ||
(scope[iterator + 'SelectShow' + modifier] && scope[iterator + 'SearchSelectValue' + modifier]) ||
(list.fields[scope[iterator + 'SearchField' + modifier]] &&
list.fields[scope[iterator + 'SearchField' + modifier]].searchType === 'gtzero')) {
if (list.fields[scope[iterator + 'SearchField' + modifier]].searchField) {
scope[iterator + 'SearchParams'] += '&' + list.fields[scope[iterator + 'SearchField' + modifier]].searchField + '__';
} else if (list.fields[scope[iterator + 'SearchField' + modifier]].sourceModel) {
// handle fields whose source is a related model e.g. inventories.organization
scope[iterator + 'SearchParams'] += '&' + list.fields[scope[iterator + 'SearchField' + modifier]].sourceModel + '__' +
list.fields[scope[iterator + 'SearchField' + modifier]].sourceField + '__';
} else if ( list.fields[scope[iterator + 'SearchField' + modifier]].searchType === 'select' &&
Empty(scope[iterator + 'SearchSelectValue' + modifier].value) ) {
scope[iterator + 'SearchParams'] += '&' + scope[iterator + 'SearchField' + modifier] + '__';
} else if ( list.fields[scope[iterator + 'SearchField' + modifier]].searchType === 'select' &&
!Empty(scope[iterator + 'SearchSelectValue' + modifier].value) ) {
scope[iterator + 'SearchParams'] += '&' + scope[iterator + 'SearchField' + modifier];
} else {
scope[iterator + 'SearchParams'] += '&' + scope[iterator + 'SearchField' + modifier] + '__';
}
if (list.fields[scope[iterator + 'SearchField' + modifier]].searchType &&
(list.fields[scope[iterator + 'SearchField' + modifier]].searchType === 'int' ||
list.fields[scope[iterator + 'SearchField' + modifier]].searchType === 'boolean')) {
scope[iterator + 'SearchParams'] += 'int=';
} else if (list.fields[scope[iterator + 'SearchField' + modifier]].searchType &&
list.fields[scope[iterator + 'SearchField' + modifier]].searchType === 'gtzero') {
scope[iterator + 'SearchParams'] += 'gt=0';
} else if ( (list.fields[scope[iterator + 'SearchField' + modifier]].searchType === 'select') &&
Empty(scope[iterator + 'SearchSelectValue' + modifier].value) && !/\_\_$/.test(scope[iterator + 'SearchParams']) ) {
scope[iterator + 'SearchParams'] += '=iexact=';
} else if (/\_\_$/.test(scope[iterator + 'SearchParams'])) {
scope[iterator + 'SearchParams'] += 'icontains=';
} else {
scope[iterator + 'SearchParams'] += scope[iterator + 'SearchType' + modifier] + '=';
}
if (list.fields[scope[iterator + 'SearchField' + modifier]].searchType &&
(list.fields[scope[iterator + 'SearchField' + modifier]].searchType === 'boolean' ||
list.fields[scope[iterator + 'SearchField' + modifier]].searchType === 'select')) {
scope[iterator + 'SearchParams'] += scope[iterator + 'SearchSelectValue' + modifier].value;
} else {
if ((!list.fields[scope[iterator + 'SearchField' + modifier]].searchType) ||
(list.fields[scope[iterator + 'SearchField' + modifier]].searchType &&
list.fields[scope[iterator + 'SearchField' + modifier]].searchType !== 'or' &&
list.fields[scope[iterator + 'SearchField' + modifier]].searchType !== 'gtzero')) {
scope[iterator + 'SearchParams'] += encodeURI(scope[iterator + 'SearchValue' + modifier]);
}
}
}
}
}
if ((iterator === 'inventory' && scope.inventoryFailureFilter) ||
(iterator === 'host' && scope.hostFailureFilter)) {
//Things that bypass the search widget. Should go back and add a second widget possibly on
//inventory pages and eliminate this
scope[iterator + 'SearchParams'] += '&has_active_failures=true';
}
if (sort_order) {
scope[iterator + 'SearchParams'] += (scope[iterator + 'SearchParams']) ? '&' : '';
scope[iterator + 'SearchParams'] += 'order_by=' + encodeURI(sort_order);
}
e.stopPropagation();
scope.$emit('doSearch', iterator, page, load, calcOnly, deferWaitStop);
});
scope.startSearch = function (e, iterator) {
// If use clicks enter while on input field, start the search
if (e.keyCode === 13) {
scope.search(iterator);
}
};
/**
* Initiate a searh.
*
* @iterator: required, list.iterator value
* @Page: optional. Added to accomodate back function on Job Events detail.
* @Load: optional, set to false if 'Loading' message not desired
* @calcOnly: optional, set to true when you want to calc or figure out search params without executing the search
* @deferWaitStop: optional, when true refresh.js will NOT issue Wait('stop'), thus leaving the spinner. Caller is then
* responsible for stopping the spinner post refresh.
*/
scope.search = function (iterator, page, load, calcOnly, deferWaitStop) {
page = page || null;
load = (load || !scope[set] || scope[set].length === 0) ? true : false;
calcOnly = (calcOnly) ? true : false;
deferWaitStop = (deferWaitStop) ? true : false;
if (load) {
scope[set] = []; //clear the list array to make sure 'Loading' is the only thing visible on the list
}
scope.$emit('prepareSearch', iterator, page, load, calcOnly, deferWaitStop);
};
scope.sort = function (iterator, fld) {
// Reset sort icons back to 'icon-sort' on all columns
// except the one clicked.
$('.list-header').each(function () {
if ($(this).attr('id') !== iterator + '-' + fld + '-header') {
var icon = $(this).find('i');
icon.attr('class', 'fa fa-sort');
}
});
// Toggle the icon for the clicked column
// and set the sort direction
var icon = $('#' + iterator + '-' + fld + '-header i'),
direction = '';
if (icon.hasClass('fa-sort')) {
icon.removeClass('fa-sort');
icon.addClass('fa-sort-up');
} else if (icon.hasClass('fa-sort-up')) {
icon.removeClass('fa-sort-up');
icon.addClass('fa-sort-down');
direction = '-';
} else if (icon.hasClass('fa-sort-down')) {
icon.removeClass('fa-sort-down');
icon.addClass('fa-sort-up');
}
// Set the sorder order value and call the API to refresh the list with the new order
if (list.fields[fld].searchField) {
sort_order = direction + list.fields[fld].searchField;
} else if (list.fields[fld].sortField) {
sort_order = direction + list.fields[fld].sortField;
} else {
if (list.fields[fld].sourceModel) {
sort_order = direction + list.fields[fld].sourceModel + '__' + list.fields[fld].sourceField;
} else {
sort_order = direction + fld;
}
}
scope[list.iterator + '_current_search_params'].sort_order = sort_order;
Store(iterator + '_current_search_params', scope[iterator + '_current_search_params']);
scope.search(list.iterator);
};
// Call after modal dialogs to remove any lingering callbacks
scope.searchCleanup = function () {
scope.removeDoSearch();
scope.removePrepareSearch();
scope.removePrepareSearch2();
};
};
}
]);