diff --git a/fos/fos-routing.module.ts b/fos/fos-routing.module.ts new file mode 100644 index 00000000..fb248a66 --- /dev/null +++ b/fos/fos-routing.module.ts @@ -0,0 +1,13 @@ +import {NgModule} from '@angular/core'; +import {RouterModule} from '@angular/router'; + +import {FosComponent} from './fos.component'; +import {PreviousRouteRecorder} from '../utils/piwik/previousRouteRecorder.guard'; +@NgModule({ + imports: [ + RouterModule.forChild([ + { path: '', component: FosComponent, canDeactivate: [PreviousRouteRecorder] } + ]) + ] +}) +export class FosRoutingModule { } diff --git a/fos/fos.component.html b/fos/fos.component.html new file mode 100644 index 00000000..af6ee6e0 --- /dev/null +++ b/fos/fos.component.html @@ -0,0 +1,159 @@ + +
+
+ +
+
+
+
+
+ + Beta + +
+

+ Fields of Science . +

+
+ 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 Research Graph (full-texts, citations, references, venues) we apply AI and bring forward any multidisciplinarity potential. +
+
+ Our work is based on the work from our partner Athena Research Center: SciNoBo : A Hierarchical Multi-Label Classifier of Scientific Publications - https://doi.org/10.1145/3487553.3524677 +
+ + +
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+ + +
+
+
+
+
+
+
+
+
+ + +
+ + +
+
+
+
+ + + + +
+
+

+ + +

+
+
+

+ + +

+
+ + +
+
+
+
+
+
+
+
+ +
+

No results were found.

+
+
+
+
+
diff --git a/fos/fos.component.less b/fos/fos.component.less new file mode 100644 index 00000000..c8020e14 --- /dev/null +++ b/fos/fos.component.less @@ -0,0 +1,5 @@ +@import (reference) "~src/assets/openaire-theme/less/_import-variables"; + +.custom-bottom-border { + border-bottom: 5px solid fade(@explore-color, @global-opacity); +} diff --git a/fos/fos.component.ts b/fos/fos.component.ts new file mode 100644 index 00000000..0fc13908 --- /dev/null +++ b/fos/fos.component.ts @@ -0,0 +1,219 @@ +import {HttpClient} from "@angular/common/http"; +import {ChangeDetectorRef, Component, ElementRef, 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; + +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 Research Graph (full-texts, citations, references, venues) we apply AI and bring forward any multidisciplinarity potential."; + + 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; + + constructor( + private httpClient: HttpClient, + 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() { + if (this.properties.enablePiwikTrack && (typeof document !== 'undefined')) { + this.subscriptions.push(this._piwikService.trackView(this.properties, this.pageTitle, this.properties.piwikSiteId).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.httpClient.get(properties.domain+'/assets/common-assets/vocabulary/fos.json').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) { + this.activeSection = this.route.snapshot.fragment; + let i = this.fos.findIndex(item => item.id == this.route.snapshot.fragment); + 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; + if(this.tabs) { + let slider = UIkit.slider(this.tabs.nativeElement); + let i = this.fos.findIndex(item => item.id == fragment); + 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 => { + this.keyword = value; + this.findMatches(this.keyword); + if (typeof document !== '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(child => { + this.fosOptions.push(child.id); + if(child.children) { + child.children.forEach(child2 => { + this.fosOptions.push(child2.id); + }); + } + }); + } + }); + } + + findMatches(value: string) { + this.viewResults = JSON.parse(JSON.stringify(this.fos)); + let matchLevel1: boolean = false; + let matchLevel2: 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()); + // 3rd level search + if(subItem.children?.length && !matchLevel2) { + subItem.children = subItem.children.filter(subSubItem => subSubItem.id.includes(value?.toLowerCase())); + } + return subItem.children?.length > 0 || matchLevel2; + }); + } + return item.children?.length > 0; + }); + } + } + + highlightKeyword(name) { + if(name.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'"); + } +} diff --git a/fos/fos.module.ts b/fos/fos.module.ts new file mode 100644 index 00000000..d0f1f833 --- /dev/null +++ b/fos/fos.module.ts @@ -0,0 +1,34 @@ +import {CommonModule} from "@angular/common"; +import {NgModule} from "@angular/core"; +import {FormsModule} from "@angular/forms"; +import {RouterModule} from "@angular/router"; +import {PreviousRouteRecorder} from "../utils/piwik/previousRouteRecorder.guard"; +import {IconsModule} from "../utils/icons/icons.module"; +import {BreadcrumbsModule} from "../utils/breadcrumbs/breadcrumbs.module"; +import {Schema2jsonldModule} from "../sharedComponents/schema2jsonld/schema2jsonld.module"; +import {SearchInputModule} from "../sharedComponents/search-input/search-input.module"; +import {SEOServiceModule} from "../sharedComponents/SEO/SEOService.module"; +import {PiwikService} from "../utils/piwik/piwik.service"; + +import {FosRoutingModule} from './fos-routing.module'; +import {FosComponent} from './fos.component'; + +@NgModule({ + imports: [ + CommonModule, FormsModule, RouterModule, + FosRoutingModule, BreadcrumbsModule, IconsModule, + SearchInputModule, Schema2jsonldModule, SEOServiceModule + ], + declarations: [ + FosComponent + ], + providers: [ + PreviousRouteRecorder, PiwikService + ], + exports: [ + FosComponent + ] +}) +export class FosModule { + +} \ No newline at end of file diff --git a/sdg/sdg-routing.module.ts b/sdg/sdg-routing.module.ts new file mode 100644 index 00000000..96963f04 --- /dev/null +++ b/sdg/sdg-routing.module.ts @@ -0,0 +1,13 @@ +import {NgModule} from '@angular/core'; +import {RouterModule} from '@angular/router'; + +import {SdgComponent} from './sdg.component'; +import {PreviousRouteRecorder} from '../utils/piwik/previousRouteRecorder.guard'; +@NgModule({ + imports: [ + RouterModule.forChild([ + { path: '', component: SdgComponent, canDeactivate: [PreviousRouteRecorder] } + ]) + ] +}) +export class SdgRoutingModule { } diff --git a/sdg/sdg.component.html b/sdg/sdg.component.html new file mode 100644 index 00000000..2bad4bed --- /dev/null +++ b/sdg/sdg.component.html @@ -0,0 +1,72 @@ + +
+
+ +
+
+
+
+
+ + Beta + +

