openaire-library/fos/fos.component.ts

311 lines
11 KiB
TypeScript

import {HttpClient} from "@angular/common/http";
import {ChangeDetectorRef, Component, ElementRef, Input, OnDestroy, OnInit, ViewChild} from "@angular/core";
import {Subscription} from "rxjs";
import {Breadcrumb} from "../utils/breadcrumbs/breadcrumbs.component";
import {EnvProperties} from "../utils/properties/env-properties";
import {properties} from "src/environments/environment";
import {FormBuilder, FormControl} from "@angular/forms";
import {ActivatedRoute, Router} from "@angular/router";
import {Meta, Title} from "@angular/platform-browser";
import {Location} from "@angular/common";
import {StringUtils} from "../utils/string-utils.class";
import {SEOService} from "../sharedComponents/SEO/SEO.service";
import {PiwikService} from "../utils/piwik/piwik.service";
import {debounceTime, distinctUntilChanged} from "rxjs/operators";
import Timeout = NodeJS.Timeout;
import {ISVocabulariesService} from "../utils/staticAutoComplete/ISVocabularies.service";
import {SearchFields} from "../utils/properties/searchFields";
import {HelperFunctions} from "../utils/HelperFunctions.class";
declare var UIkit;
@Component({
selector: 'fos',
templateUrl: 'fos.component.html',
styleUrls: ['fos.component.less']
})
export class FosComponent implements OnInit, OnDestroy {
public url: string = null;
public pageTitle: string = "OpenAIRE | Fields of Science";
public pageDescription: string = "We have integrated a Field-of-Science (FoS) taxonomy into our dataset to organize and discover research more effectively. Using the full capabilities of the OpenAIRE Graph (full-texts, citations, references, venues) we apply AI and bring forward any multidisciplinarity potential.";
public scrollPos = 0;
public selectedParentLevels = [];
public fos: any[] = [];
public fosOptions: string[] = [];
public activeSection: string;
public keywordControl: FormControl;
public keyword: string;
public viewResults = [];
public result = [];
properties: EnvProperties = properties;
public breadcrumbs: Breadcrumb[] = [{name: 'home', route: '/'}, {name: 'Fields of Science'}];
private subscriptions: Subscription[] = [];
private observer: IntersectionObserver;
private timeout: Timeout;
@ViewChild('tabs') tabs: ElementRef;
public sliderInit: boolean = false;
private searchFieldsHelper: SearchFields = new SearchFields();
constructor(
private vocabulariesService: ISVocabulariesService,
private fb: FormBuilder,
private location: Location,
private route: ActivatedRoute,
private _router: Router,
private _meta: Meta,
private _title: Title,
private seoService: SEOService,
private _piwikService: PiwikService,
private cdr: ChangeDetectorRef
) {}
ngOnInit() {
this.subscriptions.push(this._piwikService.trackView(this.properties, this.pageTitle).subscribe());
this.url = this.properties.domain + this.properties.baseLink + this._router.url;
this.seoService.createLinkForCanonicalURL(this.url);
this.updateUrl(this.url);
this.updateTitle(this.pageTitle);
this.updateDescription(this.pageDescription);
this.vocabulariesService.getFos(properties).subscribe(data => {
this.fos = data['fos'];
this.convertFosToOptions();
if (typeof document !== 'undefined') {
setTimeout(()=> {
let slider = UIkit.slider(this.tabs.nativeElement);
slider.clsActive = 'uk-slider-active';
slider.updateActiveClasses();
this.sliderInit = true;
slider.slides.forEach(item => {
item.classList.remove('uk-active');
});
if (this.route.snapshot.fragment) {
let splitFragment = this.route.snapshot.fragment.split("||");
this.activeSection = this.route.snapshot.fragment.split("||")[0];
let i = this.fos.findIndex(item => (item.id == this.route.snapshot.fragment || this.route.snapshot.fragment.startsWith(item.id+"||")));
if(i <0 || i>this.fos.length-1) {
this._router.navigate(['./'], {fragment: "", relativeTo: this.route, state: {disableScroll: true}});
} else {
if (splitFragment.length > 1) {
let level1 = this.fos[i];
let level2 = null;
let level3 = null;
if (level1.children) {
level2 = level1.children.find(item => item.code == splitFragment[1]);
if (level2 && level2.children) {
level3 = level2.children.find(item => item.code == splitFragment[2]);
this.selectedParentLevels = [this.fos[i], level2, level3];
}
}
if(!level2 || !level3) {
this._router.navigate(['./'], {fragment: level1.id, relativeTo: this.route, state: {disableScroll: true}});
}
} else {
slider.show(i);
}
}
} else {
this.activeSection = this.fos[0].id;
}
this.cdr.detectChanges();
});
}
this.subscriptions.push(this.route.fragment.subscribe(fragment => {
if(fragment) {
this.activeSection = fragment.split("||")[0];
if(this.tabs) {
let slider = UIkit.slider(this.tabs.nativeElement);
let i = this.fos.findIndex(item => (item.id == fragment || fragment.startsWith(item.id+"||")));
slider.show(i);
}
} else {
this.activeSection = this.fos[0].id;
}
}));
this.keywordControl = this.fb.control('');
this.subscriptions.push(this.keywordControl.valueChanges.pipe(debounceTime(500), distinctUntilChanged()).subscribe(value => {
if(this.keyword !== undefined || value) {
this.selectedParentLevels = [];
}
this.keyword = value;
this.findMatches(this.keyword);
if (typeof IntersectionObserver !== 'undefined') {
setTimeout(() => {
this.setObserver();
});
}
}));
});
}
public ngOnDestroy() {
for (let sub of this.subscriptions) {
sub.unsubscribe();
}
if(this.observer) {
this.observer.disconnect();
}
}
private setObserver() {
if(this.observer) {
this.observer.disconnect();
}
this.observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if(entry.isIntersecting) {
if(this.timeout) {
clearTimeout(this.timeout);
}
this.timeout = setTimeout(() => {
this._router.navigate(['./'], {fragment: entry.target.id, relativeTo: this.route, state: {disableScroll: true}});
}, 200);
}
});
}, {threshold: 0.25, rootMargin: '-100px'});
this.fos.forEach(fos => {
let element = document.getElementById(fos.id);
if(element) {
this.observer.observe(element);
}
});
}
convertFosToOptions() {
this.fosOptions = [];
this.fos.forEach(fos => {
this.fosOptions.push(fos.id);
if(fos.children) {
fos.children.forEach(l2 => {
this.fosOptions.push(l2.id);
// hide L3 & L4 FoS
if(l2.children && properties.environment == "development") {
l2.children.forEach(l3 => {
this.fosOptions.push(l3.id);
if(l3.children) {
l3.children.forEach(l4 => {
this.fosOptions.push(l4.id);
})
}
});
}
});
}
});
}
findMatches(value: string) {
this.viewResults = JSON.parse(JSON.stringify(this.fos));
let matchLevel1: boolean = false;
let matchLevel2: boolean = false;
let matchLevel3: boolean = false;
// 1st level search
if(this.viewResults.length) {
this.viewResults = this.viewResults.filter(item => {
matchLevel1 = !!item.id.includes(value?.toLowerCase());
// 2nd level search
if(item.children?.length && !matchLevel1) {
item.children = item.children.filter(subItem => {
matchLevel2 = !!subItem.id.includes(value?.toLowerCase());
// hide L3 & L4 FoS
if(properties.environment == "development") {
// 3rd level search
if(subItem.children?.length && !matchLevel2) {
subItem.children = subItem.children.filter(subSubItem => {
matchLevel3 = subSubItem.id.includes(value?.toLowerCase());
// 4th level search
if (subSubItem.children?.length && !matchLevel3) {
subSubItem.children = subSubItem.children.filter(level4Item => {
return level4Item.id.toLowerCase().includes(value?.toLowerCase())
});
}
return subSubItem.children?.length > 0 || matchLevel3;
});
}
return subItem.children?.length > 0;
} else {
return matchLevel2;
}
});
}
return item.children?.length > 0;
});
}
}
highlightKeyword(name) {
if(name.toLowerCase().includes(this.keyword.toLowerCase())) {
return name.replace(new RegExp(this.keyword, "gi"), (matchedValue) => `<mark class="highlighted">${matchedValue}</mark>`);
} else {
return name;
}
}
public urlEncodeAndQuote(str: string): string {
return StringUtils.quote(StringUtils.URIEncode(str));
}
private updateUrl(url: string) {
this._meta.updateTag({content: url}, "property='og:url'");
}
private updateTitle(title: string) {
var _title = ((title.length > 50) ? title.substring(0, 50) : title);
this._title.setTitle(_title);
this._meta.updateTag({content: _title}, "property='og:title'");
}
private updateDescription(description: string) {
this._meta.updateTag({content: description}, "name='description'");
this._meta.updateTag({content: description}, "property='og:description'");
}
public buildFosQueryParam(fos) {
// return {'foslabel': this.urlEncodeAndQuote(fos.id+"||"+fos.label)};
return (this.searchFieldsHelper.getFosParameter() == 'foslabel' ? ({'foslabel': this.urlEncodeAndQuote(fos.id+"||"+fos.label)}) : ({'fos': this.urlEncodeAndQuote(fos.id)}));
}
public backClicked() {
let id = this.selectedParentLevels[0].id;
this.selectedParentLevels=[];
this.cdr.detectChanges();
if(this.scrollPos) {
HelperFunctions.scrollTo(0, this.scrollPos);
this._router.navigate(['./'], {fragment: id, relativeTo: this.route, state: {disableScroll: true}});
} else {
this._router.navigate(['./'], {
fragment: id,
relativeTo: this.route,
state: {disableScroll: false, behavior: 'auto'}
});
}
this.cdr.detectChanges();
if (typeof IntersectionObserver !== 'undefined') {
setTimeout(() => {
this.setObserver();
}, 200);
}
}
public moreClicked(level1, level2, level3) {
this.scrollPos = window.scrollY;
if(this.observer) {
this.observer.disconnect();
}
this.selectedParentLevels=[level1, level2, level3];
this.cdr.detectChanges();
this._router.navigate(['./'],
{fragment: this.selectedParentLevels[0].id+"||"+this.selectedParentLevels[1].code+"||"+this.selectedParentLevels[2].code,
relativeTo: this.route, state: {disableScroll: false, behavior: 'auto'}});
}
}