Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/bibl visualization #238

Merged
merged 29 commits into from
Jun 27, 2024
Merged
Show file tree
Hide file tree
Changes from 27 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
9845f21
Add bibliographicDesc to projectInfo
laurelled Mar 25, 2024
ee6ddae
Correct spelling mistake in bibliography-parser file name
laurelled Mar 26, 2024
f5d2551
Add bibliographic sort & order labels
laurelled Apr 4, 2024
7cf7e62
Add bibliographic styles to ui_config.js
laurelled Apr 4, 2024
e4f893e
Move bibliographic entries' parsing
laurelled Apr 8, 2024
6ab9c24
Add BibliographyInfo component
laurelled Apr 8, 2024
b9d5943
Add BibliographicStyleSelector component
laurelled Apr 8, 2024
d3eefcb
Add StyledBiblioEntry component
laurelled Apr 8, 2024
93fb4f5
Implement styling for bibliographic visualization
laurelled Apr 10, 2024
67e9bfd
Fix pubPlace typo in bibliography-parser.ts
laurelled Apr 12, 2024
d3fe9db
Change getChildrenTextAndSpecificAttribute to prevent null strings
laurelled Apr 15, 2024
3ea3018
Move space break insertion from bibliography parsing to visualization
laurelled Apr 15, 2024
77fb499
Add authorsDetails to BibliographicEntry parsing
laurelled Apr 15, 2024
86b5826
Add articleTitle and edition to BibliographyEntry parsing
laurelled Apr 15, 2024
d60c628
Expand citedRange and biblScope parsing
laurelled Apr 15, 2024
6a6fcce
Add idno, doi, volumeNumber, pageNumber and issueNumber to Bibliograp…
laurelled Apr 28, 2024
a1bb944
Fix modal.component.ts error on path handling
laurelled Apr 28, 2024
46f59f5
Refactor parsing methods in bibliography-parsers.ts
laurelled Apr 29, 2024
969b77b
Rename BibliographicEntry type properties from articleTitle & edition…
laurelled Apr 29, 2024
8f5c2b2
Change bibliographic styles id type to string to allow additional cus…
laurelled Apr 30, 2024
353c277
Add reprintedIn and editedBy labels for bibliographic visualization
laurelled Apr 30, 2024
418be25
Refactor allowedBibliographicStyle in ui_config
laurelled Apr 30, 2024
a18a0fe
Add BiblStructEntry handling in StyledBiblio component
laurelled Apr 30, 2024
4efd335
Add return type to functions in biblio-styled.component.ts
laurelled May 20, 2024
f082bb4
Add return functions type for bibliography-parser.ts
laurelled May 20, 2024
437d6fd
Add type annotations to bibliography visualization variables
laurelled Jun 21, 2024
c7789d8
Style bibliographic style selector
laurelled Jun 21, 2024
d70e2cd
Lint code
laurelled Jun 27, 2024
488ac0e
Merge branch 'develop' into feature/bibl-visualization
laurelled Jun 27, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions src/app/app.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,9 +92,40 @@ export interface UiConfig {
initNavBarOpened: boolean;
thumbnailsButton: boolean;
viscollButton: boolean;
defaultBibliographicStyle: string;
allowedBibliographicStyles: {
[key: string]: {
id: string;
label: string;
enabled: boolean;
propsOrder: BibliographicProperties[];
properties: BibliographicStyle;
}
};
theme: 'neutral' | 'modern' | 'classic';
syncZonesHighlightButton: boolean;
}
export type CitingRanges = 'issue' | 'volume' | 'page';
export type BibliographicProperties = 'author'| 'date'| 'title'| 'editor' | 'publication' | 'pubPlace' | 'publisher' | 'doi';
export type BibliographicStyle = Partial<{
propsDelimiter: string;
authorStyle: Partial<{
forenameInitials: boolean;
delimiter: string;
lastDelimiter: string;
order: Array<'forename' | 'surname'>;
maxAuthors: string;
}>;
publicationStyle: Partial<{
citingAcronym: 'all' | 'none' | CitingRanges[];
includeEditor: boolean;
inBrackets: CitingRanges[];
}>;
dateInsidePublication: boolean
titleQuotes: boolean;
emphasized: BibliographicProperties[];
inBrackets: BibliographicProperties[];
}>;