+ Science for UN Sustainable Development Goals +

+

+ Laying the foundation for new approaches and solutions. +

+
+ We have developed a classification scheme for UN Sustainable Development Goals, to view contributions of research towards complex challenges for humanity such as climate change, biodiversity loss, pollution and poverty reduction. +
+
+ + +
+
+
+ +
+ +
+
+
+
+
+
+ +
+
+ +
+
diff --git a/sdg/sdg.component.less b/sdg/sdg.component.less new file mode 100644 index 00000000..d24ff52d --- /dev/null +++ b/sdg/sdg.component.less @@ -0,0 +1,30 @@ +@import (reference) "~src/assets/openaire-theme/less/_import-variables"; + +@sdgs: #E6233D, #DF9F00, #19A220, #D70023, #FF0B00, #00BFE8, #FFC300, #B10240, #FF5D00, +#F50D86, #FF8A00, #CA8A03, #2B772B, #0098DF, #00B91C, #0069A2, #1C336A; + +custom-sdg-dot:after { + content: ""; + background-image: url("~src/assets/common-assets/sdg/sdg-dot-img.svg"); + display: inline-block; + background-size: 100% 100%; + height: 18px; + width: 18px; + margin-left: 10px; +} + +.sdg-card { + width: 265px; + height: 255px; + + img { + height: @global-control-height; + width: auto; + } + + each(@sdgs, { + &.sdg-@{index} { + background-color: @value !important; + } + }) +} diff --git a/sdg/sdg.component.ts b/sdg/sdg.component.ts new file mode 100644 index 00000000..ca24d9ae --- /dev/null +++ b/sdg/sdg.component.ts @@ -0,0 +1,97 @@ +import {HttpClient} from "@angular/common/http"; +import {Component, OnDestroy, OnInit} 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 {RefineFieldResultsService} from "../services/refineFieldResults.service"; +import {OpenaireEntities} from "../utils/properties/searchFields"; +import {StringUtils} from "../utils/string-utils.class"; +import {Router} from '@angular/router'; +import {Meta, Title} from "@angular/platform-browser"; +import {SEOService} from "../sharedComponents/SEO/SEO.service"; +import {PiwikService} from "../utils/piwik/piwik.service"; + +@Component({ + selector: 'sdg', + templateUrl: 'sdg.component.html', + styleUrls: ['sdg.component.less'] +}) +export class SdgComponent implements OnInit, OnDestroy { + public url: string = null; + public pageTitle: string = "OpenAIRE | Sustainable Development Goals"; + public pageDescription: string = "Laying the foundation for new approaches and solutions. We have developed a classification scheme for UN Sustainable Development Goals, to view contributions of research towards complex challenges for humanity such as climate change, biodiversity loss, pollution and poverty reduction."; + + private sdgs: any = []; + private sdgsResearchOutcomes: any = []; + public displayedSdgs: any = []; + + public loading: boolean; + properties: EnvProperties = properties; + openaireEntities = OpenaireEntities; + public breadcrumbs: Breadcrumb[] = [{name: 'home', route: '/'}, {name: 'Sustainable Development Goals'}]; + + subscriptions: Subscription[] = []; + + constructor( + private httpClient: HttpClient, private refineFieldResultsService: RefineFieldResultsService, + private _router: Router, + private _meta: Meta, + private _title: Title, + private seoService: SEOService, + private _piwikService: PiwikService + ) {} + + ngOnInit() { + this.loading = true; + if (this.properties.enablePiwikTrack && (typeof document !== 'undefined')) { + this.subscriptions.push(this._piwikService.trackView(this.properties, this.pageTitle, this.properties.piwikSiteId).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.httpClient.get(properties.domain+'/assets/common-assets/vocabulary/sdg.json').subscribe(data => { + this.sdgs = data['sdg']; + }); + this.refineFieldResultsService.getRefineFieldsResultsByEntityName(['sdg'], 'result', this.properties, null).subscribe(data => { + this.sdgsResearchOutcomes = data[1][0].values; + let merged =[]; + for(let i=0; i innerItem.id === this.sdgs[i].id)) + }); + } + this.displayedSdgs = merged; + this.loading = false; + }); + } + + public ngOnDestroy() { + for (let sub of this.subscriptions) { + sub.unsubscribe(); + } + } + + 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'"); + } +} diff --git a/sdg/sdg.module.ts b/sdg/sdg.module.ts new file mode 100644 index 00000000..258d0e01 --- /dev/null +++ b/sdg/sdg.module.ts @@ -0,0 +1,34 @@ +import {CommonModule} from "@angular/common"; +import {NgModule} from "@angular/core"; +import {FormsModule} from "@angular/forms"; +import {RouterModule} from "@angular/router"; +import {PreviousRouteRecorder} from "../utils/piwik/previousRouteRecorder.guard"; +import {BreadcrumbsModule} from "../utils/breadcrumbs/breadcrumbs.module"; +import {RefineFieldResultsServiceModule} from "../services/refineFieldResultsService.module"; +import {LoadingModule} from "../utils/loading/loading.module"; +import {Schema2jsonldModule} from "../sharedComponents/schema2jsonld/schema2jsonld.module"; +import {SEOServiceModule} from "../sharedComponents/SEO/SEOService.module"; +import {PiwikService} from "../utils/piwik/piwik.service"; + +import {SdgRoutingModule} from './sdg-routing.module'; +import {SdgComponent} from './sdg.component'; + +@NgModule({ + imports: [ + CommonModule, FormsModule, RouterModule, + SdgRoutingModule, BreadcrumbsModule, RefineFieldResultsServiceModule, + LoadingModule, Schema2jsonldModule, SEOServiceModule + ], + declarations: [ + SdgComponent + ], + providers: [ + PreviousRouteRecorder, PiwikService + ], + exports: [ + SdgComponent + ] +}) +export class SdgModule { + +} \ No newline at end of file