Build script, CurrentDocumentService, MultiLanguage, Svg logo

This commit is contained in:
Andrea Cavalli 2019-04-14 22:46:07 +02:00
parent a83cb71d4f
commit b9a6d10b15
35 changed files with 704 additions and 106 deletions

View File

@ -11,36 +11,57 @@ const argument0 = process.argv[2];
async function main(runMode) {
const angularSourceBuffer = fs.readFileSync("buildconfig.json");
const angularSource = JSON.parse(angularSourceBuffer);
const generalConfiguration = angularSource.angular.projects[angularSource.projectName].architect.build.options;
angularSource.angular.projects[angularSource.projectName].architect.build.options = {};
const productionConfiguration = angularSource.angular.projects[angularSource.projectName].architect.build.configurations["production"];
delete angularSource.angular.projects[angularSource.projectName].architect.build.configurations["production"];
angularSource.languages.forEach((language, languageIndex) => {
const defaultIndex = angularSource.angular.projects[angularSource.projectName].architect.build.options.index;
const defaultIndex = generalConfiguration.index;
const defaultIndexName = defaultIndex.split(".").slice(0, -1).join(".");
const defaultIndexExtension = defaultIndex.split(".").pop();
const languageSpecificIndex = defaultIndexName + "." + language + ".generated." + defaultIndexExtension;
const languageConfiguration = {
"index": languageSpecificIndex,
...angularSource.angular.projects[angularSource.projectName].architect.build.configurations[language]
};
delete angularSource.angular.projects[angularSource.projectName].architect.build.configurations[language];
angularSource.angular.projects[angularSource.projectName].architect.build.configurations["production_" + language] = {
...generalConfiguration,
...productionConfiguration,
...languageConfiguration
};
angularSource.angular.projects[angularSource.projectName].architect.build.configurations["production_" + language].fileReplacements = [
{
"replace": defaultIndex,
"with": languageSpecificIndex
},
...angularSource.angular.projects[angularSource.projectName].architect.build.configurations["production_" + language].fileReplacements
];
resolvePaths(angularSource.angular.projects[angularSource.projectName].architect.build.configurations["production_" + language], "assets", language);
angularSource.angular.projects[angularSource.projectName].architect.build.configurations["serve_" + language] = {
"aot": true,
...generalConfiguration,
...languageConfiguration,
"aot": true,
};
if (angularSource.angular.projects[angularSource.projectName].architect.build.configurations["serve_" + language].fileReplacements === undefined) {
angularSource.angular.projects[angularSource.projectName].architect.build.configurations["serve_" + language].fileReplacements = [];
}
angularSource.angular.projects[angularSource.projectName].architect.build.configurations["serve_" + language].fileReplacements.push({
"replace": defaultIndex,
"with": languageSpecificIndex
});
angularSource.angular.projects[angularSource.projectName].architect.serve.configurations[language] = {
"browserTarget": "cavallium-website:build:serve_"+language,
"port": 4200 + languageIndex
};
resolvePaths(angularSource.angular.projects[angularSource.projectName].architect.build.configurations["serve_" + language], "assets", language);
// Create index.language.html
let indexText = fs.readFileSync(defaultIndex).toString("utf8");
indexText = indexText.replace("<html>", "<html lang=\"" + language + "\">");
indexText = indexText.replace("generate=\"language_attribute\"", "lang=\"" + language + "\"");
fs.writeFileSync(languageSpecificIndex, indexText);
});
@ -60,7 +81,14 @@ async function main(runMode) {
case "serve":
await Promise.all(angularSource.languages.map((language, index) => {
console.log("Building for language " + JSON.stringify(language) + (index > 0 ? " (hidden)" : ""));
const childProcess = spawn("ng", ["serve", "--configuration=" + language, "--host=0.0.0.0"],
const arguments = ["serve", "--configuration=" + language];
if (angularSource.localIpAddress) {
arguments.push("--host=" + angularSource.localIpAddress);
}
if (angularSource.disableHostCheck) {
arguments.push("--disable-host-check");
}
const childProcess = spawn("ng", arguments,
{ stdio: [process.stdin, index == 0 ? process.stdout : null, process.stderr] });
return onExit(childProcess);
}));
@ -85,4 +113,14 @@ function onExit(childProcess) {
reject(err);
});
});
}
function resolvePaths(obj, elem, language) {
if (obj !== undefined && obj[elem] !== undefined && Array.isArray(obj[elem])) {
obj[elem] = obj[elem].map((asset) => typeof asset === "string" ? asset.replace("_GENERATED_LANGUAGE_", language) : {
"glob": asset.glob.replace("_GENERATED_LANGUAGE_", language),
"input": asset.input.replace("_GENERATED_LANGUAGE_", language),
"output": asset.output.replace("_GENERATED_LANGUAGE_", language)
});
}
}