export interface EditionConfig {
editionTitle: string;
Expand Down
6 changes: 6 additions & 0 deletions src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ import { ApparatusEntryComponent } from './components/apparatus-entry/apparatus-
import { ApparatusEntryDetailComponent } from './components/apparatus-entry/apparatus-entry-detail/apparatus-entry-detail.component';
import { ApparatusEntryReadingsComponent } from './components/apparatus-entry/apparatus-entry-readings/apparatus-entry-readings.component';
import { BiblioEntryComponent } from './components/biblio/biblio.component';
import { BibliographyInfoComponent } from './components/bibliography-info/bibliography-info.component';
import { BibliographicStyleSelectorComponent } from './components/bibliography-info/bibliographic-style-selector/bibliographic-style-selector';
import { BiblioListComponent } from './components/biblioList/biblio-list.component';
import { CharComponent } from './components/char/char.component';
import { ChoiceComponent } from './components/choice/choice.component';
Expand Down Expand Up @@ -117,6 +119,7 @@ import { SourceNoteComponent } from './components/sources/source-note/source-not
import { SourcesComponent } from './components/sources/sources.component';
import { SourcesPanelComponent } from './panels/sources-panel/sources-panel.component';
import { StartsWithPipe } from './pipes/starts-with.pipe';
import { StyledBiblioEntryComponent } from './components/bibliography-info/biblio-styled/biblio-styled.component';
import { SuppliedComponent } from './components/supplied/supplied.component';
import { SurplusComponent } from './components/surplus/surplus.component';
import { TagsDeclComponent } from './components/tags-decl/tags-decl.component';
Expand Down Expand Up @@ -208,6 +211,8 @@ const DynamicComponents = [
AnnotatorDirective,
AppComponent,
BiblioEntryComponent,
BibliographyInfoComponent,
BibliographicStyleSelectorComponent,
BiblioListComponent,
CollationComponent,
ContentViewerComponent,
Expand Down Expand Up @@ -250,6 +255,7 @@ const DynamicComponents = [
SourcesComponent,
SourcesPanelComponent,
StartsWithPipe,
StyledBiblioEntryComponent,
TextPanelComponent,
TextSourcesComponent,
TextTextComponent,
Expand Down
2 changes: 1 addition & 1 deletion src/app/components/biblio/biblio.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<ng-container *ngIf="showEmptyValues || (data[element.value] && data[element.value].length > 0)">
<ng-container *ngIf="showAttrNames">{{element.value}}: </ng-container>
<ng-container *ngIf="data[element.value]">
{{ data[element.value] }} <span *ngIf="data[element.value].length > 0" [class.hidden]="(!isCommaSeparated)">,</span>
{{ data[element.value] }} <span *ngIf="data[element.value].length > 0" [class.hidden]="(!isCommaSeparated)">,</span>&nbsp;
</ng-container>
<br *ngIf="!inline && data[element.value].length > 0">
</ng-container>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
<ng-container *ngIf="biblEntry.type && (biblEntry.type.name === 'BibliographicEntry')">
<ng-container *ngIf="isStructured(biblEntry); else plainText">
<ng-container *ngFor="let element of showList">
<ng-template *ngTemplateOutlet="styledBiblioTemplate; context: { element, entries: [biblEntry] }"></ng-template>
</ng-container>
</ng-container>
<ng-template #plainText>{{ biblEntry.text }}</ng-template>
</ng-container>
<ng-container *ngIf="biblEntry.type && (biblEntry.type.name === 'BibliographicStructEntry')">
<ng-container *ngFor="let element of showList">
<ng-template *ngTemplateOutlet="styledBiblioTemplate; context: { element, entries: getContextForElement(element, biblEntry)}"></ng-template>
</ng-container>
</ng-container>

<ng-template #styledBiblioTemplate let-element="element" let-entries="entries">
<ng-container *ngIf="entries.length > 0 && firstContainsProperty(entries, element)">
<span [ngClass]="styleProperties?.emphasized && styleProperties.emphasized.includes(element) ? 'font-italic' : ''">
<ng-container *ngIf="styleProperties?.inBrackets && styleProperties?.inBrackets.includes(element)">(</ng-container><ng-template [ngTemplateOutlet]="this[element]" [ngTemplateOutletContext]="{ $implicit: entries }"></ng-template><ng-container *ngIf="styleProperties?.inBrackets && styleProperties?.inBrackets.includes(element)">)</ng-container>{{styleProperties?.propsDelimiter || ","}}
</span>
</ng-container>
</ng-template>

<ng-template #title let-entries>
<ng-container *ngIf="styleProperties?.titleQuotes">«</ng-container>{{ entries[0]?.titleDetails ? entries[0].titleDetails.title : entries[0].title }}<ng-container *ngIf="styleProperties?.titleQuotes">»</ng-container>
</ng-template>

<ng-template #author let-entries>
<ng-container *ngFor="let author of getAuthorsDetails(entries); last as last; index as index; count as count; ">
<ng-container *ngIf="author.forename && author.surname; else authorFullName">
<ng-container *ngFor="let name of (styleProperties.authorStyle?.order || ['surname', 'forename']); last as isLast">{{name === "forename" && styleProperties.authorStyle?.forenameInitials ? author.forenameInitials : author[name] }}<ng-container *ngIf="!isLast">,&nbsp;</ng-container></ng-container>
</ng-container>
<ng-template #authorFullName>{{ author.fullName }}</ng-template>
<ng-container *ngIf="!last">{{ index < count - 2 ? (styleProperties.authorStyle?.delimiter || ',') : (styleProperties.authorStyle?.lastDelimiter || " and") }}&nbsp;</ng-container>
</ng-container>
</ng-template>

<ng-template #publication let-entries>
<ng-container *ngFor="let monogr of entries; index as index; last as last">
<ng-container>{{ monogr.publication }}</ng-container>
<ng-container *ngIf="monogr?.volumeNumber">&nbsp;<span *ngIf="requiresAcronym('volume')">vol. </span>{{monogr.volumeNumber}}</ng-container>
<span class="font-normal">
<ng-container *ngIf="(index > 0 || styleProperties.publicationStyle?.includeEditor) && !containsOnlyEmptyValues(monogr.editor)">, <ng-template [ngTemplateOutlet]="editor" [ngTemplateOutletContext]="{ $implicit: [ monogr ]}"></ng-template>,</ng-container>
<ng-container *ngIf="monogr?.issueNumber">{{ styleProperties.publicationStyle?.inBrackets && styleProperties.publicationStyle?.inBrackets.includes('issue') ? '(' : ', ' }}<span *ngIf="requiresAcronym('issue')">no. </span>{{styleProperties?.publicationStyle?.inBrackets && styleProperties.publicationStyle?.inBrackets.includes('issue')? monogr.issueNumber + ')' : monogr.issueNumber }}</ng-container>
<ng-container *ngIf="styleProperties?.dateInsidePublication || index > 0">,&nbsp;<ng-container *ngIf="styleProperties?.inBrackets && styleProperties.inBrackets.includes('date')">(</ng-container><ng-template [ngTemplateOutlet]="date" [ngTemplateOutletContext]="{ $implicit: [ monogr ]}"></ng-template><ng-container *ngIf="styleProperties?.inBrackets && styleProperties.inBrackets.includes('date')">)</ng-container></ng-container>
<ng-container *ngIf="monogr?.pageNumber">: <span *ngIf="requiresAcronym('issue')">pp. </span>{{monogr.pageNumber}}</ng-container>
<ng-container *ngIf="index > 0">, <ng-container *ngFor="let property of getPublisherDetailsOrder(); last as last">{{monogr[property]}}<ng-container *ngIf="!last">: </ng-container></ng-container></ng-container>
<ng-container *ngIf="!last">&nbsp;{{ "reprintedIn" | translate }}&nbsp;</ng-container>
</span>
</ng-container>
</ng-template>

<ng-template #date let-entries>{{ entries[0].date[0] || 's.d.' }}</ng-template>

<ng-template #publisher let-entries>{{ entries[0].publisher }}</ng-template>

<ng-template #pubPlace let-entries>{{ entries[0].pubPlace }}</ng-template>

<ng-template #editor let-entries>
<ng-container *ngIf="!containsOnlyEmptyValues(entries[0].editor)">{{ "editedBy" | translate}}&nbsp;<ng-container *ngFor="let editor of entries[0].editor; last as last">{{editor}}<ng-container *ngIf="!last">,</ng-container></ng-container>
</ng-container>
</ng-template>

<ng-template #doi let-entries><ng-container *ngFor="let entry of entries"><a *ngIf="entry.doi" target="_blank" [href]="'https://doi.org/' + entry.doi">https://doi.org/{{entry.doi}}</a></ng-container></ng-template>
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
.font-italic {
font-style: italic;
}

.font-normal {
font-style: normal !important;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';

import { BiblioEntryComponent } from './biblio.component';

describe('BiblioComponent', () => {
let component: BiblioEntryComponent;
let fixture: ComponentFixture<BiblioEntryComponent>;

beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ BiblioEntryComponent ],
})
.compileComponents();
});

beforeEach(() => {
fixture = TestBed.createComponent(BiblioEntryComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});

it('should create', () => {
expect(component).toBeTruthy();
});
});
laurelled marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import { AfterViewInit, ChangeDetectorRef, Component, Input, OnChanges, SimpleChanges, TemplateRef, ViewChild } from '@angular/core';
import { AppConfig, BibliographicStyle, CitingRanges } from 'src/app/app.config';
import { AuthorDetail, BibliographicEntry, BibliographicStructEntry } from 'src/app/models/evt-models';

