diff --git a/src/app/app.component.css b/src/app/app.component.css index 89bf810..446982e 100644 --- a/src/app/app.component.css +++ b/src/app/app.component.css @@ -1,12 +1,13 @@ + body { padding-right: 50px; } .rect, .rect.unhovered { opacity:0.0; } .rect:hover, .rect.hovered { opacity:0.34000017; fill:#e2fa00; fill-opacity:0.98431373 } diff --git a/src/app/app.component.html b/src/app/app.component.html index 1df4d14..646d5dc 100644 --- a/src/app/app.component.html +++ b/src/app/app.component.html @@ -1,9 +1,16 @@ - -
- - - +
+ + + Standard + Green + + + +

Der späte Nietzsche, Manuskripte 1885-1889

+
+ + +
- - diff --git a/src/app/app.component.ts b/src/app/app.component.ts index c3fb079..3b466bf 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -1,26 +1,43 @@ -import { Component } from '@angular/core'; -import { QueryService } from './services/query.service'; -import { WordService } from './services/field-interaction.service'; -import { OptionService } from './services/options.service'; +import {Component, OnInit} from '@angular/core'; +import { OverlayContainer} from '@angular/cdk/overlay'; +import {QueryService} from './services/query.service'; +import {WordService} from './services/field-interaction.service'; +import {OptionService} from './services/options.service'; +import {Router} from "@angular/router"; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'], providers: [QueryService, WordService, OptionService ] }) -export class AppComponent { - title = 'svg-test-app'; - image = { height: 973.91998, width: 2038.5601, file_name: "/assets/W-II-1,131et132.jpg" }; - text_field = { left: 358.805, top: 78.051, width: 662.761, height: 831.879 }; - manuscript = { title: 'W II 1', - pages: [ - { number: '131', - metadata: [ - { head: { description: 'Stellenkommentar'}, content: [ { reference: '2', quote: 'Leben Thomas Carlyle’s', text: 'vgl. Froude, Das Leben Thomas Carlyles'} ]}, - { head: { description: 'Druckorte' }, content: [ { reference: '24-34', text: 'KGW VIII 9[11]'}, { reference: '34-40', text:'KGW VIII 9[12]'} ]} - ] - } - ] - }; +export class AppComponent implements OnInit { + title = 'nietzsche app'; + theme = 'standard-theme'; + manuscriptQuery: string; + manuscriptData: any; + chosenManuscript = 'W-II-1'; + chosenPage = '131et132'; + + constructor(private queryService: QueryService, private overlayContainer: OverlayContainer) { + } + + ngOnInit() { + this.overlayContainer.getContainerElement().classList.add(this.theme); + this.getManuscriptData(); + + } + + onThemeChange() { + this.overlayContainer.getContainerElement().classList.add(this.theme); + } + + getManuscriptData() { + this.queryService.getQueryfromFilename('manuscripts.rq').subscribe(q => { + this.manuscriptQuery = q; + this.queryService.getData(q).subscribe(manuscripts => { + this.manuscriptData = manuscripts; + console.log(manuscripts); }); + }); + } } diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 876e39a..5a4844a 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -1,98 +1,123 @@ import { AppComponent } from './app.component'; import { BrowserModule } from '@angular/platform-browser'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { CdkTableModule } from '@angular/cdk/table'; import { DragDropModule } from '@angular/cdk/drag-drop'; import { FormsModule, ReactiveFormsModule} from '@angular/forms'; import { NgModule } from '@angular/core'; - -import { MatDialogModule, MatToolbarModule} from '@angular/material'; +import {MatCardModule} from '@angular/material/card'; +import { MatDialogModule, MatToolbarModule, MatButtonModule} from '@angular/material'; import { MatExpansionModule } from '@angular/material/expansion'; import {MatIconModule} from '@angular/material/icon'; import { MatInputModule } from '@angular/material/input'; import { MatListModule } from '@angular/material/list'; import {MatMenuModule} from '@angular/material/menu'; import { MatPaginatorModule, MatSortModule, MatSidenavModule, MatCheckboxModule, MatRadioModule } from '@angular/material'; import { MatSelectModule } from '@angular/material/select'; import { MatTableModule } from '@angular/material/table'; import {MatTabsModule} from '@angular/material/tabs'; import {MatTooltipModule} from '@angular/material'; import { DataListViewComponent } from './data-list-view/data-list-view.component'; import { DataListViewTableComponent, HighlightPipe } from './data-list-view/data-list-view-table/data-list-view-table.component'; import { DialogComponent } from './dialog-component/dialog.component'; import { InfoBoxComponent } from './info-box-component/info-box.component'; import { TextFieldComponent} from './textfield-component/textfield.component'; import { WordPositionDirective } from './textfield-component/word-position.directive'; import { TextfieldOptionsComponentComponent } from './textfield-options-component/textfield-options-component.component'; import { CommonModule } from '@angular/common'; import { HttpClientModule } from '@angular/common/http'; import { RdfDataBrowserComponentComponent } from './rdf-data-browser-component/rdf-data-browser-component.component'; import { QueryService } from './services/query.service'; import {AnnotationService, TextStyleService} from './services/annotation.service'; import { DisplayedCollumnsService } from './data-list-view/data-list-view-services/table-data.service'; import {DataListViewSettings} from './data-list-view/data-list-view-settings/data-list-view-settings.service'; import { MarkupTextComponentComponent } from './markup-text-component/markup-text-component.component'; import { BergwerkeComponent } from './bergwerke/bergwerke.component'; import { SubmenuComponentComponent } from './markup-text-component/submenu-component/submenu-component.component'; import { MarkupTextButtonsComponentComponent } from './markup-text-component/markup-text-buttons-component/markup-text-buttons-component.component'; import { GenericControlsComponent } from './generic-control-elements-component/generic-control-elements-component.component'; import { MarkupHyperlinkComponentComponent } from './markup-text-component/markup-hyperlink-component/markup-hyperlink-component.component'; import { MarkupTextIconComponent } from './markup-text-component/markup-text-icon-component/markup-text-icon.component'; import { UploadRdfDataComponentComponent } from './upload-rdf-data-component/upload-rdf-data-component.component'; import { CustomizableSearchComponentComponent } from './customizable-search-component/customizable-search-component.component'; +import { SelectComponentComponent } from './generic-control-elements-component/select-component/select-component.component'; +import { RadioComponentComponent } from './generic-control-elements-component/radio-component/radio-component.component'; +import { CheckboxComponentComponent } from './generic-control-elements-component/checkbox-component/checkbox-component.component'; +import {GenericControlElementsService} from './generic-control-elements-component/generic-control-elements-service.service'; +import {routing} from './app.routing'; +import {HomeComponent} from './home.component'; +import { ManuscriptViewComponentComponent } from './manuscript-view-component/manuscript-view-component.component'; +import { TextViewComponentComponent } from './text-view-component/text-view-component.component'; +import { ContentViewTabComponentComponent } from './content-view-tab-component/content-view-tab-component.component'; +import { RhizomeViewComponentComponent } from './rhizome-view-component/rhizome-view-component.component'; +import { MainMenuComponentComponent } from './main-menu-component/main-menu-component.component'; +import { SidenavComponentComponent } from './sidenav-component/sidenav-component.component'; @NgModule({ declarations: [ AppComponent, + HomeComponent, TextFieldComponent, WordPositionDirective, InfoBoxComponent, TextfieldOptionsComponentComponent, DataListViewComponent, DataListViewTableComponent, DialogComponent, HighlightPipe, RdfDataBrowserComponentComponent, MarkupTextComponentComponent, BergwerkeComponent, SubmenuComponentComponent, MarkupTextButtonsComponentComponent, GenericControlsComponent, MarkupHyperlinkComponentComponent, MarkupTextIconComponent, UploadRdfDataComponentComponent, - CustomizableSearchComponentComponent + CustomizableSearchComponentComponent, + SelectComponentComponent, + RadioComponentComponent, + CheckboxComponentComponent, + ManuscriptViewComponentComponent, + TextViewComponentComponent, + ContentViewTabComponentComponent, + RhizomeViewComponentComponent, + MainMenuComponentComponent, + SidenavComponentComponent ], imports: [ + routing, BrowserModule, BrowserAnimationsModule, CdkTableModule, CommonModule, DragDropModule, HttpClientModule, + MatButtonModule, + MatCardModule, MatCheckboxModule, MatDialogModule, MatExpansionModule, MatIconModule, MatInputModule, MatListModule, MatMenuModule, MatPaginatorModule, MatRadioModule, MatSelectModule, MatSidenavModule, MatSortModule, MatTableModule, MatTabsModule, MatToolbarModule, MatTooltipModule, FormsModule, ReactiveFormsModule ], - providers: [ QueryService, DisplayedCollumnsService, DataListViewSettings, TextStyleService, AnnotationService ], + providers: [ QueryService, DisplayedCollumnsService, DataListViewSettings, TextStyleService, AnnotationService, GenericControlElementsService ], bootstrap: [AppComponent], entryComponents: [DialogComponent] }) export class AppModule { } diff --git a/src/app/app.routing.ts b/src/app/app.routing.ts new file mode 100644 index 0000000..c682c53 --- /dev/null +++ b/src/app/app.routing.ts @@ -0,0 +1,17 @@ +import {RouterModule, Routes} from '@angular/router'; +import {BergwerkeComponent} from './bergwerke/bergwerke.component'; +import {HomeComponent} from './home.component'; +import {TextViewComponentComponent} from './text-view-component/text-view-component.component'; +import {ContentViewTabComponentComponent} from './content-view-tab-component/content-view-tab-component.component'; +import {CONTENT_ROUTES} from './content-view-tab-component/content-view-routes'; + +const APP_ROUTES: Routes = [ + {path: '', redirectTo: 'home', pathMatch: 'prefix'}, + {path: 'home', component: HomeComponent}, + {path: 'bergwerke', component: BergwerkeComponent}, + {path: 'contentView', component: ContentViewTabComponentComponent, children: CONTENT_ROUTES} +]; + +export const routing = RouterModule.forRoot(APP_ROUTES); + +// c=edition&man=XYZ&page=11b&word=rect45&viewtab=transcription&view=2&infobox=true diff --git a/src/app/bergwerke/bergwerke.component.html b/src/app/bergwerke/bergwerke.component.html index 5eb0e58..60cb9d7 100644 --- a/src/app/bergwerke/bergwerke.component.html +++ b/src/app/bergwerke/bergwerke.component.html @@ -1,11 +1,13 @@

This is a Text app styled with markup component

+

Unverhofftes Wiedersehen

+ diff --git a/src/app/bergwerke/bergwerke.component.ts b/src/app/bergwerke/bergwerke.component.ts index aecd5c4..443b275 100644 --- a/src/app/bergwerke/bergwerke.component.ts +++ b/src/app/bergwerke/bergwerke.component.ts @@ -1,64 +1,66 @@ import { Component, OnInit } from '@angular/core'; import markupDefaultSettings from '../markup-text-component/markup-text-settings.json'; import {DialogComponent} from '../dialog-component/dialog.component'; import {MatDialog} from '@angular/material'; +import {GenericControlElementsService} from '../generic-control-elements-component/generic-control-elements-service.service'; @Component({ selector: 'app-bergwerke', templateUrl: './bergwerke.component.html', - styleUrls: ['./bergwerke.component.scss'] + styleUrls: ['./bergwerke.component.scss'], + providers: [GenericControlElementsService] }) export class BergwerkeComponent implements OnInit { myText = 'In Falun in Schweden küßte vor guten fünfzig Jahren und mehr ein junger Bergmann seine junge hübsche Braut und sagte zu ihr: »Auf Sankt Luciä wird unsere Liebe von des Priesters Hand gesegnet. Dann sind wir Mann und Weib, und bauen uns ein eigenes Nestlein.« – »Und Friede und Liebe soll darin wohnen«, sagte die schöne Braut mit holdem Lächeln, »denn du bist mein einziges und alles, und ohne dich möchte ich lieber im Grab sein, als an einem andern Ort.« Als sie aber vor St. Luciä der Pfarrer zum zweitenmal in der Kirche ausgerufen hatte: »So nun jemand Hindernis wüßte anzuzeigen, warum diese Personen nicht möchten ehelich zusammenkommen« – da meldete sich der Tod. Denn als der Jüngling den andern Morgen in seiner schwarzen Bergmannskleidung an ihrem Haus vorbeiging, der Bergmann hat sein Totenkleid immer an, da klopfte er zwar noch einmal an ihrem Fenster, und sagte ihr guten Morgen, aber keinen guten Abend mehr. Er kam nimmer aus dem Bergwerk zurück, und sie saumte vergeblich selbigen Morgen ein schwarzes Halstuch mit rotem Rand für ihn zum Hochzeittag, sondern als er nimmer kam, legte sie es weg, und weinte um ihn und vergaß ihn nie. Unterdessen wurde die Stadt Lissabon in Portugal durch ein Erdbeben zerstört, und der Siebenjährige Krieg ging vorüber, und Kaiser Franz der Erste starb, und der Jesuitenorden wurde aufgehoben und Polen geteilt, und die Kaiserin Maria Theresia starb, und der Struensee wurde hingerichtet, Amerika wurde frei, und die vereinigte französische und spanische Macht konnte Gibraltar nicht erobern. Die Türken schlossen den General Stein in der Veteraner Höhle in Ungarn ein, und der Kaiser Joseph starb auch. Der König Gustav von Schweden eroberte russisch Finnland, und die Französische Revolution und der lange Krieg fing an, und der Kaiser Leopold der Zweite ging auch ins Grab. Napoleon eroberte Preußen, und die Engländer bombardierten Kopenhagen, und die Ackerleute säeten und schnitten. Der Müller mahlte, und die Schmiede hämmerten, und die Bergleute gruben nach den Metalladern in ihrer unterirdischen Werkstatt. Als aber die Bergleute in Falun im Jahr 1809 etwas vor oder nach Johannis zwischen zwei Schachten eine Öffnung durchgraben wollten, gute dreihundert Ellen tief unter dem Boden gruben sie aus dem Schutt und Vitriolwasser den Leichnam eines Jünglings heraus, der ganz mit Eisenvitriol durchdrungen, sonst aber unverwest und unverändert war; also daß man seine Gesichtszüge und sein Alter noch völlig erkennen konnte, als wenn er erst vor einer Stunde gestorben, oder ein wenig eingeschlafen wäre, an der Arbeit. Als man ihn aber zu Tag ausgefördert hatte, Vater und Mutter, Gefreundte und Bekannte waren schon lange tot, kein Mensch wollte den schlafenden Jüngling kennen oder etwas von seinem Unglück wissen, bis die ehemalige Verlobte des Bergmanns kam, der eines Tages auf die Schicht gegangen war und nimmer zurückkehrte. Grau und zusammengeschrumpft kam sie an einer Krücke an den Platz und erkannte ihren Bräutigam; und mehr mit freudigem Entzücken als mit Schmerz sank sie auf die geliebte Leiche nieder, und erst als sie sich von einer langen heftigen Bewegung des Gemüts erholt hatte, »es ist mein Verlobter«, sagte sie endlich, »um den ich fünfzig Jahre lang getrauert hatte, und den mich Gott noch einmal sehen läßt vor meinem Ende. Acht Tage vor der Hochzeit ist er unter die Erde gegangen und nimmer heraufgekommen.« Da wurden die Gemüter aller Umstehenden von Wehmut und Tränen ergriffen, als sie sahen die ehemalige Braut jetzt in der Gestalt des hingewelkten kraftlosen Alters und den Bräutigam noch in seiner jugendlichen Schöne, und wie in ihrer Brust nach 50 Jahren die Flamme der jugendlichen Liebe noch einmal erwachte; aber er öffnete den Mund nimmer zum Lächeln oder die Augen zum Wiedererkennen; und wie sie ihn endlich von den Bergleuten in ihr Stüblein tragen ließ, als die einzige, die ihm angehöre, und ein Recht an ihn habe, bis sein Grab gerüstet sei auf dem Kirchhof. Den andern Tag, als das Grab gerüstet war auf dem Kirchhof und ihn die Bergleute holten, schloß sie ein Kästlein auf, legte sie ihm das schwarzseidene Halstuch mit roten Streifen um, und begleitete ihn alsdann in ihrem Sonntagsgewand, als wenn es ihr Hochzeittag und nicht der Tag seiner Beerdigung wäre. Denn als man ihn auf dem Kirchhof ins Grab legte, sagte sie: »Schlafe nun wohl, noch einen Tag oder zehen im kühlen Hochzeitbett, und laß dir die Zeit nicht lange werden. Ich habe nur noch wenig zu tun, und komme bald, und bald wird\'s wieder Tag. – Was die Erde einmal wiedergegeben hat, wird sie zum zweitenmal auch nicht behalten«, sagte sie, als sie fortging, und noch einmal umschaute.'; mySettings = JSON.parse(JSON.stringify(markupDefaultSettings)); myStandoffData: Array> = [ [ "stoff:SemanticAnnotation", 3, 7, 'humgeo:City', "https://www.wikidata.org/wiki/Q26509"], [ "stoff:CssStyle", 0, 19, "font-style: italic;"], [ "stoff:SemanticAnnotation", 12, 19, 'humgeo:State', "https://www.wikidata.org/wiki/Q34"], [ "stoff:SemanticAnnotation", 1662, 1666, 'human:Organization', "https://de.wikipedia.org/wiki/K%C3%B6nig"], [ "stoff:SemanticAnnotation", 1679, 1686, 'humgeo:State', "https://de.wikipedia.org/wiki/Schweden"], [ "stoff:SemanticAnnotation", 1662, 1686, 'human:Person', "https://de.wikipedia.org/wiki/Karl_X._Gustav"], [ "stoff:CssStyle", 110, 301, "font-style: italic;"], [ "stoff:CssStyle", 346, 457, "font-style: italic;"], [ "stoff:CssStyle", 544, 645, "font-style: italic;"], [ "stoff:CssStyle", 3163, 3186, "font-style: italic;"], [ "stoff:CssStyle", 3207, 3398, "font-style: italic;"], [ "stoff:CssStyle", 4334, 4605, "font-style: italic;"], [ "stoff:CssStyle", 2097, 2101, "font-weight: bold;"], [ "stoff:CssStyle", 2097, 2101, "color: blue;"], [ "stoff:CssStyle", 2097, 2101, "text-decoration: underline;"], [ "stoff:CssStyle", 1182, 1190, "text-decoration: underline;"], [ "stoff:CssStyle", 1182, 1190, "font-weight: bold;"], [ "stoff:CssStyle", 1182, 1190, "color: blue;"], [ "stoff:CssStyle", 1443, 1449, "text-decoration: underline;"], [ "stoff:CssStyle", 1443, 1449, "font-weight: bold;"], [ "stoff:CssStyle", 1443, 1449, "color: blue;"], [ "stoff:CssStyle", 1285, 1300, "text-decoration: underline;"], [ "stoff:CssStyle", 1285, 1300, "font-weight: bold;"], [ "stoff:CssStyle", 1285, 1300, "color: red;"], [ "stoff:CssStyle", 1316, 1328, "text-decoration: underline;"], [ "stoff:CssStyle", 1316, 1328, "color: green;"], [ "stoff:CssStyle", 1383, 1396, "text-decoration: underline;"], [ "stoff:CssStyle", 1383, 1396, "font-weight: bold;"], [ "stoff:CssStyle", 1383, 1396, "color: red;"], [ "stoff:CssStyle", 1639, 1644, "text-decoration: underline;"], [ "stoff:CssStyle", 1639, 1644, "font-weight: bold;"], [ "stoff:CssStyle", 1639, 1644, "color: red;"] ]; constructor( private dialog: MatDialog ) { } ngOnInit() { } private openOptionsDialog() { this.dialog.open(DialogComponent, { data: { message: this.mySettings, buttonText: {cancel: 'close'}, title: 'Options', contentType: 0 }, }); } } diff --git a/src/app/content-view-tab-component/content-view-routes.ts b/src/app/content-view-tab-component/content-view-routes.ts new file mode 100644 index 0000000..8deafe1 --- /dev/null +++ b/src/app/content-view-tab-component/content-view-routes.ts @@ -0,0 +1,11 @@ +import { Routes } from '@angular/router'; +import {ManuscriptViewComponentComponent} from "../manuscript-view-component/manuscript-view-component.component"; +import {RhizomeViewComponentComponent} from "../rhizome-view-component/rhizome-view-component.component"; +import {TextViewComponentComponent} from "../text-view-component/text-view-component.component"; + +export const CONTENT_ROUTES: Routes = [ + { path: '', redirectTo: 'manuscript', pathMatch: 'prefix' }, + { path: 'manuscript', component: ManuscriptViewComponentComponent }, + { path: 'text', component: TextViewComponentComponent }, + { path: 'rhizome', component: RhizomeViewComponentComponent } +]; diff --git a/src/app/content-view-tab-component/content-view-tab-component.component.html b/src/app/content-view-tab-component/content-view-tab-component.component.html new file mode 100644 index 0000000..3673522 --- /dev/null +++ b/src/app/content-view-tab-component/content-view-tab-component.component.html @@ -0,0 +1,10 @@ + + diff --git a/src/app/content-view-tab-component/content-view-tab-component.component.scss b/src/app/content-view-tab-component/content-view-tab-component.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/src/app/content-view-tab-component/content-view-tab-component.component.spec.ts b/src/app/content-view-tab-component/content-view-tab-component.component.spec.ts new file mode 100644 index 0000000..c4396c1 --- /dev/null +++ b/src/app/content-view-tab-component/content-view-tab-component.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ContentViewTabComponentComponent } from './content-view-tab-component.component'; + +describe('ContentViewTabComponentComponent', () => { + let component: ContentViewTabComponentComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ ContentViewTabComponentComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ContentViewTabComponentComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/content-view-tab-component/content-view-tab-component.component.ts b/src/app/content-view-tab-component/content-view-tab-component.component.ts new file mode 100644 index 0000000..efb93d7 --- /dev/null +++ b/src/app/content-view-tab-component/content-view-tab-component.component.ts @@ -0,0 +1,36 @@ +import { Component, OnInit } from '@angular/core'; +import {Router} from '@angular/router'; + +@Component({ + selector: 'app-content-view-tab-component', + templateUrl: './content-view-tab-component.component.html', + styleUrls: ['./content-view-tab-component.component.scss'] +}) +export class ContentViewTabComponentComponent implements OnInit { + + navLinks: any[]; + activeLinkIndex = -1; + + constructor(private router: Router) { + this.navLinks = [ + { + label: 'Manuskripte', + link: 'manuscript', + index: 0 + }, { + label: 'Seitenansicht', + link: 'text', + index: 1 + }, { + label: 'Rhizome-Ansicht', + link: 'rhizome', + index: 2 + }, + ]; + } + ngOnInit(): void { + this.router.events.subscribe((res) => { + this.activeLinkIndex = this.navLinks.indexOf(this.navLinks.find(tab => tab.link === '.' + this.router.url)); + }); + } +} diff --git a/src/app/customizable-search-component/customizable-search-component.component.html b/src/app/customizable-search-component/customizable-search-component.component.html index 3841c0a..e30112a 100644 --- a/src/app/customizable-search-component/customizable-search-component.component.html +++ b/src/app/customizable-search-component/customizable-search-component.component.html @@ -1,2 +1,15 @@

Customizable search

- +

Edit

+ + + + +
+

Preview

+ +
+

Loaded mask

+ + diff --git a/src/app/customizable-search-component/customizable-search-component.component.ts b/src/app/customizable-search-component/customizable-search-component.component.ts index 68e87d4..bc83e8a 100644 --- a/src/app/customizable-search-component/customizable-search-component.component.ts +++ b/src/app/customizable-search-component/customizable-search-component.component.ts @@ -1,24 +1,46 @@ -import { Component, OnInit } from '@angular/core'; -import mySearchSettings from './my_search_mask.json'; +import {Component, OnChanges, OnInit} from '@angular/core'; +import mySearchSettings from './search_mask.json'; +import { + ControlElement, + ControlElementGroup, + ControlElementOption, + GenericControlElementsService, SelectionBundle +} from '../generic-control-elements-component/generic-control-elements-service.service'; @Component({ selector: 'app-customizable-search-component', templateUrl: './customizable-search-component.component.html', - styleUrls: ['./customizable-search-component.component.scss'] + styleUrls: ['./customizable-search-component.component.scss'], + providers: [GenericControlElementsService] }) export class CustomizableSearchComponentComponent implements OnInit { - constructor() { } + constructor( private controlElementService: GenericControlElementsService) { } searchMaskDefinitions = JSON.parse(JSON.stringify(mySearchSettings)); + selection: SelectionBundle; + controlElementGroups: Array; // thematic groups of controlElements + controlElements: Array; + controlElementOptions: Array; // selections available for a control like a drop down box or radiobuttons + newlyGeneratedMask: any; + ready = false; ngOnInit() { + if (this.searchMaskDefinitions) { + const a = this.searchMaskDefinitions; + a['description'] = 'new description'; + const fuu = this.writeJson(a); + this.newlyGeneratedMask = JSON.parse(fuu); + this.ready = true; + } + } + + private addNewControlElementGroup() { } - // Todo: Load from JSON - - // create Control --> - - // Save to JSON - + private writeJson(o) { + const f = JSON.stringify(o); + console.log('loaded mask', this.newlyGeneratedMask); + return f; + } } diff --git a/src/app/customizable-search-component/my_search_mask.json b/src/app/customizable-search-component/my_search_mask.json deleted file mode 100644 index bde739f..0000000 --- a/src/app/customizable-search-component/my_search_mask.json +++ /dev/null @@ -1,105 +0,0 @@ -{ "displaySettings": true, - "multiToggle": true, - "queryLanguage": "SPARQL", - "settings":[ - { - "label": "styles", - "pannelOpenState": true, - "description": "Styles (non semantic markup only)", - "entries": [ - { - "label":"style", - "description":"render styles", - "value":false, - "type":"checkbox" - } - ] - }, - { - "label":"semanticMarkup", - "pannelOpenState": true, - "condition": [ - {"option":"markup.renderMarkup", "value":"all"}, - {"option":"markup.renderMarkup", "value":"semanticAnnotation"} - ], - "description":"Authors", - "entries":[ - { - "label":"authors", - "description":"select author", - "query": "SELECT ?S from {?s a lombardus:author}", - "options":[ - - ], - "value":"icon", - "type":"select" - }, - { - "label":"renderIcons", - "description":"render semantic overlaps", - "options":[ - { - "id":"split", - "description":"split up in multiple icons" - }, - { - "id":"group", - "description":"group polysemantics in one icon" - } - ], - "value":"after", - "type":"radio", - "condition": [{"option":"semanticMarkup.styleType", "value":"icon"}] - }, - { - "label":"position", - "description":"position them", - "options":[ - { - "id":"before", - "description":"before the text" - }, - { - "id":"after", - "description":"after the text" - } - ], - "value":"after", - "type":"radio", - "condition": [{"option":"semanticMarkup.styleType", "value":"icon"}] - }, - { - "label":"actions", - "description":"action to take place on click at a semantic annotation", - "options":[ - { - "id":"external", - "description":"open external link" - }, - { - "id":"pannel", - "description":"display resource in pannel" - }, - { - "id":"menu", - "description":"open menu" - } - ], - "value":"menu", - "type":"select", - "condition": [ - {"option": "semanticMarkup.styleType", "value": "icon"}, - {"option": "semanticMarkup.styleType", "value": "button"}, - {"option": "semanticMarkup.styleType", "value": "hyperlink"} - ] - }, - { - "label":"tooltip", - "description":"show tooltip", - "value":true, - "type":"checkbox" - } - ] - } - ] -} diff --git a/src/app/customizable-search-component/search_mask.json b/src/app/customizable-search-component/search_mask.json new file mode 100644 index 0000000..4d97330 --- /dev/null +++ b/src/app/customizable-search-component/search_mask.json @@ -0,0 +1,59 @@ +{ "type": "genericSearch", + "displaySettings": true, + "multiToggle": true, + "controlElementGroups":[ + { + "label": "genericearch", + "pannelOpenState": true, + "description": "Selection", + "toolTip": "Select main resource", + "controlElements": [ + { + "label":"thing", + "description":"fuu sth", + "type":"select", + "genericOptions": { + "queryString": "" + }, + "value": "author" + } + ] + }, + { + "label":"optional", + "pannelOpenState": true, + "description":"Optional: And give me theese informations if existing)", + "toolTip:": "These will be outputtet in the result set. Not filtered", + "controlElements":[ + { + "label":"bbb", + "description":"render sometthing", + "options":[ + { + "id":"button", + "description":"buttons" + }, + { + "id":"icon", + "description":"icons" + }, + { + "id":"hyperlink", + "description":"text with hyperlinks" + }, + { + "id":"markup", + "description":"whatever" + }, + { + "id":"none", + "description":"plane unstyled text" + } + ], + "value":"", + "type":"select" + } + ] + } + ] +} diff --git a/src/app/dialog-component/dialog.component.html b/src/app/dialog-component/dialog.component.html index 47a903b..97d8ab1 100644 --- a/src/app/dialog-component/dialog.component.html +++ b/src/app/dialog-component/dialog.component.html @@ -1,17 +1,17 @@

{{this.title}}

- +
diff --git a/src/app/dialog-component/dialog.component.ts b/src/app/dialog-component/dialog.component.ts index a05b13d..d7b52c4 100644 --- a/src/app/dialog-component/dialog.component.ts +++ b/src/app/dialog-component/dialog.component.ts @@ -1,39 +1,40 @@ import {Component, Inject, Injectable} from '@angular/core'; import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material'; +import {GenericControlElementsService} from '../generic-control-elements-component/generic-control-elements-service.service'; @Component({ selector: 'dialog-component', templateUrl: './dialog.component.html', styleUrls: ['./dialog.component.css'] }) @Injectable() export class DialogComponent { // DEFAULT values in case there is no initialContent or button text passed from app initialContent = 'loading resource'; cancelButtonText = 'close'; title: string; contentType: number; // 0 === OptionSettings, 1 === data browser constructor( @Inject(MAT_DIALOG_DATA) private data: any, private dialogRef: MatDialogRef ) { this.dialogRef.updateSize('1200vw', '800vw'); this.title = data.message; // in case there is no data/initialContent passed the default this.initialContent will be displayed- Same for button text. // if (data) { this.initialContent = data.message || this.initialContent; this.title = data.title; this.contentType = data.contentType; if (data.buttonText) { this.cancelButtonText = data.buttonText.cancel || this.cancelButtonText; } } else { console.log('no data passed to dialog.'); } } onConfirmClick(): void { this.dialogRef.close(true); } } diff --git a/src/app/generic-control-elements-component/generic-control-elements-component.component.html b/src/app/generic-control-elements-component/generic-control-elements-component.component.html index 8ccfff7..4841f29 100644 --- a/src/app/generic-control-elements-component/generic-control-elements-component.component.html +++ b/src/app/generic-control-elements-component/generic-control-elements-component.component.html @@ -1,61 +1,43 @@ -
+

- - -

{{group.description}}

-
+ + {{group.description}}
- -
-

-
{{controlElement.description}}
- - - {{option.description}} - - -
- -
-

-
{{controlElement.description}}
- -
- {{option.description}} -
-
-
- -
-

- - {{controlElement.description}} - -
-
-

- - {{controlElement.description}} - +
+

{{controlElement.description}}

+
+ +
+
+ +
+
+ +
+
+ +
diff --git a/src/app/generic-control-elements-component/generic-control-elements-component.component.ts b/src/app/generic-control-elements-component/generic-control-elements-component.component.ts index b79fb0a..e83db72 100644 --- a/src/app/generic-control-elements-component/generic-control-elements-component.component.ts +++ b/src/app/generic-control-elements-component/generic-control-elements-component.component.ts @@ -1,42 +1,40 @@ import {Component, Input, OnInit} from '@angular/core'; -import {GenericOptionsService, SelectionBundle} from './generic-control-elements-service.service'; +import {ControlMask, GenericControlElementsService, SelectionBundle} from './generic-control-elements-service.service'; @Component({ selector: 'app-generic-control-elements-component', templateUrl: './generic-control-elements-component.component.html', styleUrls: ['./generic-control-elements-component.component.scss'] }) export class GenericControlsComponent implements OnInit { - @Input() initialSettings?: any; + @Input() initialSettingsJson?: any; + @Input() initialSettings?: ControlMask; selection: SelectionBundle; // the selected values in one object - constructor(private optionService: GenericOptionsService) { } + constructor(private optionService: GenericControlElementsService) { } ngOnInit() { - if (this.initialSettings) { - console.log('ControlSettings loaded by input: ', this.initialSettings); - this.selection = this.optionService.getSelectionFromJson(this.initialSettings); + if (this.initialSettingsJson) { + this.selection = this.optionService.getSelectionFromJson(this.initialSettingsJson); + console.log('ControlSettings loaded by input: ', this.initialSettingsJson); console.log(this.selection); - this.optionService.getOptionsFromJson(this.initialSettings); - } else {console.log('no ControlSettings passed to display'); } + this.initialSettings = this.optionService.getOptionsFromJson(this.initialSettingsJson); + console.log('wrong settings?', this.initialSettings); + } } - onSelectValueChange(settingGroup, changedSetting) { - this.selection[settingGroup][changedSetting.label] = changedSetting.value; - this.optionService.setSelection(this.selection); - console.log('selections set: ', this.selection); - } // evaluates conditions on one control field private evaluateDisplayOption(conditions) { if (conditions) { let evaluator = false; for (const condition of conditions) { for (const val of condition.values) { if (val === this.selection[condition.group][condition.option]) { evaluator = true; + // console.log('Evaluating val', val, '| vs |', this.selection[condition.group][condition.option]); } } } return evaluator; } else {return true; } } } diff --git a/src/app/generic-control-elements-component/generic-control-elements-service.service.ts b/src/app/generic-control-elements-component/generic-control-elements-service.service.ts index 006b9ce..19b9e30 100644 --- a/src/app/generic-control-elements-component/generic-control-elements-service.service.ts +++ b/src/app/generic-control-elements-component/generic-control-elements-service.service.ts @@ -1,159 +1,213 @@ import {EventEmitter, Injectable} from '@angular/core'; -@Injectable({ - providedIn: 'root' - // Todo: 1 service instance per component - -}) -export class GenericOptionsService { +@Injectable() +export class GenericControlElementsService { selection: SelectionBundle; // collection of all selected values selectionEvent: EventEmitter = new EventEmitter(); // Settings of the control interface - optionSettings: OptionSettings; // overall options to render/change + controlMask: ControlMask; // overall options to render/change + controlMaskEvent: EventEmitter = new EventEmitter(); controlElementGroups: Array; // thematic groups of controlElements controlElements: Array; + controlElement: ControlElement; controlElementOptions: Array; // selections available for a control like a drop down box or radiobuttons - + genericOptionSet: any; // options object for generic search; constructor() { this.selection = new SelectionBundle(); } public getSelectionFromJson(initialSettings: any) { for (const group of initialSettings.controlElementGroups) { const controlSettingGroup = new SelectionGroup(); for (const entry of group.controlElements) { controlSettingGroup[entry.label] = entry.value; } this.selection[group.label] = controlSettingGroup ; } return this.selection; } - public setSelection(newSelection: SelectionBundle) { - this.selection = newSelection; - this.selectionEvent.emit(this.selection); + public setControlOptions(control) { + this.controlElementOptions = []; + for (const option of control.options ) { + const o = new ControlElementOption(option.id, option.description); + this.controlElementOptions.push(o); + } } - public createOptionsBundle( ) { + public getGenericControlOptions() { + } public getOptionsFromJson(settings) { - console.log('settings', settings); this.controlElementGroups = []; for (const settingGroup of settings.controlElementGroups) { + this.controlElements = []; for (const control of settingGroup.controlElements) { - this.controlElements = []; - if (control.type !== 'checkbox') { - this.controlElementOptions = []; - for (const option of control.options ) { - const o = new ControlElementOption(option.id, option.description); - this.controlElementOptions.push(o); - } + if (settings.type === 'staticOptions' && control.type !== 'checkbox' && control.type !== 'input') { + this.setControlOptions(control); + this.controlElement = new ControlElement(control.label, control.description, control.type, control.value, control.condition, this.controlElementOptions, null, control.toolTip); + } + if (settings.type === 'staticOptions' && control.type === 'checkbox' ) { + this.controlElement = new ControlElement(control.label, control.description, control.type, control.value, control.condition, null, null, control.toolTip); } - const c = new ControlElement(control.label, control.pannelOpenState, control.description, this.controlElementOptions, control.condition); - this.controlElements.push(c); + if (settings.type === 'genericSearch') { + this.genericOptionSet = control.genericOptionSet; + this.controlElement = new ControlElement(control.label, control.description, control.type, control.value, control.condition, null, this.genericOptionSet, control.toolTip); + } + this.controlElements.push(this.controlElement); } const oGroup = new ControlElementGroup(settingGroup.label, settingGroup.pannelOpenState, settingGroup.description, this.controlElements, settingGroup.condition); - console.log('oGroup', oGroup); this.controlElementGroups.push(oGroup); } - this.optionSettings = new OptionSettings(settings.displaySettings, settings.multiToggle, this.controlElementGroups); - console.log('optionSettings', this.optionSettings); + this.controlMask = new ControlMask(settings.type, settings.displaySettings, settings.multiToggle, this.controlElementGroups); + return this.controlMask; + } + + public onSelectValueChange(settingGroup, changedSetting) { + this.selection[settingGroup][changedSetting.label] = changedSetting.value; + this.selectionEvent.emit(this.selection); + console.log('selections set: ', this.selection); + } + + public createNewSearchMask() { + return new ControlMask('genericSearch', true, + true, + [new ControlElementGroup( + '', + true, + '' + )]); + } + + public createNewControlElementGroup(label: string, + pannelOpenState = true, + description: string, + controlElements?: Array, + condition?: Array) { + } } -export class OptionSettings { +export class ControlMask { + type: string; displaySettings: boolean; multiToggle: boolean; controlElementGroups: Array; + description?: string; - constructor(displaySettings: boolean, multiToggle: boolean, controlElementGroups: Array ) { + constructor(type: string, displaySettings = true, multiToggle = true, controlElementGroups: Array, description?: string) { + this.type = type; this.displaySettings = displaySettings; this.multiToggle = multiToggle; this.controlElementGroups = controlElementGroups; + this.description = description; } } export class ControlElementGroup { label: string; pannelOpenState: boolean; description: string; controlElements: Array; condition?: Array; + toolTip?: string; constructor( label: string, - pannelOpenState: boolean, + pannelOpenState = true, description: string, - controlElements: Array, - condition?: Array) { + controlElements?: Array, + condition?: Array, + toolTip?: string ) { this.label = label; this.pannelOpenState = pannelOpenState; this.description = description; this.controlElements = controlElements; this.condition = condition; + this.toolTip = toolTip; } } export class ControlElement { label: string; description: string; type: string; value: any; + condition: Array; options?: Array; - genericOptions?: Array; + genericOptionSet?: GenericOptionSet; + toolTip?: string; constructor( label: string, description: string, type: string, value: any, + condition?: Array, options?: Array, - genericOptions?: Array) { + genericOptionSet?: GenericOptionSet, + toolTip?: string) { this.label = label; this.description = description; this.type = type; this.value = value; + this.condition = condition; this.options = options; - this.genericOptions = genericOptions; + this.genericOptionSet = genericOptionSet; + this.toolTip = toolTip; } } export class DisplayCondition { group: string; control: string; values: Array; } export class ControlElementOption { id: string; description: string; constructor(id: string, description: string) { this.description = description; this.id = id; } } +export class GenericOptionSet { + queryString?: string; + queryObject?: any; + cotrolOptions?: Array; + + constructor(id: string, description: string, queryString?: string, queryObject?: string, cotrolOptions?: Array) { + this.queryObject = queryObject; + this.queryString = queryString; + this.cotrolOptions = cotrolOptions; // the actual queryresults which are shown as options in a select or checkbox f.e. + } +} + export class GenericOptionsData { iri: string; + rdfsType: string; // the class of the thing stringValue: string; xsdType: string; - constructor(iri: string, stringValue: string, xsdType: string) { + constructor(iri: string, rdfsType: string, stringValue: string, xsdType: string) { this.iri = iri; + this.rdfsType = rdfsType; this.stringValue = stringValue; this.xsdType = xsdType; } } export class SelectionBundle { [prop: string]: SelectionGroup; } export class SelectionGroup { [prop: string]: any; } diff --git a/src/app/home.component.html b/src/app/home.component.html new file mode 100644 index 0000000..3cc7c7e --- /dev/null +++ b/src/app/home.component.html @@ -0,0 +1,3 @@ + + Start + diff --git a/src/app/home.component.ts b/src/app/home.component.ts new file mode 100644 index 0000000..6de0037 --- /dev/null +++ b/src/app/home.component.ts @@ -0,0 +1,13 @@ +import { Component } from '@angular/core'; + + +@Component({ + selector: 'app-home-component', + templateUrl: './home.component.html', + styleUrls: ['./app.component.css'] +}) +export class HomeComponent { + +} + + diff --git a/src/app/main-menu-component/main-menu-component.component.html b/src/app/main-menu-component/main-menu-component.component.html new file mode 100644 index 0000000..9dac3c2 --- /dev/null +++ b/src/app/main-menu-component/main-menu-component.component.html @@ -0,0 +1,13 @@ + +
+ + + + + + +
+
+ diff --git a/src/app/main-menu-component/main-menu-component.component.scss b/src/app/main-menu-component/main-menu-component.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/src/app/main-menu-component/main-menu-component.component.ts b/src/app/main-menu-component/main-menu-component.component.ts new file mode 100644 index 0000000..149824a --- /dev/null +++ b/src/app/main-menu-component/main-menu-component.component.ts @@ -0,0 +1,16 @@ +import { Component, OnInit } from '@angular/core'; +import {MatButtonModule} from '@angular/material/button'; + +@Component({ + selector: 'app-main-menu-component', + templateUrl: './main-menu-component.component.html', + styleUrls: ['./main-menu-component.component.scss'] +}) +export class MainMenuComponentComponent implements OnInit { + + constructor() { } + + ngOnInit() { + } + +} diff --git a/src/app/manuscript-view-component/manuscript-view-component.component.html b/src/app/manuscript-view-component/manuscript-view-component.component.html new file mode 100644 index 0000000..cc7abcc --- /dev/null +++ b/src/app/manuscript-view-component/manuscript-view-component.component.html @@ -0,0 +1,3 @@ + + + diff --git a/src/app/manuscript-view-component/manuscript-view-component.component.scss b/src/app/manuscript-view-component/manuscript-view-component.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/src/app/manuscript-view-component/manuscript-view-component.component.spec.ts b/src/app/manuscript-view-component/manuscript-view-component.component.spec.ts new file mode 100644 index 0000000..4404e8d --- /dev/null +++ b/src/app/manuscript-view-component/manuscript-view-component.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ManuscriptViewComponentComponent } from './manuscript-view-component.component'; + +describe('ManuscriptViewComponentComponent', () => { + let component: ManuscriptViewComponentComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ ManuscriptViewComponentComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ManuscriptViewComponentComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/manuscript-view-component/manuscript-view-component.component.ts b/src/app/manuscript-view-component/manuscript-view-component.component.ts new file mode 100644 index 0000000..0e4a3f9 --- /dev/null +++ b/src/app/manuscript-view-component/manuscript-view-component.component.ts @@ -0,0 +1,16 @@ +import { Component, OnInit } from '@angular/core'; +import {FormControl} from '@angular/forms'; + +@Component({ + selector: 'app-manuscript-view-component', + templateUrl: './manuscript-view-component.component.html', + styleUrls: ['./manuscript-view-component.component.scss'] +}) +export class ManuscriptViewComponentComponent implements OnInit { + mode = new FormControl('over'); // push, side + constructor() { } + + ngOnInit() { + } + +} diff --git a/src/app/markup-text-component/markup-text-component.component.ts b/src/app/markup-text-component/markup-text-component.component.ts index 867bfa3..a86977e 100644 --- a/src/app/markup-text-component/markup-text-component.component.ts +++ b/src/app/markup-text-component/markup-text-component.component.ts @@ -1,205 +1,205 @@ import {Component, Input, OnChanges} from '@angular/core'; -import {GenericOptionsService, SelectionBundle} from '../generic-control-elements-component/generic-control-elements-service.service'; +import {GenericControlElementsService, SelectionBundle} from '../generic-control-elements-component/generic-control-elements-service.service'; import markupDefaultSettings from './markup-text-settings.json'; @Component({ selector: 'app-markup-text-component', templateUrl: './markup-text-component.component.html', styleUrls: ['./markup-text-component.component.scss'] // TODO: implement dynamic css from files/assets/server or somewhere }) /** TODO: change documentation * Simple and leightweight component marking up/styling a text input textToStyle with standoff markupData. * @param StandoffData: markup data. * @param textToStyle: the word for which the markup information should be applied as styles in the template. * @param startIndices: the startindices of all final text segments to style * @param styleSegments: the resulting style segments with their css styles which are then applied by ngStyle in the template */ export class MarkupTextComponentComponent implements OnChanges { @Input() standoffData: Array>; // [[rdfs:type, startIndex:number, endIndex:number, cssStyle:string], [...], ...] @Input() textToStyle: string; @Input() inputSettings: string; // the actual settings actualSettings?: SelectionBundle; // in case there is no settings input defaultSettings = JSON.parse(JSON.stringify(markupDefaultSettings)); // Options on how to render renderSemanticAnnotations: boolean; renderStyleMarkup: boolean; markupData: Array>; startIndices: Array = []; // the start indices of all style segments templateSegments: Array = []; // the resulting style segments with their css styles - constructor( private optionService: GenericOptionsService) { + constructor( private optionService: GenericControlElementsService) { if (!this.inputSettings ) { this.actualSettings = this.optionService.getSelectionFromJson(this.defaultSettings); console.log('No markup settings passed by input. Loading default settings'); console.log('Settings: ', this.actualSettings); } else { this.actualSettings = this.optionService.getSelectionFromJson(this.inputSettings); console.log('markup settings loaded by input.'); console.log('Settings: ', this.actualSettings); } } ngOnChanges() { // subscribing to OptionSettings changes this.optionService.selectionEvent.subscribe(newSettings => { this.actualSettings = newSettings; this.setRenderType(newSettings.markup.renderMarkup); }); // TODO: go for data stream as an observable/promise if (this.standoffData && this.standoffData.length > 0 ) { this.markupData = this.standoffData; } console.log('this.markupData:', this.markupData); // initial settings for markup rendering this.setRenderType(this.actualSettings.markup.renderMarkup); this.startIndices = this.getStartIndices(this.markupData); this.createSegments(); } /** * Creates the final text segments and styles (this.templateSegments) which will * be *ngFored as and styled with *ngStyle in the template. * Iterates through all startindices defined, pushes the corresponding substring * of our text to style together with its matching styles to this.templateSegments * */ private createSegments() { this.templateSegments = []; // needs reset, let c = 1; // counter for getting the end index of the substring() via this.startIndices[c] for (const startIndex of this.startIndices) { const textSegment = this.textToStyle.substring(startIndex, this.startIndices[c]); const markupDef = this.getMarkupDef(startIndex); if (markupDef) { this.templateSegments.push([markupDef[0], textSegment, markupDef[1], markupDef[2]]); } c += 1; } console.log('this.templateSegments: ', this.templateSegments); } /** * Gets the styles that apply for a passed start index. Hence the styles of a segment * apply to every character within that segment equally, we only need to get the styles * for the startindex, so all those styles apply to the whole segment correctly. * Iterates through all defined styles/ranges ranges of this.markupData. * For every range covering the startIndex, its style is added as property to the styles object. * * @param startIndex: The startindex of a new range to style * @return styles: An object with all matching css styles as properties */ private getMarkupDef(startIndex) { const markup = {}; const resource = []; // more than one linked resource possible at overlaps let markuptype = ''; for (const entry of this.markupData) { // if our startIndex is within a given range, the style of that range will be added to styles. if (entry[1] <= startIndex && startIndex <= entry[2]) { if (entry[0] === 'stoff:CssStyle' && this.renderStyleMarkup) { if (markuptype === '') { // markuptype = CssStyle only if not yet set. CssStyle should not overwrite Semanticannotation. markuptype = entry[0].split(':')[1]; } // Adds css property/value as an object: Splits the style string at ':', // removes leading and ending spaces, deletes ";" and assigns it as an object as css property:"value" markup[entry[3].split(':')[0].trim()] = entry[3].split(':')[1].trim().replace(';', ''); } if (entry[0] === 'stoff:SemanticAnnotation' && this.renderSemanticAnnotations) { markuptype = entry[0].split(':')[1]; // SemanticAnnotation // adds css class markup[(entry[3].trim().replace(':', ' ' ).toLowerCase())] = true; resource.push( { linkText: this.textToStyle.substring(entry[1], entry[2] + 1), link: entry[4], cssClass: entry[3].trim().replace(':', ' ' ).toLowerCase(), oClass: entry[3].trim().split(':')[1] }); // links } } } if (Object.keys(markup).length === 0 && markup.constructor === Object) { // If there is no range applying, e.g. no style at all defined for a startindex we simply define font-style: normal. return ['CssStyle', {'font-style': 'normal'}, '']; } else { return [markuptype, markup, resource ]; } } /** * Creates an array of startindices defining the final text/style segments. * Hence every end index can be described as a startindex minus one (an end * index equals a start index of sth. new -1), end indices are also covered implicitly. * So every endIndex +1 is also pushed to the startIndices array if not yet existing. * If a defined range is ending at the very last character, there is obviously not a new * style starting and an additive startIndex is wrong. Therefore the last startIndex * is simply popped from the array if its value exceeds textToStyle.length. * * @return startIndices: Array of all distinct startIndices of the final text segments. */ private getStartIndices(standoffData) { const startIndices: Array = []; // Push every startindex to startIndices if not yet there (distinct). // Hence every endIndex is itself equals a (startIndex -1), we simply add also every endIndex +1 to // the Array of startIndices if there is not yet a start defined for that character. standoffData.forEach(range => { if (startIndices.indexOf(range[1]) === -1) { startIndices.push(range[1]); } if (startIndices.indexOf(range[2] + 1) === -1) { startIndices.push(range[2] + 1); } }); // if there is no startindex of 0, we have to add it to define the first text segment. if (startIndices.indexOf(0) === -1) { startIndices.push(0); } // sort it startIndices.sort((n1, n2) => n1 - n2); // If the last endIndex is the very last character of the string to style, we must not generate a startIndex at endIndex+1. // In any other case we have to start a new style. // If the last generated startIndex is bigger than this.textToStyle.length // we have to pop that last startIndex (from the right) from our startIndices. // If the textToStyle.length is bigger or equals the last startIndex, that last startIndex simply means the end of the style // one character before. In these cases a last style range with font style undefined (style:normal) has to start, // so the last end ist also defined. if (this.textToStyle.length < startIndices[startIndices.length - 1]) { console.log('popping last startIndex: ', startIndices[startIndices.length - 1]); startIndices.pop(); } return startIndices; } // runs some action // TODO: Add actions private getHRef(action, target) { console.log('action = ', action); if (action === 'external') { return target; } } private getToolTip(segment) { let toolTip = ''; for (const thing of segment) { if (toolTip === '') { toolTip = thing.linkText + ': ' + thing.link + ' a ' + thing.cssClass.split(' ')[0] + ':' + thing.cssClass.split(' ')[1]; } else { toolTip = toolTip + '; ' + thing.linkText + ': ' + thing.link + ' a ' + thing.cssClass.split(' ')[0] + ':' + thing.cssClass.split(' ')[1]; } } return toolTip; } public setRenderType(renderMarkup: string) { if (renderMarkup === 'style') { this.renderSemanticAnnotations = false; this.renderStyleMarkup = true; } if (renderMarkup === 'semanticAnnotation') { this.renderSemanticAnnotations = true; this.renderStyleMarkup = false; } if (renderMarkup === 'all') { this.renderSemanticAnnotations = true; this.renderStyleMarkup = true; } // run segmentation again with new renderSettings this.createSegments(); } } diff --git a/src/app/markup-text-component/markup-text-settings.json b/src/app/markup-text-component/markup-text-settings.json index 18d239c..c103de5 100644 --- a/src/app/markup-text-component/markup-text-settings.json +++ b/src/app/markup-text-component/markup-text-settings.json @@ -1,152 +1,153 @@ -{ "displaySettings": true, +{ "type": "staticOptions", + "displaySettings": true, "multiToggle": true, "controlElementGroups":[ { "label": "markup", "pannelOpenState": true, "description": "Markup options", "controlElements": [ { "label":"renderMarkup", "description":"render markup", "type":"radio", "options":[ { "id":"semanticAnnotation", "description":"semantic annotations only" }, { "id":"style", "description":"styles markup only" }, { "id":"all", "description":"both combined" } ], "value":"semanticAnnotation" } ] }, { "label": "styles", "pannelOpenState": true, "description": "Styles (non semantic markup only)", "controlElements": [ { "label":"style", "description":"render styles", "value":true, "type":"checkbox" } ] }, { "label":"semanticMarkup", "pannelOpenState": true, "condition": [ {"group": "markup", "option":"renderMarkup", "values": ["all", "semanticAnnotation"] } ], "description":"Semantic annotations", "controlElements":[ { "label":"styleType", "description":"render semantic annotations as", "options":[ { "id":"button", "description":"buttons" }, { "id":"icon", "description":"icons" }, { "id":"hyperlink", "description":"text with hyperlinks" }, { "id":"markup", "description":"styled text/markup only" }, { "id":"none", "description":"plane unstyled text" } ], "value":"icon", "type":"select" }, { "label":"renderIcons", "description":"render semantic overlaps", "options":[ { "id":"split", "description":"split up in multiple icons" }, { "id":"group", "description":"group polysemantics in one icon" } ], "value":"after", "type":"radio", "condition": [ {"group": "semanticMarkup", "option": "styleType", "values": ["icon"]} ] }, { "label":"position", "description":"position them", "options":[ { "id":"before", "description":"before the text" }, { "id":"after", "description":"after the text" } ], "value":"after", "type":"radio", "condition": [ {"group": "semanticMarkup", "option": "styleType", "values": ["icon"]} ] }, { "label":"actions", "description":"action to take place on click at a semantic annotation", "options":[ { "id":"external", "description":"open external link" }, { "id":"pannel", "description":"display resource in pannel" }, { "id":"menu", "description":"open menu" } ], "value":"menu", "type":"select", "condition": [{ "group": "semanticMarkup", "option": "styleType", "values": ["icon", "button", "hyperlink"]} ] }, { "label":"tooltip", "description":"show tooltip", "value":true, "type":"checkbox" } ] } ] } diff --git a/src/app/services/query.service.ts b/src/app/services/query.service.ts index dd10da0..e8777fb 100644 --- a/src/app/services/query.service.ts +++ b/src/app/services/query.service.ts @@ -1,133 +1,133 @@ import { Injectable } from '@angular/core'; import { Parser, Generator, Wildcard } from 'sparqljs'; import {HttpClient, HttpHeaders} from '@angular/common/http'; @Injectable() export class QueryService { constructor(private http: HttpClient) { } parser = new Parser(); sparqlGenerator = new Generator({}); // prefixes and namespaces for shrinking iri's to the enduser // TODO: move namespaces to a file and read it in namespaces = { data: 'http://rdfh.ch/projects/0068#', owl: 'http://www.w3.org/2002/07/owl#', rdfs: 'http://www.w3.org/1999/02/22-rdf-syntax-ns#', tln: 'http://www.nie.org/ontology/nietzsche#', }; - baseUrl = 'http://localhost:3030/nietzsche-rw/query'; + baseUrl = 'http://fuseki.nie-ine.ch/nietzsche-rw/query'; /** * Gets the data from an endpoint via http post * * @param query: The query to run. * @param queryType: "CONSTRUCT" or "QUERY" * @returns the response. */ public getData(query: string, queryType?: string ) { let httpOptions; if (queryType === 'CONSTRUCT') { // A construct does contain a text as response, not a json, so responseType must be 'text' to avoid parse errors httpOptions = { headers: new HttpHeaders({'Content-Type': 'application/sparql-query', 'Accept': 'text/turtle'}), responseType: 'text'}; return this.http.post(this.baseUrl, query, httpOptions); } else { httpOptions = { headers: new HttpHeaders({ 'Content-Type': 'application/sparql-query', 'Accept': 'application/sparql-results+json; charset=UTF-8'})}; } if (this.baseUrl && httpOptions.headers && (query === undefined || query === null || query === '') ) { console.log('fallback to static query as there is no query passed'); this.getQueryfromFilename('fallbackQuery.rq' ).subscribe(fallbackQuery => { console.log('fallbackQuery: ', fallbackQuery); return this.http.post(this.baseUrl, query, httpOptions); }); } else { return this.http.post(this.baseUrl, query, httpOptions); } } /** * Shrinks an iri according to the defined prefixes/namespaces. * * @param iri The iri to be shrunken. * @returns shrunkIri: the shrunken iri if it can be shrunken. */ public shrink_iri(iri) { let shrunkIri: string; Object.keys(this.namespaces).forEach((ns, index) => { // console.log(this.namespaces[ns] + ' key ' + ns ) if (iri.includes(this.namespaces[ns])) { shrunkIri = iri.replace(this.namespaces[ns], ns + ':' ); } }); if (shrunkIri) { return shrunkIri; } else {return iri; } } /** * Gets all the properties and connected resources of one thing. Gets it depending on its type or role: * if the thing is a property, it gets all connected ?s and ?o; * if it's usage as subject is of interest, it gets all ?p and ?o; * if it's usage as object is of interest, it gets all ?s ?p; * * @param iri: The iri of the selected resource * @param resourceType: the type of usage i.e. as ?s, ?p or ?o * @returns the query for the resource. */ public getQueryforResourceData(iri: string, resourceType?: string) { const parsedQuery = this.parser.parse('SELECT ?s ?p ?o WHERE { ?s ?p ?o }'); // reset the subject iri to the word's iri we like to query for const resource = {'termType': 'NamedNode', 'value': decodeURI(iri) }; switch (resourceType) { case 'subject': { parsedQuery.where[0].triples[0].subject = resource; break; } case 'predicate': { parsedQuery.where[0].triples[0].predicate = resource; break; } case 'object': { parsedQuery.where[0].triples[0].object = resource; break; } } // generate the new query string and return it return this.sparqlGenerator.stringify(parsedQuery); } /** * Gets a text file by its name from the directory assets/queries. * * @param filename The name of the file + file name extension. * @returns the text of the file. */ public getQueryfromFilename(filename) { return this.http.get('../assets/queries/' + filename, {responseType: 'text'}); } /** * Gets a query string from a given file in the directory assets/queries. * * @param filename The name of the file + file name extension. * @returns The JSON equivalence of the parsed query. */ public parseQueryFromFile(filename) { return this.getQueryfromFilename(filename ) .subscribe(query => { this.parser.parse(query); } ); } private setWhereIri(query, index, iri) { if (query.queryType === 'CONSTRUCT') { query.where[index].patterns[0].triples[0].subject.id = iri; } else { query = ''; } return query; } } diff --git a/src/app/sidenav-component/sidenav-component.component.html b/src/app/sidenav-component/sidenav-component.component.html new file mode 100644 index 0000000..54f9fcb --- /dev/null +++ b/src/app/sidenav-component/sidenav-component.component.html @@ -0,0 +1,22 @@ + + +
+

Here will be the

+
  • manuscript 1
  • +
  • manuscript 2
  • +
  • manuscript 3
  • +
  • manuscript 4
  • +
  • manuscript 5
  • +
    +
    + +
    +
    +
    +

    MANUSCRIPT 1

    +
    manuscript description
    +
    +
    +
    diff --git a/src/app/sidenav-component/sidenav-component.component.scss b/src/app/sidenav-component/sidenav-component.component.scss new file mode 100644 index 0000000..79d966a --- /dev/null +++ b/src/app/sidenav-component/sidenav-component.component.scss @@ -0,0 +1,57 @@ +@import "../../styles.scss"; + +// main container for all +#sidenav-container { + width: 100%; + height: 100%; + min-height: 300px; +} + +// the navigation drawer on the very left containing the items to choose +#sidenav-drawer { + padding: 10px; + width: 25%; +} + +// the content on the right containing button-container and the text +#sidenav-content { + min-height: 300px; + width: 100%; + height: 100%; + margin: 0; + padding: 0; +} + +.content-area { + min-height: 300px; + height: 100%; + display:table; +} + +.button-container { + height:100%; + vertical-align: middle; + width:10%; + display: table-cell; +} + +#toggle-button { // fills sidenav-button-container completely, so all is one button + height: 100px; + min-width: unset; // needed because angular sets an own min width!? + width: 15px; + display: flex; + justify-content: center; + align-items: center; + margin: 0; + padding: 0; +} + +.text-container { + height:100%; + vertical-align: top; + align-content: left; + width:100%; + display: table-cell; + padding: 10px; +} + diff --git a/src/app/sidenav-component/sidenav-component.component.spec.ts b/src/app/sidenav-component/sidenav-component.component.spec.ts new file mode 100644 index 0000000..db06114 --- /dev/null +++ b/src/app/sidenav-component/sidenav-component.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { SidenavComponentComponent } from './sidenav-component.component'; + +describe('SidenavComponentComponent', () => { + let component: SidenavComponentComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ SidenavComponentComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(SidenavComponentComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/sidenav-component/sidenav-component.component.ts b/src/app/sidenav-component/sidenav-component.component.ts new file mode 100644 index 0000000..56c67bf --- /dev/null +++ b/src/app/sidenav-component/sidenav-component.component.ts @@ -0,0 +1,21 @@ +import { Component, OnInit } from '@angular/core'; + +@Component({ + selector: 'app-sidenav-component', + templateUrl: './sidenav-component.component.html', + styleUrls: ['./sidenav-component.component.scss'] +}) +export class SidenavComponentComponent implements OnInit { + //TODO: Set this as a property in Manuscript/Page/Rhizome & as a optionall param + openState = true; + + constructor() { } + + ngOnInit() { + } + + toggleDrawer() { + this.openState = !this.openState; + } + +} diff --git a/src/app/text-view-component/text-view-settings.json b/src/app/text-view-component/text-view-settings.json new file mode 100644 index 0000000..5a94446 --- /dev/null +++ b/src/app/text-view-component/text-view-settings.json @@ -0,0 +1,41 @@ +{ "type": "staticOptions", + "displaySettings": true, + "multiToggle": true, + "controlElementGroups":[ + { + "label": "display", + "pannelOpenState": false, + "description": "Optionen", + "controlElements": [ + { + "label":"displayMode", + "description":"Ansicht", + "toolTip": "Wechselt die Ansicht der Texteinheit.", + "type":"select", + "options":[ + { + "id":"faksimile", + "description":"Nur Faksimile anzeigen" + }, + { + "id":"transcription", + "description":"Nur Transkription anzeigen" + }, + { + "id":"synopsis", + "description":"Synopse (Faksimile und Transkription)" + } + ], + "value":"synopsis" + }, + { + "label":"doublePage", + "description":"Doppelseite des Manuskripts anzeigen", + "toolTip:": "Zeigt das Faksimile als Doppelseite (recto & verso).", + "value":true, + "type":"checkbox" + } + ] + } + ] +} diff --git a/src/app/textfield-component/textfield.component.html b/src/app/textfield-component/textfield.component.html index 043c9d3..e2349bd 100644 --- a/src/app/textfield-component/textfield.component.html +++ b/src/app/textfield-component/textfield.component.html @@ -1,51 +1,50 @@ -
    {{svgToShow}}
    {{word.text}} {{word.text}} {{word.text}} diff --git a/src/assets/queries/manuscripts.rq b/src/assets/queries/manuscripts.rq new file mode 100644 index 0000000..f4052cc --- /dev/null +++ b/src/assets/queries/manuscripts.rq @@ -0,0 +1,12 @@ +PREFIX data: +PREFIX tln: + +SELECT ?manuscript ?title ?type ?descr ?earlier1 ?earlierauthor ?earlierCitation +WHERE { + ?manuscript a . + +OPTIONAL { ?manuscript tln:hasTitle ?title} +OPTIONAL { ?manuscript tln:hasManuscriptType ?type} +OPTIONAL { ?manuscript tln:hasDescription ?d1 . + ?d1 tln:textHasContent ?descr .} + } diff --git a/src/styles.scss b/src/styles.scss index 863d0cd..0ba298c 100644 --- a/src/styles.scss +++ b/src/styles.scss @@ -1,37 +1,45 @@ // Custom Theming for Angular Material // For more information: https://material.angular.io/guide/theming @import '~@angular/material/theming'; // Plus imports for other components in your app. // Include the common styles for Angular Material. We include this here so that you only // have to load a single css file for Angular Material in your app. // Be sure that you only ever include this mixin once! @include mat-core(); -// Define the palettes for your theme using the Material Design palettes available in palette.scss -// (imported above). For each palette, you can optionally specify a default, lighter, and darker -// hue. Available color palettes: https://material.io/design/color/ -$svg-test-app-primary: mat-palette($mat-indigo); -$svg-test-app-accent: mat-palette($mat-pink, A200, A100, A400); - -// The warn palette is optional (defaults to red). -$svg-test-app-warn: mat-palette($mat-red); - -// Create the theme object (a Sass map containing all of the palettes). -$svg-test-app-theme: mat-light-theme($svg-test-app-primary, $svg-test-app-accent, $svg-test-app-warn); - +// importing the themes from our theme files +@import "./themes/standard-theme"; +@import "./themes/green-theme"; // Include theme styles for core and each component used in your app. // Alternatively, you can import and @include the theme mixins for each component // that you are using. -@include angular-material-theme($svg-test-app-theme); + +// for own components, i.e. non material component we like to use the same style/palettes + + +.standard-theme { + @include angular-material-theme($standard-theme); + + h1 { color: mat-color($standard-theme-primary)} +} +.green-theme { + @include angular-material-theme($green-theme); + + h1 { color: mat-color($green-theme-primary)} +} + /* You can add global styles to this file, and also import other style files */ + html, body { height: 100%; } body { margin: 0; font-family: Roboto, "Helvetica Neue", sans-serif; } .flex-spacer { flex: 1 1 auto; } + + diff --git a/src/themes/green-theme.scss b/src/themes/green-theme.scss new file mode 100644 index 0000000..f61ea17 --- /dev/null +++ b/src/themes/green-theme.scss @@ -0,0 +1,11 @@ +// Define the palettes for your theme using the Material Design palettes available in palette.scss +// (imported above). For each palette, you can optionally specify a default, lighter, and darker +// hue. Available color palettes: https://material.io/design/color/ +$green-theme-primary: mat-palette($mat-light-green); +$green-theme-accent: mat-palette($mat-green); + +// The warn palette is optional. +$green-theme-warn: mat-palette($mat-green); + +// Create the theme object (a Sass map containing all of the palettes). +$green-theme: mat-dark-theme($green-theme-primary, $green-theme-accent, $green-theme-warn); diff --git a/src/themes/standard-theme.scss b/src/themes/standard-theme.scss new file mode 100644 index 0000000..5d39f8e --- /dev/null +++ b/src/themes/standard-theme.scss @@ -0,0 +1,11 @@ +// Define the palettes for your theme using the Material Design palettes available in palette.scss +// (imported above). For each palette, you can optionally specify a default, lighter, and darker +// hue. Available color palettes: https://material.io/design/color/ +$standard-theme-primary: mat-palette($mat-blue-gray); +$standard-theme-accent: mat-palette($mat-blue, A200, A100, A400); + +// The warn palette is optional (defaults to red). +$standard-theme-warn: mat-palette($mat-red); + +// Create the theme object (a Sass map containing all of the palettes). +$standard-theme: mat-light-theme($standard-theme-primary, $standard-theme-accent, $standard-theme-warn);