View File

@ -2,6 +2,8 @@
"version": 1,
"projectName": "cavallium-website",
"languages": ["it", "en"],
"localIpAddress": "0.0.0.0",
"disableHostCheck": true,
"angular": {
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
@ -28,7 +30,8 @@
"tsConfig": "src/tsconfig.app.json",
"assets": [
"src/favicon.ico",
"src/assets"
"src/assets",
"src/documents"
],
"styles": [
"src/styles.scss",
@ -86,7 +89,7 @@
"browserTarget": "cavallium-website:build:production"
},
"en": {
"browserTarget": "my-project:build:it"
"browserTarget": "my-project:build:en"
},
"it": {
"browserTarget": "my-project:build:it"
@ -113,7 +116,8 @@
"scripts": [],
"assets": [
"src/favicon.ico",
"src/assets"
"src/assets",
"src/documents"
]
}
},

95
package-lock.json generated
View File

@ -673,6 +673,11 @@
"@types/jasmine": "*"
}
},
"@types/marked": {
"version": "0.6.5",
"resolved": "https://registry.npmjs.org/@types/marked/-/marked-0.6.5.tgz",
"integrity": "sha512-6kBKf64aVfx93UJrcyEZ+OBM5nGv4RLsI6sR1Ar34bpgvGVRoyTgpxn4ZmtxOM5aDTAaaznYuYUH8bUX3Nk3YA=="
},
"@types/node": {
"version": "8.9.5",
"resolved": "https://registry.npmjs.org/@types/node/-/node-8.9.5.tgz",
@ -2028,6 +2033,17 @@
"integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=",
"dev": true
},
"clipboard": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/clipboard/-/clipboard-2.0.4.tgz",
"integrity": "sha512-Vw26VSLRpJfBofiVaFb/I8PVfdI1OxKcYShe6fm0sP/DtmiWQNCjhM/okTvdCo0G+lMMm1rMYbk4IK4x1X+kgQ==",
"optional": true,
"requires": {
"good-listener": "^1.2.2",
"select": "^1.1.2",
"tiny-emitter": "^2.0.0"
}
},
"cliui": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz",
@ -2661,6 +2677,12 @@
"integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=",
"dev": true
},
"delegate": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/delegate/-/delegate-3.2.0.tgz",
"integrity": "sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw==",
"optional": true
},
"delegates": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
@ -3624,14 +3646,12 @@
"balanced-match": {
"version": "1.0.0",
"bundled": true,
"dev": true,
"optional": true
"dev": true
},
"brace-expansion": {
"version": "1.1.11",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
@ -3646,20 +3666,17 @@
"code-point-at": {
"version": "1.1.0",
"bundled": true,
"dev": true,
"optional": true
"dev": true
},
"concat-map": {
"version": "0.0.1",
"bundled": true,
"dev": true,
"optional": true
"dev": true
},
"console-control-strings": {
"version": "1.1.0",
"bundled": true,
"dev": true,
"optional": true
"dev": true
},
"core-util-is": {
"version": "1.0.2",
@ -3776,8 +3793,7 @@
"inherits": {
"version": "2.0.3",
"bundled": true,
"dev": true,
"optional": true
"dev": true
},
"ini": {
"version": "1.3.5",
@ -3789,7 +3805,6 @@
"version": "1.0.0",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"number-is-nan": "^1.0.0"
}
@ -3804,7 +3819,6 @@
"version": "3.0.4",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"brace-expansion": "^1.1.7"
}
@ -3812,14 +3826,12 @@
"minimist": {
"version": "0.0.8",
"bundled": true,
"dev": true,
"optional": true
"dev": true
},
"minipass": {
"version": "2.3.5",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"safe-buffer": "^5.1.2",
"yallist": "^3.0.0"
@ -3838,7 +3850,6 @@
"version": "0.5.1",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"minimist": "0.0.8"
}
@ -3919,8 +3930,7 @@
"number-is-nan": {
"version": "1.0.1",
"bundled": true,
"dev": true,
"optional": true
"dev": true
},
"object-assign": {
"version": "4.1.1",
@ -3932,7 +3942,6 @@
"version": "1.4.0",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"wrappy": "1"
}
@ -4054,7 +4063,6 @@
"version": "1.0.2",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"code-point-at": "^1.0.0",
"is-fullwidth-code-point": "^1.0.0",
@ -4270,6 +4278,15 @@
"minimatch": "~3.0.2"
}
},
"good-listener": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/good-listener/-/good-listener-1.2.2.tgz",
"integrity": "sha1-1TswzfkxPf+33JoNR3CWqm0UXFA=",
"optional": true,
"requires": {
"delegate": "^3.1.2"
}
},
"graceful-fs": {
"version": "4.1.15",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz",
@ -5944,6 +5961,11 @@
"object-visit": "^1.0.0"
}
},
"marked": {
"version": "0.6.2",
"resolved": "https://registry.npmjs.org/marked/-/marked-0.6.2.tgz",
"integrity": "sha512-LqxwVH3P/rqKX4EKGz7+c2G9r98WeM/SW34ybhgNGhUQNKtf1GmmSkJ6cDGJ/t6tiyae49qRkpyTw2B9HOrgUA=="
},
"md5.js": {
"version": "1.3.5",
"resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz",
@ -6291,6 +6313,17 @@
"integrity": "sha512-MFh0d/Wa7vkKO3Y3LlacqAEeHK0mckVqzDieUKTT+KGxi+zIpeVsFxymkIiRpbpDziHc290Xr9A1O4Om7otoRA==",
"dev": true
},
"ngx-markdown": {
"version": "7.1.5",
"resolved": "https://registry.npmjs.org/ngx-markdown/-/ngx-markdown-7.1.5.tgz",
"integrity": "sha512-boL1eQkdHHwVQuLsRF4eaIxuBx2PoJidO/YStLU2a0u6Q7VCjaZI09oSPgsAmsDjJ/Hu7ZWGPv475H7BGJ8iCw==",
"requires": {
"@types/marked": "^0.6.0",
"marked": "^0.6.0",
"prismjs": "^1.16.0",
"tslib": "^1.9.0"
}
},
"nice-try": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",
@ -7225,6 +7258,14 @@
"integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==",
"dev": true
},
"prismjs": {
"version": "1.16.0",
"resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.16.0.tgz",
"integrity": "sha512-OA4MKxjFZHSvZcisLGe14THYsug/nF6O1f0pAJc0KN0wTyAcLqmsbE+lTGKSpyh+9pEW57+k6pg2AfYR+coyHA==",
"requires": {
"clipboard": "^2.0.0"
}
},
"process": {
"version": "0.11.10",
"resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
@ -8008,6 +8049,12 @@
}
}
},
"select": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/select/-/select-1.1.2.tgz",
"integrity": "sha1-DnNQrN7ICxEIUoeG7B1EGNEbOW0=",
"optional": true
},
"select-hose": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz",
@ -9235,6 +9282,12 @@
"setimmediate": "^1.0.4"
}
},
"tiny-emitter": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz",
"integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==",
"optional": true
},
"tmp": {
"version": "0.0.33",
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",

View File

@ -22,6 +22,7 @@
"@angular/platform-browser-dynamic": "~7.2.0",
"@angular/router": "~7.2.0",
"core-js": "^2.5.4",
"ngx-markdown": "^7.1.5",
"rxjs": "~6.3.3",
"tslib": "^1.9.0",
"zone.js": "~0.8.26"

View File

@ -4,6 +4,10 @@ import { ArticleComponent } from "./article/article.component";
import { RouterEmptyComponent } from "./gui/router-empty/router-empty.component";
const routes: Routes = [
{
path: "",
component: ArticleComponent
},
{
path: "article",
component: RouterEmptyComponent,

View File

@ -1,8 +1,3 @@
<nav>
<app-navbar></app-navbar>
</nav>
<h1>
Welcome to {{ title }}!
</h1>
<p i18n="@@introductionHeader">Welcome to</p>
<app-big-logo></app-big-logo>
<app-navbar #navbar></app-navbar>
<router-outlet></router-outlet>

View File

@ -0,0 +1,4 @@
:host {
display: block;
overflow-y: auto;
}

View File

@ -1,4 +1,5 @@
import { Component } from "@angular/core";
import { Component, HostListener, ViewChild } from "@angular/core";
import { NavbarComponent } from "./gui/navbar/navbar.component";
@Component({
selector: "app-root",
@ -6,5 +7,12 @@ import { Component } from "@angular/core";
styleUrls: ["./app.component.scss"]
})
export class AppComponent {
title = "cavallium-website";
title = "cavallium-website";
@ViewChild("navbar") navbar: NavbarComponent;
@HostListener("scroll", ["$event"])
handleScroll(event: Event) {
this.navbar.onParentScroll(event);
}
}

View File

@ -8,21 +8,54 @@ import { NavbarComponent } from "./gui/navbar/navbar.component";
import { FooterComponent } from "./gui/footer/footer.component";
import { ArticleComponent } from "./article/article.component";
import { RouterEmptyComponent } from "./gui/router-empty/router-empty.component";
import {HttpClientModule} from "@angular/common/http";
import { MarkdownModule, MarkedOptions, MarkedRenderer, MarkdownComponent } from "ngx-markdown";
import { BigLogoComponent } from './gui/big-logo/big-logo.component';
@NgModule({
declarations: [
AppComponent,
HomeComponent,
NavbarComponent,
FooterComponent,
ArticleComponent,
RouterEmptyComponent
],
imports: [
BrowserModule,
AppRoutingModule
],
providers: [],
bootstrap: [AppComponent]
declarations: [
AppComponent,
HomeComponent,
NavbarComponent,
FooterComponent,
ArticleComponent,
RouterEmptyComponent,
BigLogoComponent
],
imports: [
BrowserModule,
AppRoutingModule,
HttpClientModule,
MarkdownModule.forRoot({
markedOptions: {
provide: MarkedOptions,
useFactory: markedOptionsFactory,
},
}),
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
export function markedOptionsFactory(): MarkedOptions {
const renderer = new MarkedRenderer();
renderer.blockquote = (text: string) => {
return "<blockquote class=\"blockquote\"><p>" + text + "</p></blockquote>";
};
/*renderer.warning = (text: string) => {
return "<span style=\"color:red\"" + text + "</span>";
};*/
return {
renderer,
gfm: true,
tables: true,
breaks: false,
pedantic: false,
sanitize: false,
smartLists: true,
smartypants: false,
};
}

View File

@ -1,3 +1,6 @@
<p>
article works!
</p>
<!--
<p style="color: darkgray; margin-bottom: -1rem; font-size: 0.8rem">
Path: <i><u>{{documentData?.id}}</u></i>
</p>
-->
<markdown [data]="documentData?.content"></markdown>

View File

@ -0,0 +1,6 @@
:host {
display: block;
overflow-x: hidden;
word-break: break-word;
padding: 0 0.5rem;
}

View File

@ -1,19 +1,40 @@
import { Component, OnInit } from "@angular/core";
import { Component, OnInit, OnDestroy } from "@angular/core";
import { ActivatedRoute, UrlSegment } from "@angular/router";
import { map } from "rxjs/operators";
import { DocumentFetchService } from "../services/document-fetch.service";
import { DocumentData } from "../symbols/DocumentData";
import { MarkdownService } from "ngx-markdown";
import { CurrentDocumentService } from "../services/current-document.service";
@Component({
selector: "app-article",
templateUrl: "./article.component.html",
styleUrls: ["./article.component.scss"]
})
export class ArticleComponent implements OnInit {
export class ArticleComponent implements OnInit, OnDestroy {
constructor(private activatedRoute: ActivatedRoute) { }
public documentData: DocumentData;
constructor(
private activatedRoute: ActivatedRoute,
private documentFetcher: DocumentFetchService,
private markdownService: MarkdownService,
private currentDocumentService: CurrentDocumentService
) { }
ngOnInit() {
this.activatedRoute.data.subscribe(console.log);
this.activatedRoute.params.subscribe(console.log);
this.activatedRoute.url.subscribe((url: UrlSegment[]) => console.log(url.map((urlSegment) => urlSegment.path).join("/")));
this.activatedRoute.url
.pipe(map((urlSegments: UrlSegment[]) => urlSegments.map(urlSegment => urlSegment.path).join("/")))
.subscribe(async (url: string) => {
const docData: DocumentData = await this.documentFetcher.fetch(url);
this.documentData = docData;
this.currentDocumentService.setCurrentDocument(docData);
});
}
ngOnDestroy() {
this.currentDocumentService.setCurrentDocument(null);
}
}

View File

@ -0,0 +1,4 @@
<a routerLink="/">
<img src="/assets/drone.min.svg" loading="lazy" alt="logo">
<h1>Cavallium.it</h1>
</a>

View File

@ -0,0 +1,48 @@
@import "../../../styles-variables.scss";
:host {
display: block;
background-color: $color-main;
color: white;
text-align: center;
position: relative;
user-select: none;
z-index: 1002;
overflow: hidden;
}
:host:after, :host:before {
content: "";
display: flex;
clear: both;
}
a {
color: inherit;
text-decoration: none;
display: flex;
justify-content: center;
align-items: center;
margin: -0.5rem;
padding-top: 3rem;
padding-bottom: 2.5rem;
font-size: 1.5rem;
}
h1 {
margin: 0;
}
a > * {
margin: 0.5rem;
}
img {
height: 5rem;
}
@media screen and (max-width: $mobile-mode-size) {
a {
flex-direction: column;
padding-top: 1rem;
padding-bottom: 0.5rem;
font-size: 1rem;
}
img {
height: 4rem;
}
}

View File

@ -0,0 +1,25 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { BigLogoComponent } from './big-logo.component';
describe('BigLogoComponent', () => {
let component: BigLogoComponent;
let fixture: ComponentFixture<BigLogoComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ BigLogoComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(BigLogoComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,15 @@
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-big-logo',
templateUrl: './big-logo.component.html',
styleUrls: ['./big-logo.component.scss']
})
export class BigLogoComponent implements OnInit {
constructor() { }
ngOnInit() {
}
}

View File

@ -1,18 +1,22 @@
<div class="big-logo">
<h1>Big Logo</h1>
</div>
<div class="header-logo-large">
</div>
<div class="header-logo">
</div>
<div class="header-nav">
<ul>
<li *ngFor="let link of navigationLinks">
<a *ngIf="link.external === true" [href]="link.address" [target]="link.newtab === undefined || link.newtab === true ? '_blank' : '_self'">{{link.text}}</a>
<a *ngIf="link.external !== true" [routerLink]="link.address" [target]="link.newtab === true ? '_blank' : '_self'">{{link.text}}</a>
</li>
</ul>
<div class="shadow-container" [class.sticked]="stickedOnTop" [class.overflow]="overflowLogo" [class.overflow-max]="overflowMax">
<a #navlogo class="header-logo" routerLink="/" [class.visible]="stickedOnTop" [class.overflow]="overflowLogo"><strong>Cavallium.it</strong></a>
<nav class="header-nav" #navbuttons [class.overflow]="overflowLogo" [class.overflow-max]="overflowMax">
<ul>
<li *ngFor="let link of navigationLinks">
<a *ngIf="link.external === true"
[href]="link.address"
[target]="link.newtab === undefined || link.newtab === true ? '_blank' : '_self'"
[class.forcefocus] = "(currentDocument.onDocumentChange() | async)?.id == link.address"
><span>{{link.text}}</span></a>
<a
*ngIf="link.external !== true"
[routerLink]="link.address"
[target]="link.newtab === true ? '_blank' : '_self'"
[class.force-focus] = "'/article/' + (currentDocument.onDocumentChange() | async)?.id == link.address"
><span>{{link.text}}</span></a>
</li>
</ul>
</nav>
<div class="header-logo-space" [class.overflow]="overflowLogo"></div>
<div class="overflow-max-indicator" [class.visible]="overflowMax">&gt;</div>
</div>

View File

@ -0,0 +1,123 @@
@import "../../../styles-variables";
:host {
position: sticky;
top: 0;
overflow: initial;
display: block;
background-color: $color-main;
color: white;
z-index: 1001;
}
.shadow-container {
overflow-x: hidden;
display: flex;
box-shadow: 0 -2rem 2rem 2rem transparent;
transition: box-shadow ease-in-out 0.2s;
}
.shadow-container.overflow {
overflow-x: scroll;
-ms-overflow-style: none; // IE 10+
scrollbar-width: none; // Firefox
justify-content: center;
}
.shadow-container.overflow.overflow-max {
justify-content: flex-start;
}
.shadow-container.overflow::-webkit-scrollbar {
display: none; // Safari and Chrome
}
.shadow-container.sticked {
box-shadow: 0 -2rem 2rem 2rem #0000006e;
}
:host:after, :host:before {
content: "";
display: flex;
clear: both;
}
.header-logo, .header-logo-space {
padding: 1rem 0rem;
text-align: right;
vertical-align: middle;
flex: 0 0 6.2rem;
opacity: 0;
pointer-events: none;
user-select: none;
position: relative;
top: -5rem;
color: inherit;
text-decoration: none;
transition: opacity ease-in-out 0.4s, top ease-in-out 0s 0.4s;
}
.header-logo.visible {
opacity: 1;
pointer-events: initial;
font-size: initial;
top: 0;
transition: opacity ease-in-out 0.3s, top ease-in-out 0.3s;
}
.header-logo-space {
flex: 0 1 6rem;
}
.header-logo.overflow, .header-logo-space.overflow {
position: fixed;
top: 0;
visibility: hidden;
pointer-events: none;
}
.header-nav {
flex: 1 0 auto;
}
.header-nav.overflow {
flex: 0 0 auto;
}
.header-nav.overflow-max {
padding-right: 1.5rem;
}
.header-nav a {
color: inherit;
text-decoration: none;
flex: 1 1 100%;
padding: 0 0.5rem;
}
.header-nav ul {
list-style: none;
padding-inline-start: 0;
margin: 0;
display: flex;
justify-content: center;
}
.header-nav li {
flex: 0 0 auto;
text-align: center;
display: flex;
}
.header-nav span {
display: block;
padding: 1rem 0.2rem;
border-bottom: 0rem solid transparent;
padding-bottom: 1rem;
transition: border-bottom-color ease-in-out 0.2s, border-bottom-width ease-in-out 0.2s, padding-bottom ease-in-out 0.2s;
}
.header-nav li:hover span, .header-nav li .force-focus span {
border-bottom-color: white;
border-bottom-width: 0.2rem;
border-bottom-color: white;
padding-bottom: 0.8rem;
}
.overflow-max-indicator {
position: absolute;
right: 0;
top: 0;
bottom: 0;
opacity: 0;
pointer-events: none;
padding: 1rem 0;
font-weight: 800;
width: 2rem;
text-align: right;
padding-right: 0.25rem;
box-shadow: -2rem 0 0.5rem -0.5rem $color-main inset;
}
.overflow-max-indicator.visible {
opacity: 1;
}

View File

@ -1,38 +1,84 @@
import { Component, OnInit } from "@angular/core";
import { Component, OnInit, HostListener, Input, ElementRef, ViewChild, AfterViewInit } from "@angular/core";
import { NavigationLink } from "src/app/symbols/NavigationLink";
import { ActivatedRoute, UrlSegment } from "@angular/router";
import { map } from "rxjs/operators";
import { CurrentDocumentService } from "src/app/services/current-document.service";
@Component({
selector: "app-navbar",
templateUrl: "./navbar.component.html",
styleUrls: ["./navbar.component.scss"]
})
export class NavbarComponent implements OnInit {
export class NavbarComponent implements OnInit, AfterViewInit {
public navigationLinks: NavigationLink[] = [
{
text: "Software",
text: "Cardboard bug",
address: "/article/software"
},
{
text: "Midi23D",
address: "/article/midi23d"
address: "/article/software/midi23d"
},
{
text: "Calculator",
address: "/article/calculator",
newtab: true
newtab: false
},
{
text: "Github",
address: "https://github.com/Cavallium/WarpPI",
external: true,
newtab: false
newtab: true
},
];
stickedOnTop = false;
overflowLogo = false;
overflowMax = false;
@ViewChild("navlogo") navLogo: ElementRef<HTMLElement>;
@ViewChild("navbuttons") navButtons: ElementRef<HTMLElement>;
constructor() { }
constructor(private elRef: ElementRef<HTMLElement>, public currentDocument: CurrentDocumentService) { }
ngOnInit() {
// TODO: fare la parte dei percorsi preselezionati
}
ngAfterViewInit() {
setTimeout(() => {
this.updateSticked();
this.updateSize();
});
}
@Input()
public onParentScroll(event: Event) {
this.updateSticked();
}
private updateSticked() {
this.stickedOnTop = this.elRef.nativeElement.getBoundingClientRect().top <= 0;
}
private updateSize() {
if (this.overflowLogo === false
&& (this.navButtons.nativeElement.offsetWidth + this.navLogo.nativeElement.offsetWidth) > this.elRef.nativeElement.offsetWidth) {
this.overflowLogo = true;
} else if (this.overflowLogo === true
&& this.navButtons.nativeElement.offsetWidth + this.navLogo.nativeElement.offsetWidth < this.elRef.nativeElement.offsetWidth - 30) {
this.overflowLogo = false;
}
if (this.overflowMax === false
&& this.navButtons.nativeElement.offsetWidth > this.elRef.nativeElement.offsetWidth) {
this.overflowMax = true;
} else if (this.overflowMax === true
&& this.navButtons.nativeElement.offsetWidth < this.elRef.nativeElement.offsetWidth) {
this.overflowMax = false;
}
}
@HostListener("window:resize", ["$event"])
handleResize(event: Event) {
this.updateSize();
}
}

View File

@ -0,0 +1,12 @@
import { TestBed } from '@angular/core/testing';
import { CurrentDocumentService } from './current-document.service';
describe('CurrentDocumentService', () => {
beforeEach(() => TestBed.configureTestingModule({}));
it('should be created', () => {
const service: CurrentDocumentService = TestBed.get(CurrentDocumentService);
expect(service).toBeTruthy();
});
});

View File

@ -0,0 +1,21 @@
import { Injectable } from "@angular/core";
import { Observable, BehaviorSubject } from "rxjs";
import { DocumentData } from "../symbols/DocumentData";
@Injectable({
providedIn: "root"
})
export class CurrentDocumentService {
private documentSubject = new BehaviorSubject(null);
constructor() { }
public onDocumentChange(): Observable<DocumentData> {
return this.documentSubject.asObservable();
}
public setCurrentDocument(data: DocumentData) {
this.documentSubject.next(data);
}
}

View File

@ -1,44 +1,64 @@
import { Injectable } from "@angular/core";
import { Observable } from "rxjs";
import { DocumentData } from "../symbols/DocumentData";
import { HttpClient, Request, Response } from "selenium-webdriver/http";
import { encodeUriSegment } from "@angular/router/src/url_tree";
import { HttpClient } from "@angular/common/http";
import { take } from "rxjs/operators";
@Injectable({
providedIn: "root"
})
export class DocumentFetchService {
private language: string;
constructor(private http: HttpClient) {}
constructor(private http: HttpClient) {
this.language = document.body.parentElement.lang;
}
public async fetch(unsafeId: string): Promise<DocumentData> {
const encodedId = this.encodeId(unsafeId);
const response: Response = await this.http.send(new Request("GET", "/documents/" + encodedId + ".md"));
if (response.status === 200) {
return {
found: true,
id: encodedId,
content: await response.body
};
try {
return await this.fetchWithLanguage(encodedId, this.language);
} catch (e) {
try {
return await this.fetchWithLanguage(encodedId, "en");
} catch (e) {
return {
found: false,
id: encodedId,
content: await this.fetchErrorContent(404)
};
}
}
}
private async fetchWithLanguage(unsafeId: string, language: string): Promise<DocumentData> {
const encodedId = this.encodeId(unsafeId);
const response: string = await this.http.get("/documents/" + encodedId + "." + language + ".md", { responseType: "text" })
.pipe(take(1)).toPromise();
return {
found: false,
found: true,
id: encodedId,
content: await this.fetchErrorContent(404)
content: await response
};
}
public async fetchErrorContent(errorCode: number): Promise<string> {
if (errorCode > 0 && errorCode <= 700) {
const response: Response = await this.http.send(new Request("GET", "/documents/" + errorCode + ".md"));
if (response.status === 200) {
return await response.body;
try {
const response: string = await this.http.get("/documents/" + errorCode + "." + this.language + ".md", { responseType: "text" })
.pipe(take(1)).toPromise();
return response;
} catch (e) {
}
}
return "Error " + errorCode + ".";
}
private encodeId(id: string): string {
return id.split("/").map(encodeUriSegment).filter((part) => part !== "." && part !== "..").join("/");
const encoded = id.split("/").map(encodeURIComponent).filter((part) => part.length > 0 && part !== "." && part !== "..").join("/");
if (encoded.length > 0) {
return encoded;
} else {
return "index";
}
}
}

1
src/assets/drone.min.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 12 KiB

3
src/documents/404.en.md Normal file
View File

@ -0,0 +1,3 @@
# 404 Error
## Page not found

3
src/documents/404.it.md Normal file
View File

@ -0,0 +1,3 @@
# Errore 404
## Pagina non trovata

View File

@ -0,0 +1,5 @@
# Welcome to Cavallium.it
This page is a test.
**Bold.** *Cantami o diva del pelide achille l'ira funesta che infiniti addusse lutti agli achei.*

View File

@ -0,0 +1,5 @@
# Benvenuto in Cavallium.it
Questa pagina è una prova.
**Prova in grassetto.** *Cantami o diva del pelide achille l'ira funesta che infiniti addusse lutti agli achei.*

View File

@ -0,0 +1,29 @@
# LG G2 Cardboard fix
## Fixing the double vision on your LG G2 using Cardboard
### When this problem occurs
If you have installed a custom rom on your LG G2 probably the DPI setting has changed.
The DPI setting is used to calculate the pixel density, and consequently the real measures of your screen. Google Cardboard calculates the distance between eyes using the DPI setting, and if it's wrong some apps using the old CardBoard SDK don't show properly.
#### Here's an example
With 480 DPI (CyanogenMod default setting)<br>
*same length in inches:* 4
With 424 DPI (LG G2 real DPI)<br>
*same length in inches:* 3
### How to fix it
Please choose the method that you prefer
#### Method 1
***Fixing the problem for <u>some</u> apps, keeping your current DPI setting.***
1. Open Cardboard app
2. Tap **Settings**
3. Tap **Change**
4. Scan with your camera this QR-Code:<br>[warning]WARNING: If you do this you can't go back to the default settings![warning]

View File

@ -0,0 +1,55 @@
# LG G2 Cardboard fix
## Fixing the double vision on your LG G2 using Cardboard
### When this problem occurs
If you have installed a custom rom on your LG G2 probably the DPI setting has changed.
The DPI setting is used to calculate the pixel density, and consequently the real measures of your screen. Google Cardboard calculates the distance between eyes using the DPI setting, and if it's wrong some apps using the old CardBoard SDK don't show properly.
#### Here's an example
With 480 DPI (CyanogenMod default setting)<br>
*same length in inches:* 4
With 424 DPI (LG G2 real DPI)<br>
*same length in inches:* 3
### How to fix it
Please choose the method that you prefer
#### Method 1
***Fixing the problem for <u>some</u> apps, keeping your current DPI setting.***
1. Open Cardboard app
2. Tap **Settings**
3. Tap **Change**
4. Scan with your camera this QR-Code:<br>[warning]WARNING: If you do this you can't go back to the default settings![warning]
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>

View File

@ -0,0 +1,3 @@
# Midi23D
This is a test page

View File

@ -1,3 +1,3 @@
export const environment = {
production: true
production: true
};

View File

@ -3,7 +3,7 @@
// The list of file replacements can be found in `angular.json`.
export const environment = {
production: false
production: false
};
/*

View File

@ -1,8 +1,8 @@
<!doctype html>
<html>
<html generate="language_attribute">
<head>
<meta charset="utf-8">
<title>CavalliumWebsite</title>
<title>Cavallium.it</title>
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1">

View File

@ -0,0 +1,2 @@
$color-main: #3F51B5;
$mobile-mode-size: 425px;

View File

@ -1,4 +1,8 @@
/* You can add global styles to this file, and also import other style files */
html, body, body, app-root {
height: 100%;
max-height: 100%;
}
body {
margin: 0;
font-family: "Muli-custom", -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;