@Component({
selector: 'evt-styled-biblio-entry',
templateUrl: './biblio-styled.component.html',
styleUrls: ['./biblio-styled.component.scss'],
})
export class StyledBiblioEntryComponent implements OnChanges, AfterViewInit {

@ViewChild('title', { static: false }) title: TemplateRef<any>;
@ViewChild('author', { static: false }) author: TemplateRef<any>;
@ViewChild('publication', { static: false }) publication: TemplateRef<any>;
@ViewChild('editor', { static: false }) editor: TemplateRef<any>;
@ViewChild('date', { static: false }) date: TemplateRef<any>;
@ViewChild('pubPlace', { static: false }) pubPlace: TemplateRef<any>;
@ViewChild('publisher', { static: false }) publisher: TemplateRef<any>;
@ViewChild('doi', { static: false }) doi: TemplateRef<any>;


@Input() data: BibliographicEntry | BibliographicStructEntry;
@Input() style: string = AppConfig.evtSettings.ui.defaultBibliographicStyle;

public biblEntry: any;
public showList: string[];
public showAttrNames = AppConfig.evtSettings.edition.biblView.showAttrNames;
public showEmptyValues = AppConfig.evtSettings.edition.biblView.showEmptyValues;
public inline = AppConfig.evtSettings.edition.biblView.inline;
public isCommaSeparated = AppConfig.evtSettings.edition.biblView.commaSeparated;
public showMainElemTextContent = AppConfig.evtSettings.edition.biblView.showMainElemTextContent;
public styleProperties : BibliographicStyle;

flattenBiblStruct(entry: BibliographicStructEntry): BibliographicEntry[] {
return entry.analytic.concat(entry.monogrs.concat(entry.series));
}

getContextForElement(element: string, structEntry: BibliographicStructEntry): BibliographicEntry[]{
let context: BibliographicEntry[];
switch(element){
case 'title':
context = structEntry.analytic;
break;
case 'publication':
case 'date':
case 'publPlace':
case 'publisher':
case 'editor':
context = structEntry.monogrs;
break;
default:
context = this.flattenBiblStruct(structEntry);
break;
}

return context;
}

firstContainsProperty(entries: BibliographicEntry[], element:string): boolean{
const elementInFirstEntry = entries[0]?.[element];

return elementInFirstEntry && elementInFirstEntry.length > 0;
}

containsOnlyEmptyValues(arr: string[]): boolean{
return arr.reduce((prev, x) => (x === '') && prev, true);
}

requiresAcronym(elem: CitingRanges): boolean{
const publicationStyle = this.styleProperties.publicationStyle || { citingAcronym: 'none' };
if(!publicationStyle?.citingAcronym) { return false; }

if(publicationStyle.citingAcronym === 'all'){
return true;
}else if(publicationStyle.citingAcronym === 'none'){
return false;
}

return publicationStyle.citingAcronym.includes(elem);
}

getPublisherDetailsOrder(): string[]{
return this.showList.filter((x) => x === 'publisher' || x === 'pubPlace');
}

getAuthorsDetails(entries: BibliographicEntry[]): AuthorDetail[]{
return entries.reduce((prev, e) => prev.concat(e.authorsDetails), []);
}

isStructured(entry: BibliographicEntry): boolean{
// searching for the most relevant signs of a structured entry.
return entry.originalEncoding.querySelectorAll('title, author, date').length > 0;
}

ngOnChanges(changes: SimpleChanges): void {
if(this.data.type && this.data.type === BibliographicEntry){
this.biblEntry = this.data as BibliographicEntry;
}
if(this.data.type && this.data.type === BibliographicStructEntry){
this.biblEntry = this.data as BibliographicStructEntry;
}
if(changes.style){
this.showList = AppConfig.evtSettings.ui.allowedBibliographicStyles[this.style].propsOrder;
this.styleProperties = AppConfig.evtSettings.ui.allowedBibliographicStyles[this.style].properties;
}
}

constructor(private cd: ChangeDetectorRef){}
ngAfterViewInit(): void {
this.cd.detectChanges();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<div class="container p-0 m-0">
<div class="d-flex align-items-center">
<label for="biblStyleSelect">{{ 'style' | translate }}</label>
<div>
<ng-select
id="biblStyleSelect"
[items]="bibliographicStyles"
bindLabel="label"
bindValue="id"
[clearable]="false"
[searchable]="true"
[(ngModel)]="selectedStyleID"
(change)="changeStyle()"
(click)="stopPropagation($event)"
class="p-2">
</ng-select>
</div>
</div>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';

import { BibliographicStyleSelectorComponent } from './bibliographic-style-selector';

describe('BibliographicStyleSelectorComponent', () => {
let component: BibliographicStyleSelectorComponent;
let fixture: ComponentFixture<BibliographicStyleSelectorComponent>;

beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
declarations: [ BibliographicStyleSelectorComponent ],
})
.compileComponents();
}));

