/** * @fileOverview Magic Search JS * @requires AngularJS * */ // Allow the module to be pre-defined with additional dependencies try{ angular.module('MagicSearch'); } catch (exception) { angular.module('MagicSearch', []); } angular.module('MagicSearch') .directive('magicSearch', function($compile) { return { restrict: 'E', scope: { facets_json: '@facets', filter_keys: '=filterKeys', strings: '=strings' }, templateUrl: function (scope, elem) { return elem.template; }, controller: function ($scope, $timeout) { $scope.currentSearch = []; $scope.initSearch = function() { // Parse facets JSON and convert to a list of facets. $scope.facetsJson = $scope.facets_json.replace(/__apos__/g, "\'").replace(/__dquote__/g, '\\"').replace(/__bslash__/g, "\\"); $scope.facetsObj = JSON.parse($scope.facetsJson); // set facets selected and remove them from facetsObj var initialFacets = window.location.search; if (initialFacets.indexOf('?') === 0) { initialFacets = initialFacets.slice(1); } initialFacets = initialFacets.split('&'); if (initialFacets.length > 1 || initialFacets[0].length > 0) { $timeout(function() { $scope.strings['prompt'] = ''; }); } angular.forEach(initialFacets, function(facet, idx) { var facetParts = facet.split('='); angular.forEach($scope.facetsObj, function(value, idx) { if (value.name == facetParts[0]) { if (value.options === undefined) { $scope.currentSearch.push({'name':facet, 'label':[value.label, facetParts[1]]}); // allow free-form facets to remain } else { angular.forEach(value.options, function(option, idx) { if (option.key == facetParts[1]) { $scope.currentSearch.push({'name':facet, 'label':[value.label, option.label]}); $scope.deleteFacetSelection(facetParts); } }); } } }); }); $scope.filteredObj = $scope.facetsObj; }; // removes a facet from the menu $scope.deleteFacetSelection = function(facet_parts) { angular.forEach($scope.facetsObj.slice(), function(facet, idx) { if (facet.name == facet_parts[0]) { if (facet.options === undefined) { return; // allow free-form facets to remain } for (var i=0; i -1) { label = [facet.label.substring(0, idx), facet.label.substring(idx, idx + search_val.length), facet.label.substring(idx + search_val.length)]; filtered.push({'name':facet.name, 'label':label, 'options':facet.options}); } } if (filtered.length > 0) { $scope.showMenu(); $timeout(function() { $scope.filteredObj = filtered; }, 0.1); } else { $scope.$emit('textSearch', search_val, $scope.filter_keys); $scope.hideMenu(); } } else { // assume option search $scope.filteredOptions = $scope.facetOptions; if ($scope.facetOptions === undefined) { // no options, assume free form text facet return; } for (i=0; i<$scope.filteredOptions.length; i++) { var option = $scope.filteredOptions[i]; idx = option.label.toLowerCase().indexOf(search_val); if (idx > -1) { label = [option.label.substring(0, idx), option.label.substring(idx, idx + search_val.length), option.label.substring(idx + search_val.length)]; filtered.push({'key':option.key, 'label':label}); } } if (filtered.length > 0) { $scope.showMenu(); $timeout(function() { $scope.filteredOptions = filtered; }, 0.1); } } }; // enable text entry when mouse clicked anywhere in search box $('#search-main-area').on("click", function($event) { $('#search-input').trigger("focus"); if ($scope.facetSelected === undefined) { $scope.showMenu(); } }); // when facet clicked, add 1st part of facet and set up options $scope.facetClicked = function($index, $event, name) { $scope.hideMenu(); var facet = $scope.filteredObj[$index]; var label = facet.label; if (Array.isArray(label)) { label = label.join(''); } $scope.facetSelected = {'name':facet.name, 'label':[label, '']}; if (facet.options !== undefined) { $scope.filteredOptions = $scope.facetOptions = facet.options; $scope.showMenu(); } $timeout(function() { $('#search-input').val(''); }); $scope.strings['prompt'] = ''; $timeout(function() { $('#search-input').focus(); }); }; // when option clicked, complete facet and send event $scope.optionClicked = function($index, $event, name) { $scope.hideMenu(); var curr = $scope.facetSelected; curr.name = curr.name + '=' + name; curr.label[1] = $scope.filteredOptions[$index].label; if (Array.isArray(curr.label[1])) { curr.label[1] = curr.label[1].join(''); } $scope.currentSearch.push(curr); $scope.resetState(); $scope.emitQuery(); $scope.showMenu(); }; // send event with new query string $scope.emitQuery = function(removed) { var query = ''; for (var i=0; i<$scope.currentSearch.length; i++) { if ($scope.currentSearch[i].name.indexOf('text') !== 0) { if (query.length > 0) query = query + "&"; query = query + $scope.currentSearch[i].name; } } if (removed !== undefined && removed.indexOf('text') === 0) { $scope.$emit('textSearch', '', $scope.filter_keys); } else { $scope.$emit('searchUpdated', query); if ($scope.currentSearch.length > 0) { var newFacet = $scope.currentSearch[$scope.currentSearch.length-1].name; $scope.deleteFacetSelection(newFacet.split('=')); } } }; // remove facet and either update filter or search $scope.removeFacet = function($index, $event) { var removed = $scope.currentSearch[$index].name; $scope.currentSearch.splice($index, 1); if ($scope.facetSelected === undefined) { $scope.emitQuery(removed); } else { $scope.resetState(); $('#search-input').val(''); } // facet re-enabled by reload }; // clear entire searchbar $scope.clearSearch = function() { if ($scope.currentSearch.length > 0) { $scope.currentSearch = []; $scope.facetsObj = JSON.parse($scope.facetsJson); $scope.resetState(); $scope.$emit('searchUpdated', ''); $scope.$emit('textSearch', '', $scope.filter_keys); } }; $scope.isMatchLabel = function(label) { return Array.isArray(label); }; $scope.resetState = function() { $('#search-input').val(''); $scope.filteredObj = $scope.facetsObj; $scope.facetSelected = undefined; $scope.facetOptions = undefined; $scope.filteredOptions = undefined }; // showMenu and hideMenu depend on foundation's dropdown. They need // to be modified to work with another dropdown implemenation (i.e. bootstrap) $scope.showMenu = function() { $timeout(function() { if ($('#facet-drop').hasClass('open') === false) { $('#search-input').trigger('click'); } }); }; $scope.hideMenu = function() { $(document).foundation('dropdown', 'closeall'); }; $scope.initSearch(); } }; }) ;