1
0
mirror of https://github.com/bpatrik/pigallery2.git synced 2024-12-25 02:04:15 +02:00

Implementing distance search #58

This commit is contained in:
Patrik J. Braun 2021-04-06 11:32:31 +02:00
parent cbfbebf697
commit 0f7ac812ea
17 changed files with 235 additions and 63 deletions

61
package-lock.json generated
View File

@ -1,6 +1,6 @@
{
"name": "pigallery2",
"version": "1.8.2",
"version": "1.8.4",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@ -3344,6 +3344,15 @@
"integrity": "sha512-4nhBPStMK04rruRVtVc6cDqhu7S9GZai0fpXgPXrFpcPX6Xul8xnrjSdGB4KPBVYG/R5+fXWdCM8qBoiULWGPQ==",
"dev": true
},
"@types/node-geocoder": {
"version": "3.24.1",
"resolved": "https://registry.npmjs.org/@types/node-geocoder/-/node-geocoder-3.24.1.tgz",
"integrity": "sha512-NpkCX6ooS3Fc6fjVVCWUDcq7/0siDU6zVu+Q29neHLXOGkRc9VmLvLTl3yeaDRatdpAjkD9O7hOY67zrf7WP9w==",
"dev": true,
"requires": {
"@types/node": "*"
}
},
"@types/q": {
"version": "0.0.32",
"resolved": "https://registry.npmjs.org/@types/q/-/q-0.0.32.tgz",
@ -4702,8 +4711,7 @@
"bluebird": {
"version": "3.5.3",
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.3.tgz",
"integrity": "sha512-/qKPUQlaW1OyR51WeCPBvRnAlnZFUJkCSG5HzGnuIqhgyJtF+T94lFnn33eiazjRm2LAHVy2guNnaq48X9SJuw==",
"dev": true
"integrity": "sha512-/qKPUQlaW1OyR51WeCPBvRnAlnZFUJkCSG5HzGnuIqhgyJtF+T94lFnn33eiazjRm2LAHVy2guNnaq48X9SJuw=="
},
"bmp-js": {
"version": "0.1.0",
@ -13607,6 +13615,11 @@
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.0.0.tgz",
"integrity": "sha512-sSHCgWfJ+Lui/u+0msF3oyCgvdkhxDbkCS6Q8uiJquzOimkJBvX6hl5aSSA7DR1XbMpdM8r7phjcF63sF4rkKg=="
},
"node-fetch": {
"version": "2.6.1",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz",
"integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw=="
},
"node-fetch-npm": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/node-fetch-npm/-/node-fetch-npm-2.0.2.tgz",
@ -13624,6 +13637,17 @@
"integrity": "sha512-7ASaDa3pD+lJ3WvXFsxekJQelBKRpne+GOVbLbtHYdd7pFspyeuJHnWfLplGf3SwKGbfs/aYl5V/JCIaHVUKKQ==",
"dev": true
},
"node-geocoder": {
"version": "3.27.0",
"resolved": "https://registry.npmjs.org/node-geocoder/-/node-geocoder-3.27.0.tgz",
"integrity": "sha512-fNMi9smx56wFhG+2sd0qVsq5RgNlkUuQQ7UWqPwynoMb0GjxSP9joAn8wah4YDv6UzZu3b41cNmd0BglEPkC+Q==",
"requires": {
"bluebird": "^3.5.2",
"node-fetch": "^2.6.0",
"request": "^2.88.0",
"request-promise": "^4.2.2"
}
},
"node-gyp": {
"version": "3.8.0",
"resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-3.8.0.tgz",
@ -16502,6 +16526,32 @@
}
}
},
"request-promise": {
"version": "4.2.6",
"resolved": "https://registry.npmjs.org/request-promise/-/request-promise-4.2.6.tgz",
"integrity": "sha512-HCHI3DJJUakkOr8fNoCc73E5nU5bqITjOYFMDrKHYOXWXrgD/SBaC7LjwuPymUprRyuF06UK7hd/lMHkmUXglQ==",
"requires": {
"bluebird": "^3.5.0",
"request-promise-core": "1.1.4",
"stealthy-require": "^1.1.1",
"tough-cookie": "^2.3.3"
}
},
"request-promise-core": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.4.tgz",
"integrity": "sha512-TTbAfBBRdWD7aNNOoVOBH4pN/KigV6LyapYNNlAPA8JwbovRti1E88m3sYAwsLi5ryhPKsE9APwnjFTgdUjTpw==",
"requires": {
"lodash": "^4.17.19"
},
"dependencies": {
"lodash": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
}
}
},
"require-directory": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
@ -17840,6 +17890,11 @@
"resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
"integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow="
},
"stealthy-require": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz",
"integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks="
},
"stream-browserify": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.2.tgz",

