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) => `${matchedValue}`); } 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'}}); } }