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) { async function main(runMode) {
const angularSourceBuffer = fs.readFileSync("buildconfig.json"); const angularSourceBuffer = fs.readFileSync("buildconfig.json");
const angularSource = JSON.parse(angularSourceBuffer); 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"]; const productionConfiguration = angularSource.angular.projects[angularSource.projectName].architect.build.configurations["production"];
delete angularSource.angular.projects[angularSource.projectName].architect.build.configurations["production"]; delete angularSource.angular.projects[angularSource.projectName].architect.build.configurations["production"];
angularSource.languages.forEach((language, languageIndex) => { 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 defaultIndexName = defaultIndex.split(".").slice(0, -1).join(".");
const defaultIndexExtension = defaultIndex.split(".").pop(); const defaultIndexExtension = defaultIndex.split(".").pop();
const languageSpecificIndex = defaultIndexName + "." + language + ".generated." + defaultIndexExtension; const languageSpecificIndex = defaultIndexName + "." + language + ".generated." + defaultIndexExtension;
const languageConfiguration = { const languageConfiguration = {
"index": languageSpecificIndex,
...angularSource.angular.projects[angularSource.projectName].architect.build.configurations[language] ...angularSource.angular.projects[angularSource.projectName].architect.build.configurations[language]
}; };
delete 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] = { angularSource.angular.projects[angularSource.projectName].architect.build.configurations["production_" + language] = {
...generalConfiguration,
...productionConfiguration, ...productionConfiguration,
...languageConfiguration ...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] = { angularSource.angular.projects[angularSource.projectName].architect.build.configurations["serve_" + language] = {
"aot": true, ...generalConfiguration,
...languageConfiguration, ...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] = { angularSource.angular.projects[angularSource.projectName].architect.serve.configurations[language] = {
"browserTarget": "cavallium-website:build:serve_"+language, "browserTarget": "cavallium-website:build:serve_"+language,
"port": 4200 + languageIndex "port": 4200 + languageIndex
}; };
resolvePaths(angularSource.angular.projects[angularSource.projectName].architect.build.configurations["serve_" + language], "assets", language);
// Create index.language.html // Create index.language.html
let indexText = fs.readFileSync(defaultIndex).toString("utf8"); 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); fs.writeFileSync(languageSpecificIndex, indexText);
}); });
@ -60,7 +81,14 @@ async function main(runMode) {
case "serve": case "serve":
await Promise.all(angularSource.languages.map((language, index) => { await Promise.all(angularSource.languages.map((language, index) => {
console.log("Building for language " + JSON.stringify(language) + (index > 0 ? " (hidden)" : "")); 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] }); { stdio: [process.stdin, index == 0 ? process.stdout : null, process.stderr] });
return onExit(childProcess); return onExit(childProcess);
})); }));
@ -85,4 +113,14 @@ function onExit(childProcess) {
reject(err); 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, "version": 1,
"projectName": "cavallium-website", "projectName": "cavallium-website",
"languages": ["it", "en"], "languages": ["it", "en"],
"localIpAddress": "0.0.0.0",
"disableHostCheck": true,
"angular": { "angular": {
"$schema": "./node_modules/@angular/cli/lib/config/schema.json", "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1, "version": 1,
@ -28,7 +30,8 @@
"tsConfig": "src/tsconfig.app.json", "tsConfig": "src/tsconfig.app.json",
"assets": [ "assets": [
"src/favicon.ico", "src/favicon.ico",
"src/assets" "src/assets",
"src/documents"
], ],
"styles": [ "styles": [
"src/styles.scss", "src/styles.scss",
@ -86,7 +89,7 @@
"browserTarget": "cavallium-website:build:production" "browserTarget": "cavallium-website:build:production"
}, },
"en": { "en": {
"browserTarget": "my-project:build:it" "browserTarget": "my-project:build:en"
}, },
"it": { "it": {
"browserTarget": "my-project:build:it" "browserTarget": "my-project:build:it"
@ -113,7 +116,8 @@
"scripts": [], "scripts": [],
"assets": [ "assets": [
"src/favicon.ico", "src/favicon.ico",
"src/assets" "src/assets",
"src/documents"
] ]
} }
}, },

95
package-lock.json generated
View File

@ -673,6 +673,11 @@
"@types/jasmine": "*" "@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": { "@types/node": {
"version": "8.9.5", "version": "8.9.5",
"resolved": "https://registry.npmjs.org/@types/node/-/node-8.9.5.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-8.9.5.tgz",
@ -2028,6 +2033,17 @@
"integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=", "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=",
"dev": true "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": { "cliui": {
"version": "3.2.0", "version": "3.2.0",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz",
@ -2661,6 +2677,12 @@
"integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=",
"dev": true "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": { "delegates": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
@ -3624,14 +3646,12 @@
"balanced-match": { "balanced-match": {
"version": "1.0.0", "version": "1.0.0",
"bundled": true, "bundled": true,
"dev": true, "dev": true
"optional": true
}, },
"brace-expansion": { "brace-expansion": {
"version": "1.1.11", "version": "1.1.11",
"bundled": true, "bundled": true,
"dev": true, "dev": true,
"optional": true,
"requires": { "requires": {
"balanced-match": "^1.0.0", "balanced-match": "^1.0.0",
"concat-map": "0.0.1" "concat-map": "0.0.1"
@ -3646,20 +3666,17 @@
"code-point-at": { "code-point-at": {
"version": "1.1.0", "version": "1.1.0",
"bundled": true, "bundled": true,
"dev": true, "dev": true
"optional": true
}, },
"concat-map": { "concat-map": {
"version": "0.0.1", "version": "0.0.1",
"bundled": true, "bundled": true,
"dev": true, "dev": true
"optional": true
}, },
"console-control-strings": { "console-control-strings": {
"version": "1.1.0", "version": "1.1.0",
"bundled": true, "bundled": true,
"dev": true, "dev": true
"optional": true
}, },
"core-util-is": { "core-util-is": {
"version": "1.0.2", "version": "1.0.2",
@ -3776,8 +3793,7 @@
"inherits": { "inherits": {
"version": "2.0.3", "version": "2.0.3",
"bundled": true, "bundled": true,
"dev": true, "dev": true
"optional": true
}, },
"ini": { "ini": {
"version": "1.3.5", "version": "1.3.5",
@ -3789,7 +3805,6 @@
"version": "1.0.0", "version": "1.0.0",
"bundled": true, "bundled": true,
"dev": true, "dev": true,
"optional": true,
"requires": { "requires": {
"number-is-nan": "^1.0.0" "number-is-nan": "^1.0.0"
} }
@ -3804,7 +3819,6 @@
"version": "3.0.4", "version": "3.0.4",
"bundled": true, "bundled": true,
"dev": true, "dev": true,
"optional": true,
"requires": { "requires": {
"brace-expansion": "^1.1.7" "brace-expansion": "^1.1.7"
} }
@ -3812,14 +3826,12 @@
"minimist": { "minimist": {
"version": "0.0.8", "version": "0.0.8",
"bundled": true, "bundled": true,
"dev": true, "dev": true
"optional": true
}, },
"minipass": { "minipass": {
"version": "2.3.5", "version": "2.3.5",
"bundled": true, "bundled": true,
"dev": true, "dev": true,
"optional": true,
"requires": { "requires": {
"safe-buffer": "^5.1.2", "safe-buffer": "^5.1.2",
"yallist": "^3.0.0" "yallist": "^3.0.0"
@ -3838,7 +3850,6 @@
"version": "0.5.1", "version": "0.5.1",
"bundled": true, "bundled": true,
"dev": true, "dev": true,
"optional": true,
"requires": { "requires": {
"minimist": "0.0.8" "minimist": "0.0.8"
} }
@ -3919,8 +3930,7 @@
"number-is-nan": { "number-is-nan": {
"version": "1.0.1", "version": "1.0.1",
"bundled": true, "bundled": true,
"dev": true, "dev": true
"optional": true
}, },
"object-assign": { "object-assign": {
"version": "4.1.1", "version": "4.1.1",
@ -3932,7 +3942,6 @@
"version": "1.4.0", "version": "1.4.0",
"bundled": true, "bundled": true,
"dev": true, "dev": true,
"optional": true,
"requires": { "requires": {
"wrappy": "1" "wrappy": "1"
} }
@ -4054,7 +4063,6 @@
"version": "1.0.2", "version": "1.0.2",
"bundled": true, "bundled": true,
"dev": true, "dev": true,
"optional": true,
"requires": { "requires": {
"code-point-at": "^1.0.0", "code-point-at": "^1.0.0",
"is-fullwidth-code-point": "^1.0.0", "is-fullwidth-code-point": "^1.0.0",
@ -4270,6 +4278,15 @@
"minimatch": "~3.0.2" "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": { "graceful-fs": {
"version": "4.1.15", "version": "4.1.15",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz",
@ -5944,6 +5961,11 @@
"object-visit": "^1.0.0" "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": { "md5.js": {
"version": "1.3.5", "version": "1.3.5",
"resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz",
@ -6291,6 +6313,17 @@
"integrity": "sha512-MFh0d/Wa7vkKO3Y3LlacqAEeHK0mckVqzDieUKTT+KGxi+zIpeVsFxymkIiRpbpDziHc290Xr9A1O4Om7otoRA==", "integrity": "sha512-MFh0d/Wa7vkKO3Y3LlacqAEeHK0mckVqzDieUKTT+KGxi+zIpeVsFxymkIiRpbpDziHc290Xr9A1O4Om7otoRA==",
"dev": true "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": { "nice-try": {
"version": "1.0.5", "version": "1.0.5",
"resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",
@ -7225,6 +7258,14 @@
"integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==",
"dev": true "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": { "process": {
"version": "0.11.10", "version": "0.11.10",
"resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", "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": { "select-hose": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz",
@ -9235,6 +9282,12 @@
"setimmediate": "^1.0.4" "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": { "tmp": {
"version": "0.0.33", "version": "0.0.33",
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", "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/platform-browser-dynamic": "~7.2.0",
"@angular/router": "~7.2.0", "@angular/router": "~7.2.0",
"core-js": "^2.5.4", "core-js": "^2.5.4",
"ngx-markdown": "^7.1.5",
"rxjs": "~6.3.3", "rxjs": "~6.3.3",
"tslib": "^1.9.0", "tslib": "^1.9.0",
"zone.js": "~0.8.26" "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"; import { RouterEmptyComponent } from "./gui/router-empty/router-empty.component";
const routes: Routes = [ const routes: Routes = [
{
path: "",
component: ArticleComponent
},
{ {
path: "article", path: "article",
component: RouterEmptyComponent, component: RouterEmptyComponent,

View File

@ -1,8 +1,3 @@
<nav> <app-big-logo></app-big-logo>
<app-navbar></app-navbar> <app-navbar #navbar></app-navbar>
</nav>
<h1>
Welcome to {{ title }}!
</h1>
<p i18n="@@introductionHeader">Welcome to</p>
<router-outlet></router-outlet> <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({ @Component({
selector: "app-root", selector: "app-root",
@ -6,5 +7,12 @@ import { Component } from "@angular/core";
styleUrls: ["./app.component.scss"] styleUrls: ["./app.component.scss"]
}) })
export class AppComponent { 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 { FooterComponent } from "./gui/footer/footer.component";
import { ArticleComponent } from "./article/article.component"; import { ArticleComponent } from "./article/article.component";
import { RouterEmptyComponent } from "./gui/router-empty/router-empty.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({ @NgModule({
declarations: [ declarations: [
AppComponent, AppComponent,
HomeComponent, HomeComponent,
NavbarComponent, NavbarComponent,
FooterComponent, FooterComponent,
ArticleComponent, ArticleComponent,
RouterEmptyComponent RouterEmptyComponent,
], BigLogoComponent
imports: [ ],
BrowserModule, imports: [
AppRoutingModule BrowserModule,
], AppRoutingModule,
providers: [], HttpClientModule,
bootstrap: [AppComponent] MarkdownModule.forRoot({
markedOptions: {
provide: MarkedOptions,
useFactory: markedOptionsFactory,
},
}),
],
providers: [],
bootstrap: [AppComponent]
}) })
export class AppModule { } 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 style="color: darkgray; margin-bottom: -1rem; font-size: 0.8rem">
</p> 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 { 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({ @Component({
selector: "app-article", selector: "app-article",
templateUrl: "./article.component.html", templateUrl: "./article.component.html",
styleUrls: ["./article.component.scss"] 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() { ngOnInit() {
this.activatedRoute.data.subscribe(console.log); this.activatedRoute.url
this.activatedRoute.params.subscribe(console.log); .pipe(map((urlSegments: UrlSegment[]) => urlSegments.map(urlSegment => urlSegment.path).join("/")))
this.activatedRoute.url.subscribe((url: UrlSegment[]) => console.log(url.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"> <div class="shadow-container" [class.sticked]="stickedOnTop" [class.overflow]="overflowLogo" [class.overflow-max]="overflowMax">
<h1>Big Logo</h1> <a #navlogo class="header-logo" routerLink="/" [class.visible]="stickedOnTop" [class.overflow]="overflowLogo"><strong>Cavallium.it</strong></a>
</div> <nav class="header-nav" #navbuttons [class.overflow]="overflowLogo" [class.overflow-max]="overflowMax">
<ul>
<div class="header-logo-large"> <li *ngFor="let link of navigationLinks">
<a *ngIf="link.external === true"
</div> [href]="link.address"
<div class="header-logo"> [target]="link.newtab === undefined || link.newtab === true ? '_blank' : '_self'"
[class.forcefocus] = "(currentDocument.onDocumentChange() | async)?.id == link.address"
</div> ><span>{{link.text}}</span></a>
<div class="header-nav"> <a
<ul> *ngIf="link.external !== true"
<li *ngFor="let link of navigationLinks"> [routerLink]="link.address"
<a *ngIf="link.external === true" [href]="link.address" [target]="link.newtab === undefined || link.newtab === true ? '_blank' : '_self'">{{link.text}}</a> [target]="link.newtab === true ? '_blank' : '_self'"
<a *ngIf="link.external !== true" [routerLink]="link.address" [target]="link.newtab === true ? '_blank' : '_self'">{{link.text}}</a> [class.force-focus] = "'/article/' + (currentDocument.onDocumentChange() | async)?.id == link.address"
</li> ><span>{{link.text}}</span></a>
</ul> </li>
</ul>
</nav>
<div class="header-logo-space" [class.overflow]="overflowLogo"></div>
<div class="overflow-max-indicator" [class.visible]="overflowMax">&gt;</div>
</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 { 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({ @Component({
selector: "app-navbar", selector: "app-navbar",
templateUrl: "./navbar.component.html", templateUrl: "./navbar.component.html",
styleUrls: ["./navbar.component.scss"] styleUrls: ["./navbar.component.scss"]
}) })
export class NavbarComponent implements OnInit { export class NavbarComponent implements OnInit, AfterViewInit {
public navigationLinks: NavigationLink[] = [ public navigationLinks: NavigationLink[] = [
{ {
text: "Software", text: "Cardboard bug",
address: "/article/software" address: "/article/software"
}, },
{ {
text: "Midi23D", text: "Midi23D",
address: "/article/midi23d" address: "/article/software/midi23d"
}, },
{ {
text: "Calculator", text: "Calculator",
address: "/article/calculator", address: "/article/calculator",
newtab: true newtab: false
}, },
{ {
text: "Github", text: "Github",
address: "https://github.com/Cavallium/WarpPI", address: "https://github.com/Cavallium/WarpPI",
external: true, 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() { 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 { Injectable } from "@angular/core";
import { Observable } from "rxjs";
import { DocumentData } from "../symbols/DocumentData"; import { DocumentData } from "../symbols/DocumentData";
import { HttpClient, Request, Response } from "selenium-webdriver/http"; import { HttpClient } from "@angular/common/http";
import { encodeUriSegment } from "@angular/router/src/url_tree"; import { take } from "rxjs/operators";
@Injectable({ @Injectable({
providedIn: "root" providedIn: "root"
}) })
export class DocumentFetchService { 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> { public async fetch(unsafeId: string): Promise<DocumentData> {
const encodedId = this.encodeId(unsafeId); const encodedId = this.encodeId(unsafeId);
const response: Response = await this.http.send(new Request("GET", "/documents/" + encodedId + ".md")); try {
if (response.status === 200) { return await this.fetchWithLanguage(encodedId, this.language);
return { } catch (e) {
found: true, try {
id: encodedId, return await this.fetchWithLanguage(encodedId, "en");
content: await response.body } 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 { return {
found: false, found: true,
id: encodedId, id: encodedId,
content: await this.fetchErrorContent(404) content: await response
}; };
} }
public async fetchErrorContent(errorCode: number): Promise<string> { public async fetchErrorContent(errorCode: number): Promise<string> {
if (errorCode > 0 && errorCode <= 700) { if (errorCode > 0 && errorCode <= 700) {
const response: Response = await this.http.send(new Request("GET", "/documents/" + errorCode + ".md")); try {
if (response.status === 200) { const response: string = await this.http.get("/documents/" + errorCode + "." + this.language + ".md", { responseType: "text" })
return await response.body; .pipe(take(1)).toPromise();
return response;
} catch (e) {
} }
} }
return "Error " + errorCode + "."; return "Error " + errorCode + ".";
} }
private encodeId(id: string): string { 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 = { export const environment = {
production: true production: true
}; };

View File

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

View File

@ -1,8 +1,8 @@
<!doctype html> <!doctype html>
<html> <html generate="language_attribute">
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<title>CavalliumWebsite</title> <title>Cavallium.it</title>
<base href="/"> <base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1"> <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 */ /* 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 { body {
margin: 0; margin: 0;
font-family: "Muli-custom", -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; font-family: "Muli-custom", -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;