horizon/horizon/static/framework/widgets/magic-search/magic-overrides.directive.js

216 lines
7.0 KiB
JavaScript

(function () {
'use strict';
angular
.module('MagicSearch')
.directive('magicOverrides', magicOverrides);
/**
* @ngdoc directive
* @name MagicSearch:magicOverrides
* @description
* A directive to modify and extend Magic Search functionality for use in
* Horizon.
*
* 1. The base Magic Search widget makes Foundation (a responsive front-end
* framework) specific calls in showMenu and hideMenu. In Horizon we use
* Bootstrap, therefore we need to override those methods.
*
* 2. Added 'facetsChanged' listener so we can notify the base Magic Search
* widget that new facets are available. Use this if your table has dynamic
* facets.
*
* 3. Due to the current inconsistencies in the APIs, where some support
* filtering and others do not, we wanted a way to distinguish client-side
* filtering (searching the visible subset) vs server-side filtering
* (another server filter query).
*
* To support this distinction we overrode the methods 'removeFacet' and
* 'initfacets' to emit a 'checkFacets' event. Implementers can add property
* 'isServer' to facets (what triggers the facet icon and color difference).
*
* Each table that incorporates Magic Search is responsible for adding
* property 'isServer' to their facets as they have the intimate knowledge
* of the API supplying the table data. The setting of this property needs
* to be done in the Magic Search supporting JavaScript for each table.
*
* Example:
* Set property 'isServer' on facets that you want to render as server
* facet (server icon, lighter grey color). Note: If the property
* 'isServer' is not set, then facet renders with client icon and darker
* grey color.
*
* scope.$on('checkFacets', function (event, currentSearch) {
* angular.forEach(currentSearch, function (facet) {
* if (apiVersion < 3) {
* facet.isServer = true;
* }
* });
* });
*
* 4. Overrode 'initfacets' to fix refresh/bookmark issue where facets
* menu wasn't removing facets that were already on URL.
*
* @restrict A
* @scope
*
* @example
* ```
* <div class="magic-search" magic-overrides>
* ```
*/
function magicOverrides() {
var directive = {
restrict: 'A',
controller: MagicOverridesController
};
MagicOverridesController.$inject = [
'$element',
'$scope',
'$timeout',
'$window'
];
return directive;
function MagicOverridesController($element, $scope, $timeout, $window) {
/**
* showMenu and hideMenu depend on Foundation's dropdown. They need
* to be modified to work with another dropdown implementation.
* For Bootstrap, they are not needed at all.
*/
$scope.showMenu = function () {
$timeout(function () {
$scope.isMenuOpen = true;
});
};
$scope.hideMenu = function () {
$timeout(function () {
$scope.isMenuOpen = false;
});
};
$scope.isMenuOpen = false;
/**
* Add ability to update facet
* Broadcast event when facet options are returned via AJAX.
* Should magic_search.js absorb this?
*/
var facetsChangedWatcher = $scope.$on('facetsChanged', function () {
$timeout(function () {
$scope.currentSearch = [];
$scope.initSearch();
});
});
$scope.$on('$destroy', function () {
facetsChangedWatcher();
});
function getFacets(currentFacets) {
if (angular.isUndefined(currentFacets)) {
var initialFacets = $window.location.search;
if (initialFacets.indexOf('?') === 0) {
initialFacets = initialFacets.slice(1);
}
return initialFacets.split('&');
} else {
return currentFacets.map(function(facet) {
return facet.name;
});
}
}
/**
* Override magic_search.js 'initFacets' to fix browser refresh issue
* and to emit('checkFacets') to flag facets as 'isServer'
*/
$scope.initFacets = function(currentFacets) {
var facets = getFacets(currentFacets);
if (facets.length > 1 || (facets[0] && facets[0].length > 0)) {
$timeout(function () {
$scope.strings.prompt = '';
});
}
angular.forEach(facets, function(facet) {
var facetParts = facet.split('=');
angular.forEach($scope.facetsObj, function (value) {
if (value.name == facetParts[0]) {
if (angular.isUndefined(value.options)) {
$scope.currentSearch.push({
'name': facet,
'label': [value.label, facetParts[1]]
});
/**
* for refresh case, need to remove facets that were
* bookmarked/current when browser refresh was clicked
*/
$scope.deleteFacetEntirely(facetParts);
} else {
angular.forEach(value.options, function (option) {
if (option.key == facetParts[1]) {
$scope.currentSearch.push({
'name': facet,
'label': [value.label, option.label]
});
if (value.singleton === true) {
$scope.deleteFacetEntirely(facetParts);
} else {
$scope.deleteFacetSelection(facetParts);
}
}
});
}
}
});
});
if (angular.isDefined($scope.textSearch)) {
$scope.currentSearch.push({
'name': 'text=' + $scope.textSearch,
'label': [$scope.strings.text, $scope.textSearch]
});
}
$scope.filteredObj = $scope.facetsObj;
// broadcast to check facets for server-side
$scope.$emit('checkFacets', $scope.currentSearch);
};
/**
* Override magic_search.js 'removeFacet' to emit('checkFacets')
* to flag facets as 'isServer' after removing facet and
* either update filter or search
*/
$scope.removeFacet = function ($index) {
var removed = $scope.currentSearch[$index].name;
$scope.currentSearch.splice($index, 1);
if (angular.isUndefined($scope.facetSelected)) {
$scope.emitQuery(removed);
} else {
$scope.resetState();
$element.find('.search-input').val('');
}
if ($scope.currentSearch.length === 0) {
$scope.strings.prompt = $scope.promptString;
}
// re-init to restore facets cleanly
$scope.facetsObj = $scope.copyFacets($scope.facetsSave);
var currentSearch = angular.copy($scope.currentSearch);
$scope.currentSearch = [];
$scope.initFacets(currentSearch);
// broadcast to check facets for server-side
$scope.$emit('checkFacets', $scope.currentSearch);
};
$scope.emitQuery();
}
}
})();