beforeEach(() => {
fixture = TestBed.createComponent(BibliographicStyleSelectorComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});

it('should create', () => {
expect(component).toBeTruthy();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { Component, EventEmitter, OnInit, Output } from '@angular/core';
import { AppConfig } from 'src/app/app.config';

@Component({
selector: 'evt-bibliographic-style-selector',
templateUrl: './bibliographic-style-selector.component.html',
styleUrls: ['./bibliographic-style-selector.component.scss'],
})
export class BibliographicStyleSelectorComponent implements OnInit {
public bibliographicStyles = (Object.values(AppConfig.evtSettings.ui.allowedBibliographicStyles) || []).filter((el) => el.enabled);
public selectedStyleID : string;

@Output() selectionChange: EventEmitter<string> = new EventEmitter<string>();

ngOnInit(){
this.selectedStyleID = AppConfig.evtSettings.ui.defaultBibliographicStyle;
this.selectionChange.emit(this.selectedStyleID);
}

stopPropagation(event: MouseEvent) {
event.stopPropagation();
}

changeStyle(){
this.selectionChange.emit(this.selectedStyleID);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<div>
<evt-bibliographic-style-selector (selectionChange)="setCurrentStyle($event)"></evt-bibliographic-style-selector>
</div>
<div class="evt-bibliography-content list-group">
<ng-container *ngFor="let elem of biblList">
<span>
<evt-styled-biblio-entry [data]="elem" [style]="currentStyle"></evt-styled-biblio-entry>
</span>
</ng-container>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
.bibliography_header {
position: relative;
margin-top: 0;
height: 45px;
max-height: 45px;
text-align: center;
}

.bibliography_header>div {
display: flex;
align-content: center;
}
Loading