View File

@ -43,6 +43,7 @@
"image-size": "0.9.3",
"jimp": "0.16.1",
"locale": "0.1.0",
"node-geocoder": "^3.27.0",
"reflect-metadata": "0.1.13",
"sharp": "0.23.4",
"sqlite3": "5.0.0",
@ -84,6 +85,7 @@
"@types/jasmine": "3.6.2",
"@types/jsonwebtoken": "8.5.0",
"@types/node": "14.14.19",
"@types/node-geocoder": "^3.24.1",
"@types/sharp": "0.26.1",
"@types/winston": "2.4.4",
"@types/xml2js": "0.4.7",

View File

@ -0,0 +1,6 @@
export class LocationLookupException extends Error {
constructor(message: string, public location: string) {
super(message);
}
}

View File

@ -15,6 +15,7 @@ import {Utils} from '../../common/Utils';
import {QueryParams} from '../../common/QueryParams';
import {VideoProcessing} from '../model/fileprocessing/VideoProcessing';
import {SearchQueryDTO, SearchQueryTypes} from '../../common/entities/SearchQueryDTO';
import {LocationLookupException} from '../exceptions/LocationLookupException';
export class GalleryMWs {
@ -167,6 +168,9 @@ export class GalleryMWs {
req.resultPipe = new ContentWrapper(null, result);
return next();
} catch (err) {
if (err instanceof LocationLookupException) {
return next(new ErrorDTO(ErrorCodes.LocationLookUp_ERROR, 'Cannot find location: ' + err.location, err));
}
return next(new ErrorDTO(ErrorCodes.GENERAL_ERROR, 'Error during searching', err));
}
}

View File

@ -138,7 +138,6 @@ export class ThumbnailGeneratorMWs {
private static addThInfoTODir(directory: DirectoryDTO) {
console.log(directory.path, directory.name);
if (typeof directory.media !== 'undefined') {
ThumbnailGeneratorMWs.addThInfoToPhotos(directory.media);
}

View File

@ -1,10 +1,30 @@
import {GPSMetadata} from '../../../common/entities/PhotoDTO';
import * as NodeGeocoder from 'node-geocoder';
import {LocationLookupException} from '../../exceptions/LocationLookupException';
import {Utils} from '../../../common/Utils';
export class LocationManager {
readonly geocoder: NodeGeocoder.Geocoder;
cache = new Utils.LRU<GPSMetadata>(100);
constructor() {
this.geocoder = NodeGeocoder({provider: 'openstreetmap'});
}
async getGPSData(text: string): Promise<GPSMetadata> {
throw new Error('TODO implement');
if (!this.cache.get(text)) {
const ret = await this.geocoder.geocode(text);
if (ret.length < 1) {
throw new LocationLookupException('Cannot find location:' + text, text);
}
this.cache.set(text, {
latitude: ret[0].latitude,
longitude: ret[0].longitude
});
}
return this.cache.get(text);
}
}

View File

@ -236,7 +236,6 @@ export class GalleryManager implements IGalleryManager, ISQLGalleryManager {
if (dir.preview) {
dir.preview.readyThumbnails = [];
dir.preview.readyIcon = false;
console.log(dir.preview.directory);
}
}

View File

@ -84,7 +84,7 @@ export class SearchManager implements ISearchManager {
.map(r => r.name), SearchQueryTypes.person));
}
if (type === SearchQueryTypes.any_text || type === SearchQueryTypes.position) {
if (type === SearchQueryTypes.any_text || type === SearchQueryTypes.position || type === SearchQueryTypes.distance) {
(await photoRepository
.createQueryBuilder('photo')
.select('photo.metadata.positionData.country as country, ' +
@ -99,7 +99,8 @@ export class SearchManager implements ISearchManager {
.map(pm => <Array<string>>[pm.city || '', pm.country || '', pm.state || ''])
.forEach(positions => {
result = result.concat(this.encapsulateAutoComplete(positions
.filter(p => p.toLowerCase().indexOf(text.toLowerCase()) !== -1), SearchQueryTypes.position));
.filter(p => p.toLowerCase().indexOf(text.toLowerCase()) !== -1),
type === SearchQueryTypes.distance ? type : SearchQueryTypes.position));
});
}
@ -241,12 +242,18 @@ export class SearchManager implements ISearchManager {
latDelta = (1 / ((2 * Math.PI / 360) * earth)), // 1 km in degree
lonDelta = (1 / ((2 * Math.PI / 360) * earth)); // 1 km in degree
const minLat = (<DistanceSearch>query).from.GPSData.latitude - ((<DistanceSearch>query).distance * latDelta),
maxLat = (<DistanceSearch>query).from.GPSData.latitude + ((<DistanceSearch>query).distance * latDelta),
minLon = (<DistanceSearch>query).from.GPSData.latitude -
((<DistanceSearch>query).distance * lonDelta) / Math.cos(minLat * (Math.PI / 180)),
maxLon = (<DistanceSearch>query).from.GPSData.latitude +
((<DistanceSearch>query).distance * lonDelta) / Math.cos(maxLat * (Math.PI / 180));
// TODO: properly handle latitude / longitude boundaries
const trimRange = (value: number, min: number, max: number) => {
return Math.min(Math.max(value, min), max);
};
const minLat = trimRange((<DistanceSearch>query).from.GPSData.latitude - ((<DistanceSearch>query).distance * latDelta), -90, 90),
maxLat = trimRange((<DistanceSearch>query).from.GPSData.latitude + ((<DistanceSearch>query).distance * latDelta), -90, 90),
minLon = trimRange((<DistanceSearch>query).from.GPSData.longitude -
((<DistanceSearch>query).distance * lonDelta) / Math.cos(minLat * (Math.PI / 180)), -180, 180),
maxLon = trimRange((<DistanceSearch>query).from.GPSData.longitude +
((<DistanceSearch>query).distance * lonDelta) / Math.cos(maxLat * (Math.PI / 180)), -180, 180);
return new Brackets(q => {
const textParam: any = {};

View File

@ -146,7 +146,7 @@ export class SearchQueryParser {
this.keywords.someOf + ':' :
new RegExp('^\\d*-' + this.keywords.NSomeOf + ':').exec(str)[0];
let tmpList: any = this.parse(str.slice(prefix.length + 1, -1), false); // trim brackets
// console.log(JSON.stringify(tmpList, null, 4));
const unfoldList = (q: SearchListQuery): SearchQueryDTO[] => {
if (q.list) {
if (q.type === SearchQueryTypes.UNKNOWN_RELATION) {
@ -207,7 +207,8 @@ export class SearchQueryParser {
}
if (new RegExp('^\\d*-' + this.keywords.kmFrom + ':').test(str)) {
let from = str.slice(new RegExp('^\\d*-' + this.keywords.kmFrom + ':').exec(str)[0].length);
if (from.charAt(0) === '(' && from.charAt(from.length - 1) === ')') {
if ((from.charAt(0) === '(' && from.charAt(from.length - 1) === ')') ||
(from.charAt(0) === '"' && from.charAt(from.length - 1) === '"')) {
from = from.slice(1, from.length - 1);
}
return <DistanceSearch>{

View File

@ -268,3 +268,35 @@ export class Utils {
}
}
export namespace Utils {
export class LRU<V> {
data: { [key: string]: { value: V, usage: number } } = {};
constructor(public readonly size: number) {
}
set(key: string, value: V): void {
this.data[key] = {usage: Date.now(), value: value};
if (Object.keys(this.data).length > this.size) {
let oldestK = key;
let oldest = this.data[oldestK].usage;
for (const k in this.data) {
if (this.data[k].usage < oldest) {
oldestK = k;
oldest = this.data[oldestK].usage;
}
}
delete this.data[oldestK];
}
}
get(key: string): V {
if (!this.data[key]) {
return;
}
return this.data[key].value;
}
}
}

View File

@ -22,7 +22,8 @@ export enum ErrorCodes {
SETTINGS_ERROR = 13,
TASK_ERROR = 14,
JOB_ERROR = 15
JOB_ERROR = 15,
LocationLookUp_ERROR = 16,
}
export class ErrorDTO {

View File

@ -27,40 +27,55 @@
</ng-container>
<div body class="container-fluid" style="width: 100%; padding:0" *ngIf="_galleryService.content.value.directory">
<app-gallery-navbar [directory]="_galleryService.content.value.directory"></app-gallery-navbar>
<div body class="container-fluid" style="width: 100%; padding:0">
<ng-container *ngIf="_galleryService.content.value.error">
<div class="alert alert-danger" role="alert">
{{_galleryService.content.value.error}}
</div>
</ng-container>
<ng-container *ngIf="!_galleryService.content.value.error">
<ng-container *ngIf="_galleryService.content.value.directory">
<app-gallery-directories class="directories" [directories]="directories"></app-gallery-directories>
<app-gallery-navbar [directory]="_galleryService.content.value.directory"></app-gallery-navbar>
<app-gallery-map *ngIf="isPhotoWithLocation && mapEnabled"
[photos]="_galleryService.content.value.directory.media"
[gpxFiles]="_galleryService.content.value.directory.metaFile | gpxFiles"></app-gallery-map>
<app-gallery-grid [media]="_galleryService.content.value.directory.media"
[lightbox]="lightbox"></app-gallery-grid>
</div>
<!-- Search-->
<div body class="container-fluid" style="width: 100%; padding:0" *ngIf="_galleryService.content.value.searchResult">
<div class="alert alert-info" role="alert"
*ngIf="_galleryService.content.value.searchResult.resultOverflow == true" i18n>
Too many results to show. Refine your search.
</div>
<app-gallery-navbar [searchResult]="_galleryService.content.value.searchResult"></app-gallery-navbar>
<app-gallery-directories class="directories" [directories]="directories"></app-gallery-directories>
<app-gallery-map *ngIf="isPhotoWithLocation && mapEnabled"
[photos]="_galleryService.content.value.searchResult.media"
[gpxFiles]="_galleryService.content.value.searchResult.metaFile | gpxFiles"></app-gallery-map>
<app-gallery-map *ngIf="isPhotoWithLocation && mapEnabled"
[photos]="_galleryService.content.value.directory.media"
[gpxFiles]="_galleryService.content.value.directory.metaFile | gpxFiles"></app-gallery-map>
<app-gallery-grid [media]="_galleryService.content.value.directory.media"
[lightbox]="lightbox"></app-gallery-grid>
<app-gallery-directories class="directories" [directories]="directories"></app-gallery-directories>
<app-gallery-grid [media]="_galleryService.content.value.searchResult.media"
[lightbox]="lightbox"></app-gallery-grid>
</ng-container>
<!-- Search-->
<ng-container *ngIf="_galleryService.content.value.searchResult">
<div class="alert alert-info" role="alert"
*ngIf="_galleryService.content.value.searchResult.resultOverflow == true" i18n>
Too many results to show. Refine your search.
</div>
<app-gallery-navbar [searchResult]="_galleryService.content.value.searchResult"></app-gallery-navbar>
<app-gallery-map *ngIf="isPhotoWithLocation && mapEnabled"
[photos]="_galleryService.content.value.searchResult.media"
[gpxFiles]="_galleryService.content.value.searchResult.metaFile | gpxFiles"></app-gallery-map>
<app-gallery-directories class="directories" [directories]="directories"></app-gallery-directories>
<app-gallery-grid [media]="_galleryService.content.value.searchResult.media"
[lightbox]="lightbox"></app-gallery-grid>
</ng-container>
</ng-container>
</div>
<div body class="container"
style="width: 100%; padding:0"
*ngIf="(!_galleryService.content.value.directory ||
_galleryService.content.value.directory.isPartial == true)
&& !_galleryService.content.value.searchResult">
&& !_galleryService.content.value.searchResult
&& !_galleryService.content.value.error">
<div class="spinner">
</div>

View File

@ -11,12 +11,13 @@ import {SortingMethods} from '../../../../common/entities/SortingMethods';
import {QueryParams} from '../../../../common/QueryParams';
import {PG2ConfMap} from '../../../../common/PG2ConfMap';
import {SearchQueryDTO} from '../../../../common/entities/SearchQueryDTO';
import {ErrorCodes} from '../../../../common/entities/Error';
@Injectable()
export class GalleryService {
public content: BehaviorSubject<ContentWrapper>;
public content: BehaviorSubject<ContentWrapperWithError>;
public sorting: BehaviorSubject<SortingMethods>;
lastRequest: { directory: string } = {
directory: null
@ -29,7 +30,7 @@ export class GalleryService {
private galleryCacheService: GalleryCacheService,
private _shareService: ShareService,
private navigationService: NavigationService) {
this.content = new BehaviorSubject<ContentWrapper>(new ContentWrapper());
this.content = new BehaviorSubject<ContentWrapperWithError>(new ContentWrapperWithError());
this.sorting = new BehaviorSubject<SortingMethods>(Config.Client.Other.defaultPhotoSortingMethod);
}
@ -56,7 +57,7 @@ export class GalleryService {
}
setContent(content: ContentWrapper): void {
setContent(content: ContentWrapperWithError): void {
this.content.next(content);
if (content.directory) {
const sort = this.galleryCacheService.getSorting(content.directory);
@ -70,7 +71,7 @@ export class GalleryService {
public async loadDirectory(directoryName: string): Promise<void> {
const content = new ContentWrapper();
const content = new ContentWrapperWithError();
content.directory = this.galleryCacheService.getDirectory(directoryName);
content.searchResult = null;
@ -93,7 +94,7 @@ export class GalleryService {
}
try {
const cw = await this.networkService.getJson<ContentWrapper>('/gallery/content/' + directoryName, params);
const cw = await this.networkService.getJson<ContentWrapperWithError>('/gallery/content/' + directoryName, params);
if (!cw || cw.notModified === true) {
@ -124,12 +125,20 @@ export class GalleryService {
this.ongoingSearch = query;
this.setContent(new ContentWrapper());
const cw = new ContentWrapper();
this.setContent(new ContentWrapperWithError());
const cw = new ContentWrapperWithError();
cw.searchResult = this.galleryCacheService.getSearch(query);
if (cw.searchResult == null) {
cw.searchResult = (await this.networkService.getJson<ContentWrapper>('/search/' + query)).searchResult;
this.galleryCacheService.setSearch(query, cw.searchResult);
try {
cw.searchResult = (await this.networkService.getJson<ContentWrapper>('/search/' + query)).searchResult;
this.galleryCacheService.setSearch(query, cw.searchResult);
} catch (e) {
if (e.code === ErrorCodes.LocationLookUp_ERROR) {
cw.error = 'Cannot find location: ' + e.message;
} else {
throw e;
}
}
}
if (this.ongoingSearch !== query) {
@ -146,3 +155,8 @@ export class GalleryService {
}
export class ContentWrapperWithError extends ContentWrapper {
public error: string;
}

View File

@ -12,7 +12,6 @@ import {SearchQueryParser} from '../../../../../common/SearchQueryParser';
export class AutoCompleteService {
private keywords: string[] = [];
private relationKeywords: string[] = [];
private textSearchKeywordsMap: { [key: string]: SearchQueryTypes } = {};
constructor(private _networkService: NetworkService,
@ -29,7 +28,11 @@ export class AutoCompleteService {
this.keywords.push(this._searchQueryParserService.keywords.and);
this.keywords.push(this._searchQueryParserService.keywords.or);
for (let i = 0; i < 10; i++) {
this.keywords.push(i + this._searchQueryParserService.keywords.NSomeOf);
this.keywords.push(i + '-' + this._searchQueryParserService.keywords.NSomeOf + ':( )');
}
for (let i = 0; i < 10; i++) {
this.keywords.push(i + '-' + this._searchQueryParserService.keywords.kmFrom + ':');
}
this.keywords.push(this._searchQueryParserService.keywords.to + ':' +
@ -55,11 +58,11 @@ export class AutoCompleteService {
if (searchText === '') {
return items;
}
this.typedAutoComplete(searchText, type, items);
this.typedAutoComplete(searchText, text.current, type, items);
return items;
}
public typedAutoComplete(text: string, type: SearchQueryTypes,
public typedAutoComplete(text: string, fullText: string, type: SearchQueryTypes,
items?: BehaviorSubject<RenderableAutoCompleteItem[]>): BehaviorSubject<RenderableAutoCompleteItem[]> {
items = items || new BehaviorSubject([]);
@ -71,10 +74,10 @@ export class AutoCompleteService {
}
this._networkService.getJson<IAutoCompleteItem[]>('/autocomplete/' + text, acParams).then(ret => {
this._galleryCacheService.setAutoComplete(text, type, ret);
items.next(this.sortResults(text, ret.map(i => this.ACItemToRenderable(i)).concat(items.value)));
items.next(this.sortResults(text, ret.map(i => this.ACItemToRenderable(i, fullText)).concat(items.value)));
});
} else {
items.next(this.sortResults(text, cached.map(i => this.ACItemToRenderable(i)).concat(items.value)));
items.next(this.sortResults(text, cached.map(i => this.ACItemToRenderable(i, fullText)).concat(items.value)));
}
return items;
}
@ -91,23 +94,37 @@ export class AutoCompleteService {
return tokens[1];
}
private getTypeFromPrefix(text: string): SearchQueryTypes {
const tokens = text.split(':');
if (tokens.length !== 2) {
return null;
}
if (new RegExp('^\\d*-' + this._searchQueryParserService.keywords.kmFrom).test(tokens[0])) {
return SearchQueryTypes.distance;
}
return this.textSearchKeywordsMap[tokens[0]] || null;
}
private ACItemToRenderable(item: IAutoCompleteItem): RenderableAutoCompleteItem {
private ACItemToRenderable(item: IAutoCompleteItem, searchToken: string): RenderableAutoCompleteItem {
if (!item.type) {
return {text: item.text, queryHint: item.text};
}
if (TextSearchQueryTypes.includes(item.type) && item.type !== SearchQueryTypes.any_text) {
if ((TextSearchQueryTypes.includes(item.type) ||
item.type === SearchQueryTypes.distance) &&
item.type !== SearchQueryTypes.any_text) {
let queryHint = (<any>this._searchQueryParserService.keywords)[SearchQueryTypes[item.type]] + ':"' + item.text + '"';
// if its a distance search, change hint text
const tokens = searchToken.split(':');
if (tokens.length === 2 &&
new RegExp('^\\d*-' + this._searchQueryParserService.keywords.kmFrom).test(tokens[0])) {
queryHint = tokens[0] + ':"' + item.text + '"';
}
return {
text: item.text, type: item.type,
queryHint:
(<any>this._searchQueryParserService.keywords)[SearchQueryTypes[item.type]] + ':"' + item.text + '"'
queryHint: queryHint
};
}
return {

View File

@ -34,7 +34,7 @@
<div class="autocomplete-list" *ngIf="autoCompleteRenders.length > 0"
(mouseover)="setMouseOverAutoComplete(true)" (mouseout)="setMouseOverAutoComplete(false)">
<div class="autocomplete-item"
[ngClass]="{'autocomplete-item-selected':highlightedAutoCompleteItem == i}"
[ngClass]="{'autocomplete-item-selected': highlightedAutoCompleteItem === i}"
(mouseover)="setMouseOverAutoCompleteItem(i)"
(click)="searchAutoComplete(item)"
*ngFor="let item of autoCompleteRenders; let i = index">
@ -46,6 +46,7 @@
<span *ngSwitchCase="SearchQueryTypes.keyword" class="oi oi-tag"></span>
<span *ngSwitchCase="SearchQueryTypes.person" class="oi oi-person"></span>
<span *ngSwitchCase="SearchQueryTypes.position" class="oi oi-map-marker"></span>
<span *ngSwitchCase="SearchQueryTypes.distance" class="oi oi-map-marker"></span>
</span>
{{item.preText}}<strong>{{item.highLightText}}</strong>{{item.postText}}
</div>

View File

@ -62,7 +62,6 @@ describe('GalleryRouter', (sqlHelper: DBTestHelper) => {
expect(result.body.error).to.be.equal(null);
expect(result.body.result).to.not.be.equal(null);
expect(result.body.result.directory).to.not.be.equal(null);
console.log(result.body.result.directory);
});
it('should load gallery twice (to force loading form db)', async () => {

View File

@ -999,7 +999,7 @@ describe('SearchManager', (sqlHelper: DBTestHelper) => {
});
it('should search distance', async () => {
it('should search distance', async () => {
ObjectManagers.getInstance().LocationManager = new LocationManager();
const sm = new SearchManager();