Initial commit

This commit is contained in:
2026-01-11 21:16:46 +00:00
commit 25051654a1
73 changed files with 30427 additions and 0 deletions

41
src/app/app.module.ts Normal file
View File

@@ -0,0 +1,41 @@
import { NgModule } from '@angular/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './components/app/app.component';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { MatInputModule } from '@angular/material/input';
import { MatIconModule } from '@angular/material/icon';
import { MatGridListModule } from '@angular/material/grid-list';
import { ClipboardModule } from '@angular/cdk/clipboard';
import { MatSnackBarModule} from '@angular/material/snack-bar';
import { TranslatePipe } from './translate-pipe';
import { PeselComponent } from './components/pesel/pesel.component';
import { NipComponent } from './components/nip/nip.component';
import { RegonComponent } from './components/regon/regon.component';
import { IbanComponent } from './components/iban/iban.component';
@NgModule({
declarations: [
AppComponent,
TranslatePipe,
PeselComponent,
NipComponent,
RegonComponent,
IbanComponent
],
imports: [
BrowserModule,
FormsModule,
BrowserAnimationsModule,
MatInputModule,
MatIconModule,
MatGridListModule,
ClipboardModule,
MatSnackBarModule,
ReactiveFormsModule,
],
providers: [TranslatePipe],
bootstrap: [AppComponent]
})
export class AppModule {
}

View File

@@ -0,0 +1 @@
/* No CSS *//*# sourceMappingURL=app.component.css.map */

View File

@@ -0,0 +1,9 @@
{
"version": 3,
"mappings": "",
"sources": [
"app.component.scss"
],
"names": [],
"file": "app.component.css"
}

View File

@@ -0,0 +1,7 @@
<div class="gradient"></div>
<div class="generators">
<app-regon></app-regon>
<app-nip></app-nip>
<app-pesel></app-pesel>
<app-iban></app-iban>
</div>

View File

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

View File

@@ -0,0 +1 @@
/* No CSS *//*# sourceMappingURL=iban.component.css.map */

View File

@@ -0,0 +1,9 @@
{
"version": 3,
"mappings": "",
"sources": [
"iban.component.scss"
],
"names": [],
"file": "iban.component.css"
}

View File

@@ -0,0 +1,19 @@
<div class="generator">
<div class="title">
<span>IBAN</span>
<div class="titleButtons">
<img id="lettersPrefixButton" src="assets/format_color_text_black_24dp.svg" title="Prefiks literowy" alt="Czy umieścić prefiks literowy PL" (click)="toggleLettersPrefix()" class="smaller" [style.background] = "lettersPrefix ? 'rgba(0, 0, 0, 0.35)' : ''" />
<div style="width: 20px;"></div>
<img id="separatorButton" src="assets/space_bar_black_24dp.svg" title="Separatory między grupami cyfr" alt="Czy umieścić separatory między grupami cyfr" (click)="toggleSeparators()" [style.background] = "separators ? 'rgba(0, 0, 0, 0.35)' : ''" />
</div>
</div>
<div class="container">
<input type="text" [formControl]="valueField" style="width: 380px;"/>
<div class="buttons">
<img id="ibanGenerateButton" src="assets/autorenew_black_24dp.svg" title="Generuj numer IBAN" alt="Generuj numer IBAN" style="margin-left: 1em;" (click)="generate()"/>
<img id="ibanCopyButton" src="assets/content_copy_black_24dp.svg" title="Skopiuj numer IBAN do schowka" alt="Skopiuj numer IBAN do schowka" style="margin: auto 1em;" (click)="copyToClipboard()"/>
<img id="ibanHelpButton" src="assets/info_black_24dp.svg" title="Definicja numeru IBAN na Wikipedii" alt="Definicja numeru IBAN na Wikipedii" (click)="navigateToDocs()" />
</div>
</div>
</div>

View File

@@ -0,0 +1,112 @@
import { Component, OnInit } from '@angular/core';
import { AbstractControl, FormControl } from '@angular/forms';
import { ClipboardService } from '../../service/gui/clipboard.service';
import { IbanService } from '../../service/iban.service';
@Component({
selector: 'app-iban',
templateUrl: './iban.component.html',
styleUrls: ['./iban.component.scss']
})
export class IbanComponent implements OnInit {
public valueField: FormControl;
public lettersPrefix: boolean;
public separators: boolean;
constructor(private ibanService: IbanService, private clipboardService: ClipboardService) {
this.valueField = new FormControl('iban');
this.valueField.setValidators([(control: AbstractControl) => this.ibanService.validateIban(control.value)]);
this.lettersPrefix = false;
this.separators = false;
}
ngOnInit(): void {
this.generate();
}
generate(): void {
let iban: string = this.ibanService.generateIban();
let formattedIban = iban;
if (this.separators) {
formattedIban = iban.substring(0, 2);
for (let i: number = 2; i < iban.length; i+=4) {
formattedIban += " " + iban.substring(i, i + 4);
}
}
if (this.lettersPrefix) {
formattedIban = 'PL' + (this.separators ? " " : "") + formattedIban;
}
this.valueField.setValue(formattedIban);
}
toggleLettersPrefix(): void {
this.lettersPrefix = !this.lettersPrefix;
if (this.valueField.valid) {
var formattedValue: string;
if (this.lettersPrefix) {
formattedValue = this.addLetterPrefix(this.valueField.value);
} else {
formattedValue = this.removeLetterPrefix(this.valueField.value);
}
this.valueField.setValue(formattedValue);
}
}
toggleSeparators(): void {
this.separators = !this.separators;
if (this.valueField.valid) {
var formattedValue: string;
if (this.separators) {
formattedValue = this.addSeparators(this.valueField.value);
} else {
formattedValue = this.removeSeparators(this.valueField.value);
}
this.valueField.setValue(formattedValue);
}
}
addLetterPrefix(iban: string): string {
return 'PL' + (this.separators ? " " : "") + iban;
}
removeLetterPrefix(iban: string): string {
return iban.replace(/[a-zA-Z]*/g, "");
}
addSeparators(iban: string): string {
var formattedIban = iban.substring(0, 2);
for (let i: number = 2; i < iban.length; i+=4) {
formattedIban += " " + iban.substring(i, i + 4);
}
return formattedIban;
}
removeSeparators(iban: string) : string {
return iban.replace(/ /g, "");
}
copyToClipboard(): void {
this.clipboardService.copyToClipboard(this.valueField.value);
}
navigateToDocs(): void {
window.open('https://pl.wikipedia.org/wiki/Mi%C4%99dzynarodowy_numer_rachunku_bankowego', "_blank");
}
}

View File

@@ -0,0 +1 @@
/* No CSS *//*# sourceMappingURL=nip.component.css.map */

View File

@@ -0,0 +1,9 @@
{
"version": 3,
"mappings": "",
"sources": [
"nip.component.scss"
],
"names": [],
"file": "nip.component.css"
}

View File

@@ -0,0 +1,14 @@
<div class="generator">
<div class="title">
<span>NIP</span>
</div>
<div class="container">
<input type="text" [formControl]="valueField" />
<div class="buttons">
<img id="nipGenerateButton" src="assets/autorenew_black_24dp.svg" title="Generuj numer NIP" alt="Generuj numer NIP" (click)="generate()"/>
<img id="nipCopyButton" src="assets/content_copy_black_24dp.svg" title="Skopiuj numer NIP do schowka" alt="Skopiuj numer NIP do schowka" (click)="copyToClipboard()"/>
<img id="nipHelpButton" src="assets/info_black_24dp.svg" title="Szczegóły dotyczące numeru NIP" alt="Definicja numeru NIP na Wikipedii" (click)="navigateToDocs()" />
</div>
</div>
</div>

View File

@@ -0,0 +1,34 @@
import { Component, OnInit } from '@angular/core';
import { AbstractControl, FormControl } from '@angular/forms';
import { NipService } from 'src/app/service/nip.service';
import { ClipboardService } from 'src/app/service/gui/clipboard.service';
@Component({
selector: 'app-nip',
templateUrl: './nip.component.html',
styleUrls: ['./nip.component.scss']
})
export class NipComponent implements OnInit {
public valueField: FormControl;
constructor(private nipService: NipService, private clipboardService: ClipboardService) {
this.valueField = new FormControl('nip');
this.valueField.setValidators([(control: AbstractControl) => this.nipService.validateNip(control.value)]);
}
ngOnInit(): void {
this.generate();
}
generate(): void {
this.valueField.setValue(this.nipService.generateNip());
}
copyToClipboard(): void {
this.clipboardService.copyToClipboard(this.valueField.value);
}
navigateToDocs(): void {
window.open('https://pl.wikipedia.org/wiki/Numer_identyfikacji_podatkowej', "_blank");
}
}

View File

@@ -0,0 +1 @@
/* No CSS *//*# sourceMappingURL=pesel.component.css.map */

View File

@@ -0,0 +1,9 @@
{
"version": 3,
"mappings": "",
"sources": [
"pesel.component.scss"
],
"names": [],
"file": "pesel.component.css"
}

View File

@@ -0,0 +1,19 @@
<div class="generator">
<div class="title">
<span>PESEL</span>
<div class="titleButtons">
<img id="malePeselButton" src="assets/male_black_24dp.svg" title="Płeć męska" alt="Czy generować męskie numery PESEL" (click)="toggleMale()" [style.background] = "generateMale ? 'rgba(0, 0, 0, 0.35)' : ''" />
<div style="width: 20px;"></div>
<img id="femalePeselButton" src="assets/female_black_24dp.svg" title="Płeć żeńska" alt="Czy generować żeńskie numery PESEL" (click)="toggleFemale()" [style.background] = "generateFemale ? 'rgba(0, 0, 0, 0.35)' : ''" />
</div>
</div>
<div class="container">
<input type="text" [formControl]="valueField" />
<div class="buttons">
<img id="peselGenerateButton" src="assets/autorenew_black_24dp.svg" title="Generuj numer PESEL" alt="Generuj numer PESEL" style="margin-left: 1em;" (click)="generate()"/>
<img id="peselCopyButton" src="assets/content_copy_black_24dp.svg" title="Skopiuj numer PESEL do schowka" alt="Skopiuj numer PESEL do schowka" style="margin: auto 1em;" (click)="copyToClipboard()"/>
<img id="peselHelpButton" src="assets/info_black_24dp.svg" title="Szczegóły dotyczące numeru PESEL" alt="Definicja numeru PESEL na Wikipedii" (click)="navigateToDocs()" />
</div>
</div>
</div>

View File

@@ -0,0 +1,52 @@
import { Component, OnInit } from '@angular/core';
import { AbstractControl, FormControl } from '@angular/forms';
import { PeselService } from 'src/app/service/pesel.service';
import { ClipboardService } from 'src/app/service/gui/clipboard.service';
@Component({
selector: 'app-pesel',
templateUrl: './pesel.component.html',
styleUrls: ['./pesel.component.scss']
})
export class PeselComponent implements OnInit {
public valueField: FormControl;
public generateMale: boolean;
public generateFemale: boolean;
constructor(private peselService: PeselService, private clipboardService: ClipboardService) {
this.valueField = new FormControl('pesel');
this.valueField.setValidators([(control: AbstractControl) => this.peselService.validatePesel(control.value)]);
this.generateMale = true;
this.generateFemale = true;
}
ngOnInit(): void {
this.generate();
}
generate(): void {
if (this.generateMale === false && this.generateFemale === false) {
return;
}
this.valueField.setValue(this.peselService.generatePesel(this.generateMale, this.generateFemale));
}
toggleMale(): void {
this.generateMale = !this.generateMale;
}
toggleFemale(): void {
this.generateFemale = !this.generateFemale;
}
copyToClipboard(): void {
this.clipboardService.copyToClipboard(this.valueField.value);
}
navigateToDocs(): void {
window.open('https://pl.wikipedia.org/wiki/PESEL', "_blank");
}
}

View File

@@ -0,0 +1 @@
/* No CSS *//*# sourceMappingURL=regon.component.css.map */

View File

@@ -0,0 +1,9 @@
{
"version": 3,
"mappings": "",
"sources": [
"regon.component.scss"
],
"names": [],
"file": "regon.component.css"
}

View File

@@ -0,0 +1,14 @@
<div class="generator">
<div class="title">
<span>REGON</span>
</div>
<div class="container">
<input type="text" [formControl]="valueField" />
<div class="buttons">
<img id="regonGenerateButton" src="assets/autorenew_black_24dp.svg" title="Generuj numer REGON" alt="Generuj numer REGON" (click)="generate()"/>
<img id="regonCopyButton" src="assets/content_copy_black_24dp.svg" title="Skopiuj numer REGON do schowka" alt="Skopiuj numer REGON do schowka" (click)="copyToClipboard()"/>
<img id="regonHelpButton" src="assets/info_black_24dp.svg" title="Definicja numeru REGON na Wikipedii" alt="Definicja numeru REGON na Wikipedii" (click)="navigateToDocs()" />
</div>
</div>
</div>

View File

@@ -0,0 +1,34 @@
import { Component, OnInit } from '@angular/core';
import { AbstractControl, FormControl } from '@angular/forms';
import { RegonService } from 'src/app/service/regon.service';
import { ClipboardService } from 'src/app/service/gui/clipboard.service';
@Component({
selector: 'app-regon',
templateUrl: './regon.component.html',
styleUrls: ['./regon.component.scss']
})
export class RegonComponent implements OnInit {
public valueField: FormControl;
constructor(private regonService: RegonService, private clipboardService: ClipboardService) {
this.valueField = new FormControl('nip');
this.valueField.setValidators([(control: AbstractControl) => this.regonService.validateRegon9(control.value)]);
}
ngOnInit(): void {
this.generate();
}
generate(): void {
this.valueField.setValue(this.regonService.generateRegon9());
}
copyToClipboard(): void {
this.clipboardService.copyToClipboard(this.valueField.value);
}
navigateToDocs(): void {
window.open('https://pl.wikipedia.org/wiki/REGON', "_blank");
}
}

View File

@@ -0,0 +1,74 @@
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root'
})
export class CommonService {
// numeryczny regexp
public static readonly numericRegExp: RegExp = new RegExp("^[0-9]*$");
constructor() { }
/**
* Waliduje cyfrę kontrolną dla przekazanej wartości i wag kolejnych cyfr.
*
* @param value wartość wejściowa do wyliczenia cyfry kontrolnej
* @param weights wejściowy zbiór wag
* @param lastStepFunction funkcja matematyczna finalizująca wyliczoną sumę
* @returns
*/
public validateControlDigit(value: string, weights: number[], lastStepFunction: (weight: number) => number): boolean {
const controlDigit: number = this.calculateControlDigit(value, weights, lastStepFunction);
const digits: string[] = value.split("");
return controlDigit == parseInt(digits[digits.length - 1], 10);
}
/**
* Wylicza cyfrę kontrolną dla przekazanej wartości używając przekazanej tablicy wag i końcowej operacji matematycznej
*
* @param value analizowana wartość
* @param weights tablica wag dla kolejnych cyfr
* @param lastStepFunction finalizujące działanie matematyczne
* @returns wyliczona cyfra kontrolna
*/
public calculateControlDigit(value: string, weights: number[], lastStepFunction: (weight: number) => number): number {
let digits: string[] = value.split("");
let calculatedChecksum: number = 0;
for (let i = 0; i < weights.length; i++) {
let digit: number = parseInt(digits[i], 10);
calculatedChecksum += digit * weights[i];
}
return lastStepFunction.call(this, calculatedChecksum);
}
/**
* Zwraca liczbę losową z podanego zakresu
*
* @param min minimalna zwrócona
* @param max maksymalna zwrócona wartość
* @returns
*/
public getRandomInt(min: number, max: number): number {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
/**
* Konwertuje przekazaną wartość do tekstu i dopełnia zerami z lewej strony
*
* @param value przekazana wartość liczbowa
* @param length długość tekstu z dopełnionymi zerami
* @returns
*/
public pad(value: number, length: number): string {
let text: string = value + "";
while (text.length < length) {
text = "0" + text;
}
return text;
}
}

View File

@@ -0,0 +1,22 @@
import { Clipboard } from '@angular/cdk/clipboard';
import { Injectable } from '@angular/core';
import { MatSnackBar } from '@angular/material/snack-bar';
@Injectable({
providedIn: 'root'
})
export class ClipboardService {
constructor(private snackBar: MatSnackBar, private clipboard: Clipboard) { }
copyToClipboard(value: string): void {
if (value == '') {
return;
}
this.clipboard.copy(value);
this.snackBar.open("Skopiowano do schowka.", "", {
duration: 1000
});
}
}

View File

@@ -0,0 +1,71 @@
import { Injectable } from '@angular/core';
import { ValidationErrors } from '@angular/forms';
import { CommonService } from './common.service';
@Injectable({
providedIn: 'root'
})
export class IbanService {
private readonly bankSortCode: number = 13201784;
private readonly magic: number = 252100;
constructor(private common: CommonService) { }
public generateIban(): string {
const part1: string = this.common.pad(this.common.getRandomInt(1, 9999), 4);
const part2: string = this.common.pad(this.common.getRandomInt(1, 9999), 4);
const part3: string = this.common.pad(this.common.getRandomInt(1, 9999), 4);
const part4: string = this.common.pad(this.common.getRandomInt(1, 9999), 4);
const ibanWithoutControlDigit = this.bankSortCode + part1 + part2 + part3 + part4;
const controlDigit: string = this.calculateControlDigit(ibanWithoutControlDigit);
return controlDigit + ibanWithoutControlDigit;
}
public validateIban(iban: string): ValidationErrors | null {
const errors: ValidationErrors = {};
iban = iban.replace(/ /g, "");
if (/[a-zA-Z]{2}\d*/g.test(iban)) {
iban = iban.substr(2);
}
if (iban.length < 26) {
errors.tooShort = true;
}
if (iban.length > 26) {
errors.tooLong = true;
}
if (!CommonService.numericRegExp.test(iban)) {
errors.invalidPattern = true;
}
if (iban.length == 26 && !errors.invalidPattern && !this.validateIbanControlDigit(iban)) {
errors.invalidControlDigit = true;
}
return errors;
}
private validateIbanControlDigit(iban: string): boolean {
const controlDigit: string = iban.substring(0, 2);
const ibanWithoutControlDigit = iban.substring(2, 26);
const calculatedControlDigit: string = this.calculateControlDigit(ibanWithoutControlDigit);
return controlDigit == calculatedControlDigit;
}
private calculateControlDigit(value: string): string {
const ibanWithoutControlDigit = BigInt(value + this.magic);
const rest: bigint = ibanWithoutControlDigit % BigInt(97);
const controlDigit: number = 98 - Number(rest);
return this.common.pad(controlDigit, 2);
}
}

View File

@@ -0,0 +1,92 @@
import { Injectable } from '@angular/core';
import { ValidationErrors } from '@angular/forms';
import { CommonService } from './common.service';
@Injectable({
providedIn: 'root'
})
export class NipService {
// wagi kolejnych cyfr numeru NIP
private readonly nipWeights: Array<number> = [6, 5, 7, 2, 3, 4, 5, 6, 7];
private readonly officeCodes: number[] = [
107, 108, 109, 111, 112, 113, 114, 115, 116, 117, 118, 119, 121, 122, 123, 124, 125, 154, 156, 157, 158, 337, 338, 339, 341, 342, 355, 356, 375, 376, 377, 378,
379, 381, 389, 392, 393, 394, 416, 417, 496, 497, 509, 511, 512, 519, 521, 522, 523, 524, 525, 526, 527, 528, 529, 531, 532, 533, 534, 535, 536, 566, 567, 568,
569, 572, 601, 701, 757, 758, 759, 761, 762, 774, 776, 796, 797, 798, 799, 811, 812, 821, 822, 823, 826, 837, 838, 931, 932, 948, 951, 952, 965, 971, 978
];
constructor(private common: CommonService) { }
public generateNip(): string {
let nipWithoutControlDigit: string;
let controlDigit: number = 0;
do {
// losowanie pierwszych 9 cyfr
const officeCode: number = this.officeCodes[this.common.getRandomInt(0, this.officeCodes.length - 1)];
const rest: number = this.common.getRandomInt(0, 999999);
// wyliczenie cyfry kontrolnej
nipWithoutControlDigit = this.common.pad(officeCode, 3) + this.common.pad(rest, 6);
controlDigit = this.common.calculateControlDigit(nipWithoutControlDigit, this.nipWeights, this.lastStepFunction);
} while (controlDigit == 10);
// ostateczna konkatenacja
return nipWithoutControlDigit + controlDigit;
}
validateNip(nip: string): ValidationErrors | null {
const errors: ValidationErrors = {};
if (nip.length < 10) {
errors.tooShort = true;
}
if (nip.length > 10) {
errors.tooLong = true;
}
// czy wszystkie znaki są cyframi
if (!this.validateNipRegExp(nip)) {
errors.invalidPattern = true;
}
// czy kod placówki jest poprawny
if (nip.length >= 3 && !this.validateNipOfficeCode(nip)) {
errors.nipInvalidOfficeCode = true;
}
// czy cyfra kontrolna jest poprawna
if (nip.length == 10 && !errors.invalidPattern && !this.validateNipControlDigit(nip)) {
errors.invalidControlDigit = true;
}
return errors;
}
/**
* Walidacja numeru PESEL przy użyciu wyrażeń regularnych
* @param pesel
* @returns
*/
private validateNipRegExp(pesel: string): boolean {
return CommonService.numericRegExp.test(pesel);
}
/**
* Pelna walidacja numeru PESEL: regexpem i weryfikacja cyfry kontrolnej
* @returns
*/
private validateNipControlDigit(pesel: string): boolean {
return this.common.validateControlDigit(pesel, this.nipWeights, this.lastStepFunction);
}
private validateNipOfficeCode(pesel: string): boolean {
const officeCode : number = parseInt(pesel.substring(0, 3), 10);
return this.officeCodes.indexOf(officeCode) != -1;
}
private lastStepFunction(sum: number): number {
return sum % 11;
}
}

View File

@@ -0,0 +1,172 @@
import { Injectable } from '@angular/core';
import { CommonService } from './common.service';
import { endOfMonth } from 'date-fns'
import { ValidationErrors } from '@angular/forms';
import { concat } from 'rxjs';
@Injectable({
providedIn: 'any'
})
export class PeselService {
// wagi kolejnych cyfr numeru PESEL
private readonly peselWeights: number[] = [1, 3, 7, 9, 1, 3, 7, 9, 1, 3];
private readonly maleDigits: number[] = [1, 3, 5, 7, 9];
private readonly femaleDigits: number[] = [0, 2, 4, 6, 8];
constructor(private common: CommonService) { }
public generatePesel(male: boolean, female: boolean): string {
// losowanie pierwszych 10 cyfr
const year: number = this.common.getRandomInt(0, 99);
const month: number = this.common.getRandomInt(1, 12);
const endDayOfMonth: number = endOfMonth(new Date(year, month - 1)).getDate();
const day: number = this.common.getRandomInt(1, endDayOfMonth);
const rest: number = this.common.getRandomInt(0, 999);
const sexDigits: number[] = [... male ? this.maleDigits : [], ...female ? this.femaleDigits : []];
const sexDigitIndex: number = this.common.getRandomInt(0, sexDigits.length - 1);
const sexDigit = sexDigits[sexDigitIndex];
// wyliczenie cyfry kontrolnej
const peselWithoutControlDigit: string = this.common.pad(year, 2) + this.common.pad(month, 2) + this.common.pad(day, 2) + this.common.pad(rest, 3) + this.common.pad(sexDigit, 1);
const controlDigit: number = this.common.calculateControlDigit(peselWithoutControlDigit, this.peselWeights, this.lastStepFunction);
// ostateczna konkatenacja
return peselWithoutControlDigit + controlDigit;
}
validatePesel(pesel: string): ValidationErrors | null {
const errors: ValidationErrors = {};
if (pesel.length < 11) {
errors.tooShort = true;
}
if (pesel.length > 11) {
errors.tooLong = true;
}
// czy wszystkie znaki są cyframi
if (!this.validatePeselRegExp(pesel)) {
errors.invalidPattern = true;
}
// czy miesiac jest poprawny
if (pesel.length >= 4 && !this.validatePeselMonth(pesel)) {
errors.peselInvalidBirthMonth = true;
}
// czy dzien jest poprawny
if (pesel.length >= 6 && !this.validatePeselDay(pesel)) {
errors.peselInvalidBirthDay = true;
}
// czy cyfra kontrolna jest poprawna
if (pesel.length == 11 && !errors.invalidPattern && !this.validatePeselControlDigit(pesel)) {
errors.invalidControlDigit = true;
}
return errors;
}
/**
* Walidacja numeru PESEL przy użyciu wyrażeń regularnych
* @param pesel
* @returns
*/
private validatePeselRegExp(pesel: string): boolean {
return CommonService.numericRegExp.test(pesel);
}
/**
* Pelna walidacja numeru PESEL: regexpem i weryfikacja cyfry kontrolnej
* @returns
*/
private validatePeselControlDigit(pesel: string): boolean {
return this.common.validateControlDigit(pesel, this.peselWeights, this.lastStepFunction);
}
/**
* Waliduje datę w numerze PESEL:
* 1800-1899 - miesiąc od 81
* 1900-1999 - miesiąc od 01
* 2000-2099 - miesiąc od 21
* 2100-2199 - miesiąc od 41
* 2200-2299 - miesiąc od 61
*
* @param pesel
* @returns
*/
private validatePeselMonth(pesel: string): boolean {
const month: number = parseInt(pesel.substring(2, 4), 10);
return month > 0 && month < 13 ||
month > 20 && month < 33 ||
month > 40 && month < 53 ||
month > 60 && month < 73 ||
month > 80 && month < 93;
}
private validatePeselDay(pesel: string): boolean {
// jesli nie mamy miesiaca to sprawdzamy twardy limit na 31
if (!this.validatePeselMonth(pesel)) {
const day: number = parseInt(pesel.substring(4, 6), 10);
return day <= 31;
}
const yearDiff: number = parseInt(pesel.substring(0, 2), 10);
const month: number = parseInt(pesel.substring(2, 4), 10);
const day: number = parseInt(pesel.substring(4, 6), 10);
const year: number = this.extractYearBasedOnMonth(month);
const endDayOfMonth: number = endOfMonth(new Date(year + yearDiff, month - 1)).getDate();
return day > 0 && day <= endDayOfMonth;
}
private extractDateFromPesel(pesel: string): Date | null {
const yearDiff: number = parseInt(pesel.substring(0, 2), 10);
const month: number = parseInt(pesel.substring(2, 4), 10);
const day: number = parseInt(pesel.substring(4, 6), 10);
let year: number = this.extractYearBasedOnMonth(month) + yearDiff;
const date: Date = new Date(year, month - 1, day)
if (date.getFullYear() == year && date.getMonth() + 1 == month && date.getDate() == day) {
return date;
} else {
return null;
}
}
private extractYearBasedOnMonth(month: number): number {
if (month < 20) {
return 1900;
} else if (month < 40) {
return 2000;
} else if (month < 60) {
return 2100;
} else if (month < 80) {
return 2200;
} else {
return 1800;
}
}
private lastStepFunction(sum: number): number {
sum = sum % 10;
if (sum == 0) {
sum = 10;
}
sum = 10 - sum;
return sum;
}
}

View File

@@ -0,0 +1,123 @@
import { Injectable } from '@angular/core';
import { ValidationErrors } from '@angular/forms';
import { CommonService } from './common.service';
@Injectable({
providedIn: 'root'
})
export class RegonService {
// wagi kolejnych cyfr numeru Regon/9
private static regon9Weights: number[] = [8, 9, 2, 3, 4, 5, 6, 7];
// wagi kolejnych cyfr numeru Regon/14
private static regon14Weights: number[] = [2, 4, 8, 5, 0, 9, 7, 3, 6, 1, 2, 4, 8];
// kody województw
private static provinceCodes: number[] = [1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31, 33, 35, 37, 39, 41, 43, 45, 47, 49, 51,
53, 55, 57, 59, 61, 63, 65, 67, 69, 71, 73, 75, 77, 79, 81, 83, 85, 87, 89, 91, 93, 95, 97];
constructor(private common: CommonService) { }
public generateRegon9(): string {
const provinceIndex: number = this.common.getRandomInt(0, RegonService.provinceCodes.length - 1);
const province: number = RegonService.provinceCodes[provinceIndex];
const rest: number = this.common.getRandomInt(0, 999999);
const regonWithoutControlDigit: string = this.common.pad(province, 2) + this.common.pad(rest, 6);
const controlDigit: number = this.common.calculateControlDigit(regonWithoutControlDigit, RegonService.regon9Weights, this.lastStepFunction);
return regonWithoutControlDigit + controlDigit;
}
public generateRegon14(): string {
const provinceIndex: number = this.common.getRandomInt(0, RegonService.provinceCodes.length - 1);
const province: number = RegonService.provinceCodes[provinceIndex];
const rest: number = this.common.getRandomInt(0, 999999);
const rest2: number = this.common.getRandomInt(0, 99999);
const regonWithoutControlDigit: string = this.common.pad(province, 2) + this.common.pad(rest, 6) + this.common.pad(rest2, 5);
const controlDigit: number = this.common.calculateControlDigit(regonWithoutControlDigit, RegonService.regon14Weights, this.lastStepFunction);
return regonWithoutControlDigit + controlDigit;
}
public validateRegon9(regon: string): ValidationErrors | null {
const errors: ValidationErrors = {};
if (regon.length < 9) {
errors.tooShort = true;
}
if (regon.length > 9) {
errors.tooLong = true;
}
// czy wszystkie znaki są cyframi
if (!this.validateRegonRegExp(regon)) {
errors.invalidPattern = true;
}
// czy kod placówki jest poprawny
if (regon.length >= 2 && !this.validateProvinceCode(regon)) {
errors.regonInvalidProvinceCode = true;
}
// czy cyfra kontrolna jest poprawna
if (regon.length == 9 && !errors.invalidPattern && !this.validateRegon9ControlDigit(regon)) {
errors.invalidControlDigit = true;
}
return errors;
}
public validateRegon14(regon: string): ValidationErrors | null {
const errors: ValidationErrors = {};
if (regon.length < 14) {
errors.tooShort = true;
}
if (regon.length > 14) {
errors.tooLong = true;
}
// czy wszystkie znaki są cyframi
if (!this.validateRegonRegExp(regon)) {
errors.invalidPattern = true;
}
// czy kod placówki jest poprawny
if (regon.length >= 2 && !this.validateProvinceCode(regon)) {
errors.regonInvalidProvinceCode = true;
}
// czy cyfra kontrolna jest poprawna
if (regon.length == 14 && !errors.invalidPattern && !this.validateRegon14ControlDigit(regon)) {
errors.invalidControlDigit = true;
}
return errors;
}
private validateRegon9ControlDigit(value: string): boolean {
return this.common.validateControlDigit(value, RegonService.regon9Weights, this.lastStepFunction);
}
private validateRegon14ControlDigit(value: string): boolean {
return this.common.validateControlDigit(value, RegonService.regon14Weights, this.lastStepFunction);
}
private validateProvinceCode(value: string): boolean {
const provinceCode: number = parseInt(value.substring(0, 2), 10);
return RegonService.provinceCodes.indexOf(provinceCode) != -1;
}
private validateRegonRegExp(regon: string): boolean {
return CommonService.numericRegExp.test(regon);
}
private lastStepFunction(sum: number): number {
const modulo = sum % 11;
return modulo == 10 ? 0 : modulo;
}
}

32
src/app/translate-pipe.ts Normal file
View File

@@ -0,0 +1,32 @@
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({ name: 'translate' })
export class TranslatePipe implements PipeTransform {
private readonly translations: Map<string, string> = new Map([
// wspólne
["tooLong", "Numer jest zbyt długi."],
["tooShort", "Numer jest zbyt krótki."],
["invalidPattern", "Wprowadzono znak niebędący cyfrą."],
["invalidControlDigit", "Cyfra kontrolna jest niepoprawna."],
// PESEL
["peselInvalidBirthMonth", "Miesiąc w dacie urodzenia jest niepoprawny."],
["peselInvalidBirthDay", "Dzień w dacie urodzenia jest niepoprawny."],
// NIP
["nipInvalidOfficeCode", "Kod urzędu skarbowego jest niepoprawny."],
// REGON
["regonInvalidProvinceCode", "Kod województwa jest nieprawidłowy."]
]);
transform(code: string): string {
const translated: string | undefined = this.translations.get(code);
if (translated === undefined) {
return 'Translation undefined for code: ' + code;
}
return translated;
}
}

0
src/assets/.gitkeep Normal file
View File

Binary file not shown.

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M12 6v1.79c0 .45.54.67.85.35l2.79-2.79c.2-.2.2-.51 0-.71l-2.79-2.79c-.31-.31-.85-.09-.85.36V4c-4.42 0-8 3.58-8 8 0 1.04.2 2.04.57 2.95.27.67 1.13.85 1.64.34.27-.27.38-.68.23-1.04C6.15 13.56 6 12.79 6 12c0-3.31 2.69-6 6-6zm5.79 2.71c-.27.27-.38.69-.23 1.04.28.7.44 1.46.44 2.25 0 3.31-2.69 6-6 6v-1.79c0-.45-.54-.67-.85-.35l-2.79 2.79c-.2.2-.2.51 0 .71l2.79 2.79c.31.31.85.09.85-.35V20c4.42 0 8-3.58 8-8 0-1.04-.2-2.04-.57-2.95-.27-.67-1.13-.85-1.64-.34z"/></svg>

After

Width:  |  Height:  |  Size: 612 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M18.65 8.35l-2.79 2.79c-.32.32-.1.86.35.86H18c0 3.31-2.69 6-6 6-.79 0-1.56-.15-2.25-.44-.36-.15-.77-.04-1.04.23-.51.51-.33 1.37.34 1.64.91.37 1.91.57 2.95.57 4.42 0 8-3.58 8-8h1.79c.45 0 .67-.54.35-.85l-2.79-2.79c-.19-.2-.51-.2-.7-.01zM6 12c0-3.31 2.69-6 6-6 .79 0 1.56.15 2.25.44.36.15.77.04 1.04-.23.51-.51.33-1.37-.34-1.64C14.04 4.2 13.04 4 12 4c-4.42 0-8 3.58-8 8H2.21c-.45 0-.67.54-.35.85l2.79 2.79c.2.2.51.2.71 0l2.79-2.79c.31-.31.09-.85-.36-.85H6z"/></svg>

After

Width:  |  Height:  |  Size: 613 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M11 23.59v-3.6c-5.01-.26-9-4.42-9-9.49C2 5.26 6.26 1 11.5 1S21 5.26 21 10.5c0 4.95-3.44 9.93-8.57 12.4l-1.43.69zM11.5 3C7.36 3 4 6.36 4 10.5S7.36 18 11.5 18H13v2.3c3.64-2.3 6-6.08 6-9.8C19 6.36 15.64 3 11.5 3zm-1 11.5h2v2h-2zm2-1.5h-2c0-3.25 3-3 3-5 0-1.1-.9-2-2-2s-2 .9-2 2h-2c0-2.21 1.79-4 4-4s4 1.79 4 4c0 2.5-3 2.75-3 5z"/></svg>

After

Width:  |  Height:  |  Size: 483 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><g><rect fill="none" height="24" width="24"/></g><g><path d="M15,20H5V7c0-0.55-0.45-1-1-1h0C3.45,6,3,6.45,3,7v13c0,1.1,0.9,2,2,2h10c0.55,0,1-0.45,1-1v0C16,20.45,15.55,20,15,20z M20,16V4c0-1.1-0.9-2-2-2H9C7.9,2,7,2.9,7,4v12c0,1.1,0.9,2,2,2h9C19.1,18,20,17.1,20,16z M18,16H9V4h9V16z"/></g></svg>

After

Width:  |  Height:  |  Size: 429 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><rect fill="none" height="24" width="24"/><path d="M18,2H9C7.9,2,7,2.9,7,4v12c0,1.1,0.9,2,2,2h9c1.1,0,2-0.9,2-2V4C20,2.9,19.1,2,18,2z M18,16H9V4h9V16z M3,15v-2h2v2H3z M3,9.5h2v2H3V9.5z M10,20h2v2h-2V20z M3,18.5v-2h2v2H3z M5,22c-1.1,0-2-0.9-2-2h2V22z M8.5,22h-2v-2h2V22z M13.5,22L13.5,22l0-2h2 v0C15.5,21.1,14.6,22,13.5,22z M5,6L5,6l0,2H3v0C3,6.9,3.9,6,5,6z"/></svg>

After

Width:  |  Height:  |  Size: 501 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><rect fill="none" height="24" width="24"/><path d="M17.5,9.5C17.5,6.46,15.04,4,12,4S6.5,6.46,6.5,9.5c0,2.7,1.94,4.93,4.5,5.4V17H9v2h2v2h2v-2h2v-2h-2v-2.1 C15.56,14.43,17.5,12.2,17.5,9.5z M8.5,9.5C8.5,7.57,10.07,6,12,6s3.5,1.57,3.5,3.5S13.93,13,12,13S8.5,11.43,8.5,9.5z"/></svg>

After

Width:  |  Height:  |  Size: 413 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M2 5c-.55 0-1 .45-1 1v15c0 1.1.9 2 2 2h15c.55 0 1-.45 1-1s-.45-1-1-1H4c-.55 0-1-.45-1-1V6c0-.55-.45-1-1-1zm19-4H7c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V3c0-1.1-.9-2-2-2zm-1 16H8c-.55 0-1-.45-1-1V4c0-.55.45-1 1-1h12c.55 0 1 .45 1 1v12c0 .55-.45 1-1 1z"/></svg>

After

Width:  |  Height:  |  Size: 422 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><g><rect fill="none" height="24" width="24"/></g><g><path d="M2,20h20v4H2V20z M5.49,17h2.42l1.27-3.58h5.65L16.09,17h2.42L13.25,3h-2.5L5.49,17z M9.91,11.39l2.03-5.79h0.12l2.03,5.79 H9.91z"/></g></svg>

After

Width:  |  Height:  |  Size: 335 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><g><rect fill="none" height="24" width="24"/><path d="M13.25,16.74c0,0.69-0.53,1.26-1.25,1.26c-0.7,0-1.26-0.56-1.26-1.26c0-0.71,0.56-1.25,1.26-1.25 C12.71,15.49,13.25,16.04,13.25,16.74z M11.99,6c-1.77,0-2.98,1.15-3.43,2.49l1.64,0.69c0.22-0.67,0.74-1.48,1.8-1.48 c1.62,0,1.94,1.52,1.37,2.33c-0.54,0.77-1.47,1.29-1.96,2.16c-0.39,0.69-0.31,1.49-0.31,1.98h1.82c0-0.93,0.07-1.12,0.22-1.41 c0.39-0.72,1.11-1.06,1.87-2.17c0.68-1,0.42-2.36-0.02-3.08C14.48,6.67,13.47,6,11.99,6z M19,5H5v14h14V5 M19,3c1.1,0,2,0.9,2,2v14 c0,1.1-0.9,2-2,2H5c-1.1,0-2-0.9-2-2V5c0-1.1,0.9-2,2-2H19L19,3z"/></g></svg>

After

Width:  |  Height:  |  Size: 722 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8zm-1-4h2v2h-2zm1.61-9.96c-2.06-.3-3.88.97-4.43 2.79-.18.58.26 1.17.87 1.17h.2c.41 0 .74-.29.88-.67.32-.89 1.27-1.5 2.3-1.28.95.2 1.65 1.13 1.57 2.1-.1 1.34-1.62 1.63-2.45 2.88 0 .01-.01.01-.01.02-.01.02-.02.03-.03.05-.09.15-.18.32-.25.5-.01.03-.03.05-.04.08-.01.02-.01.04-.02.07-.12.34-.2.75-.2 1.25h2c0-.42.11-.77.28-1.07.02-.03.03-.06.05-.09.08-.14.18-.27.28-.39.01-.01.02-.03.03-.04.1-.12.21-.23.33-.34.96-.91 2.26-1.65 1.99-3.56-.24-1.74-1.61-3.21-3.35-3.47z"/></svg>

After

Width:  |  Height:  |  Size: 744 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M11 7h2v2h-2zm0 4h2v6h-2zm1-9C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z"/></svg>

After

Width:  |  Height:  |  Size: 307 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><g fill="none"><path d="M0 0h24v24H0V0z"/><path d="M0 0h24v24H0V0z" opacity=".87"/></g><path d="M21 3.01H3c-1.1 0-2 .9-2 2V8c0 .55.45 1 1 1s1-.45 1-1V5.99c0-.55.45-1 1-1h16c.55 0 1 .45 1 1v12.03c0 .55-.45 1-1 1H4c-.55 0-1-.45-1-1V16c0-.55-.45-1-1-1s-1 .45-1 1v3.01c0 1.09.89 1.98 1.98 1.98H21c1.1 0 2-.9 2-2V5.01c0-1.1-.9-2-2-2zm-9.15 12.14l2.79-2.79c.2-.2.2-.51 0-.71l-2.79-2.79c-.31-.32-.85-.1-.85.35V11H2c-.55 0-1 .45-1 1s.45 1 1 1h9v1.79c0 .45.54.67.85.36z"/></svg>

After

Width:  |  Height:  |  Size: 571 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><rect fill="none" height="24" width="24"/><path d="M9.5,11c1.93,0,3.5,1.57,3.5,3.5S11.43,18,9.5,18S6,16.43,6,14.5S7.57,11,9.5,11z M9.5,9C6.46,9,4,11.46,4,14.5 S6.46,20,9.5,20s5.5-2.46,5.5-5.5c0-1.16-0.36-2.23-0.97-3.12L18,7.42V10h2V4h-6v2h2.58l-3.97,3.97C11.73,9.36,10.66,9,9.5,9z"/></svg>

After

Width:  |  Height:  |  Size: 425 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M17.65 6.35c-1.63-1.63-3.94-2.57-6.48-2.31-3.67.37-6.69 3.35-7.1 7.02C3.52 15.91 7.27 20 12 20c3.19 0 5.93-1.87 7.21-4.56.32-.67-.16-1.44-.9-1.44-.37 0-.72.2-.88.53-1.13 2.43-3.84 3.97-6.8 3.31-2.22-.49-4.01-2.3-4.48-4.52C5.31 9.44 8.26 6 12 6c1.66 0 3.14.69 4.22 1.78l-1.51 1.51c-.63.63-.19 1.71.7 1.71H19c.55 0 1-.45 1-1V6.41c0-.89-1.08-1.34-1.71-.71l-.64.65z"/></svg>

After

Width:  |  Height:  |  Size: 520 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M17.59 3.59c-.38-.38-.89-.59-1.42-.59H5c-1.11 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V7.83c0-.53-.21-1.04-.59-1.41l-2.82-2.83zM12 19c-1.66 0-3-1.34-3-3s1.34-3 3-3 3 1.34 3 3-1.34 3-3 3zm1-10H7c-1.1 0-2-.9-2-2s.9-2 2-2h6c1.1 0 2 .9 2 2s-.9 2-2 2z"/></svg>

After

Width:  |  Height:  |  Size: 410 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M18 9v4H6V9H4v6h16V9h-2z"/></svg>

After

Width:  |  Height:  |  Size: 183 B

View File

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

View File

@@ -0,0 +1,16 @@
// This file can be replaced during build by using the `fileReplacements` array.
// `ng build` replaces `environment.ts` with `environment.prod.ts`.
// The list of file replacements can be found in `angular.json`.
export const environment = {
production: false
};
/*
* For easier debugging in development mode, you can import the following file
* to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`.
*
* This import should be commented out in production mode because it will have a negative impact
* on performance if an error is thrown.
*/
// import 'zone.js/plugins/zone-error'; // Included with Angular CLI.

BIN
src/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 948 B

16
src/index.html Normal file
View File

@@ -0,0 +1,16 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Generator i walidator identyfikatorów</title>
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico">
<link rel="preconnect" href="https://fonts.gstatic.com">
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
</head>
<body>
<app-root></app-root>
</body>
</html>

12
src/main.ts Normal file
View File

@@ -0,0 +1,12 @@
import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';
import { environment } from './environments/environment';
if (environment.production) {
enableProdMode();
}
platformBrowserDynamic().bootstrapModule(AppModule)
.catch(err => console.error(err));

69
src/polyfills.ts Normal file
View File

@@ -0,0 +1,69 @@
/***************************************************************************************************
* Load `$localize` onto the global scope - used if i18n tags appear in Angular templates.
*/
import '@angular/localize/init';
/**
* This file includes polyfills needed by Angular and is loaded before the app.
* You can add your own extra polyfills to this file.
*
* This file is divided into 2 sections:
* 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
* 2. Application imports. Files imported after ZoneJS that should be loaded before your main
* file.
*
* The current setup is for so-called "evergreen" browsers; the last versions of browsers that
* automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
* Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
*
* Learn more in https://angular.io/guide/browser-support
*/
/***************************************************************************************************
* BROWSER POLYFILLS
*/
/**
* IE11 requires the following for NgClass support on SVG elements
*/
// import 'classlist.js'; // Run `npm install --save classlist.js`.
/**
* Web Animations `@angular/platform-browser/animations`
* Only required if AnimationBuilder is used within the application and using IE/Edge or Safari.
* Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0).
*/
// import 'web-animations-js'; // Run `npm install --save web-animations-js`.
/**
* By default, zone.js will patch all possible macroTask and DomEvents
* user can disable parts of macroTask/DomEvents patch by setting following flags
* because those flags need to be set before `zone.js` being loaded, and webpack
* will put import in the top of bundle, so user need to create a separate file
* in this directory (for example: zone-flags.ts), and put the following flags
* into that file, and then add the following code before importing zone.js.
* import './zone-flags';
*
* The flags allowed in zone-flags.ts are listed here.
*
* The following flags will work for all browsers.
*
* (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
* (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
* (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
*
* in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
* with the following flag, it will bypass `zone.js` patch for IE/Edge
*
* (window as any).__Zone_enable_cross_context_check = true;
*
*/
/***************************************************************************************************
* Zone JS is required by default for Angular itself.
*/
import 'zone.js'; // Included with Angular CLI.
/***************************************************************************************************
* APPLICATION IMPORTS
*/

194
src/styles.css Normal file
View File

@@ -0,0 +1,194 @@
@font-face {
font-family: 'Comfortaa';
src: url("assets/Comfortaa-VariableFont_wght.ttf");
}
body {
padding: 0;
margin: 0;
text-decoration: none;
border: none;
font-size: 150%;
font-family: 'Comfortaa', 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
.gradient {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: -1;
background-image: -webkit-gradient(linear, left top, left bottom, from(#3362bc), color-stop(#3d6cc7), color-stop(#4776d1), color-stop(#5181dc), color-stop(#5a8be7), color-stop(#5a8be7), color-stop(#5b8ae7), color-stop(#5b8ae7), color-stop(#527fdc), color-stop(#4975d1), color-stop(#406ac7), to(#3760bc));
background-image: linear-gradient(to bottom, #3362bc, #3d6cc7, #4776d1, #5181dc, #5a8be7, #5a8be7, #5b8ae7, #5b8ae7, #527fdc, #4975d1, #406ac7, #3760bc);
}
.generators {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-orient: horizontal;
-webkit-box-direction: normal;
-ms-flex-direction: row;
flex-direction: row;
-webkit-box-flex: 1;
-ms-flex-positive: 1;
flex-grow: 1;
-ms-flex-negative: 1;
flex-shrink: 1;
-webkit-box-pack: space-evenly;
-ms-flex-pack: space-evenly;
justify-content: space-evenly;
-ms-flex-wrap: wrap;
flex-wrap: wrap;
}
.generator {
margin: 1em;
background: rgba(255, 255, 255, 0.15);
-webkit-box-shadow: 0 8px 32px 0 rgba(0, 38, 135, 0.37);
box-shadow: 0 8px 32px 0 rgba(0, 38, 135, 0.37);
border-radius: 3px;
border: 1px solid rgba(255, 255, 255, 0.18);
}
.container {
margin: 0 1em 1em 1em;
display: -webkit-box;
display: -ms-flexbox;
display: flex;
place-content: space-between;
-ms-flex-wrap: wrap;
flex-wrap: wrap;
}
.container .buttons {
margin-top: 1em;
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-ms-flex-wrap: nowrap;
flex-wrap: nowrap;
place-content: space-between;
margin-left: auto;
}
.container .buttons img {
padding: .2em;
width: 25px;
height: 25px;
}
.container .buttons img:hover {
background: rgba(0, 0, 0, 0.3);
}
.container .buttons :nth-child(1) {
margin: auto 0 auto 1em;
}
.container .buttons :nth-child(2) {
margin: auto 1em;
}
.title {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-orient: horizontal;
-webkit-box-direction: normal;
-ms-flex-direction: row;
flex-direction: row;
-webkit-box-pack: justify;
-ms-flex-pack: justify;
justify-content: space-between;
margin: 0;
padding: 0;
border-top-left-radius: 3px;
border-top-right-radius: 3px;
border-bottom: 1px solid rgba(255, 255, 255, 0.7);
background: rgba(255, 255, 255, 0.2);
}
.title span {
margin-left: 1em;
padding: 0.4em 0;
color: white;
text-align: center;
font-weight: bold;
letter-spacing: 2px;
}
.title .titleButtons {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-orient: horizontal;
-webkit-box-direction: normal;
-ms-flex-direction: row;
flex-direction: row;
-ms-flex-pack: distribute;
justify-content: space-around;
-ms-flex-line-pack: center;
align-content: center;
margin: 0 1em;
padding: 0;
}
.title .titleButtons img {
padding: .1em;
width: 25px;
height: 25px;
}
.title .titleButtons img:hover {
background: rgba(0, 0, 0, 0.3);
}
.title .titleButtons .smaller {
padding: 0.2em;
width: 20px;
height: 20px;
}
input[type="text"] {
margin-top: 1em;
width: 210px;
min-width: 0px;
-webkit-box-flex: 1;
-ms-flex-positive: 1;
flex-grow: 1;
-ms-flex-negative: 1;
flex-shrink: 1;
padding: 0 0.7em;
letter-spacing: 1px;
font-size: 1em;
height: 2em;
color: white;
background: rgba(255, 255, 255, 0.15);
-webkit-box-shadow: 0 8px 32px 0 rgba(0, 38, 135, 0.37);
box-shadow: 0 8px 32px 0 rgba(0, 38, 135, 0.37);
border-radius: 3px;
border: 1px solid rgba(255, 255, 255, 0.2);
}
input[type="text"].ng-valid {
border-bottom: 3px solid #87e17a;
}
input[type="text"].ng-invalid {
border-bottom: 3px solid #fb8686;
}
img {
cursor: pointer;
-webkit-filter: invert(100%);
filter: invert(100%);
margin: auto 0;
background: rgba(0, 0, 0, 0.15);
-webkit-box-shadow: 0 8px 32px 0 rgba(255, 217, 120, 0.37);
box-shadow: 0 8px 32px 0 rgba(255, 217, 120, 0.37);
border-radius: 3px;
border: 1px solid rgba(0, 0, 0, 0.1);
}
/*# sourceMappingURL=styles.css.map */

9
src/styles.css.map Normal file
View File

@@ -0,0 +1,9 @@
{
"version": 3,
"mappings": "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",
"sources": [
"styles.scss"
],
"names": [],
"file": "styles.css"
}

159
src/styles.scss Normal file
View File

@@ -0,0 +1,159 @@
@font-face {
font-family: 'Comfortaa';
src: url('assets/Comfortaa-VariableFont_wght.ttf');
}
body {
padding: 0;
margin: 0;
text-decoration: none;
border: none;
font-size: 150%;
font-family: 'Comfortaa', 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
.gradient {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: -1;
background-image: linear-gradient(to bottom, #3362bc, #3d6cc7, #4776d1, #5181dc, #5a8be7, #5a8be7, #5b8ae7, #5b8ae7, #527fdc, #4975d1, #406ac7, #3760bc);
}
.generators {
display: flex;
flex-direction: row;
flex-grow: 1;
flex-shrink: 1;
justify-content: space-evenly;
flex-wrap: wrap;
}
.generator {
margin: 1em;
background: rgba( 255, 255, 255, 0.15 );
box-shadow: 0 8px 32px 0 rgba( 0, 38, 135, 0.37 );
border-radius: 3px;
border: 1px solid rgba( 255, 255, 255, 0.18 );
}
.container {
margin: 0 1em 1em 1em;
display: flex;
place-content: space-between;
flex-wrap: wrap;
.buttons {
margin-top: 1em;
display: flex;
flex-wrap: nowrap;
place-content: space-between;
margin-left: auto;
img {
padding: .2em;
width: 25px;
height: 25px;
&:hover {
background: rgba( 0, 0, 0, 0.30 );
}
}
:nth-child(1) {
margin: auto 0 auto 1em;
}
:nth-child(2) {
margin: auto 1em;
}
}
}
.title {
display: flex;
flex-direction: row;
justify-content: space-between;
margin: 0;
padding: 0;
border-top-left-radius: 3px;
border-top-right-radius: 3px;
border-bottom: 1px solid rgba(255, 255, 255, 0.7);
background: rgba(255, 255, 255, 0.20);
span {
margin-left: 1em;
padding: 0.4em 0;
color: white;
text-align: center;
font-weight: bold;
letter-spacing: 2px;
}
.titleButtons {
display: flex;
flex-direction: row;
justify-content: space-around;
align-content: center;
margin: 0 1em;
padding: 0;
img {
padding: .1em;
width: 25px;
height: 25px;
&:hover {
background: rgba( 0, 0, 0, 0.30 );
}
}
.smaller {
padding: 0.2em;
width: 20px;
height: 20px;
}
}
}
input[type="text"] {
margin-top: 1em;
width: 210px;
min-width: 0px;
flex-grow: 1;
flex-shrink: 1;
padding: 0 0.7em;
letter-spacing: 1px;
font-size: 1em;
height: 2em;
color: white;
background: rgba(255, 255, 255, 0.15);
box-shadow: 0 8px 32px 0 rgba( 0, 38, 135, 0.37 );
border-radius: 3px;
border: 1px solid rgba( 255, 255, 255, 0.20 );
&.ng-valid {
border-bottom: 3px solid #87e17a
}
&.ng-invalid {
border-bottom: 3px solid #fb8686;
}
}
img {
cursor: pointer;
filter: invert(100%);
margin: auto 0;
background: rgba( 0, 0, 0, 0.15 );
box-shadow: 0 8px 32px 0 rgba(255, 217, 120, 0.37);
border-radius: 3px;
border: 1px solid rgba(0, 0, 0, 0.10);
}

27
src/test.ts Normal file
View File

@@ -0,0 +1,27 @@
// This file is required by karma.conf.js and loads recursively all the .spec and framework files
import 'zone.js/testing';
import { getTestBed } from '@angular/core/testing';
import {
BrowserDynamicTestingModule,
platformBrowserDynamicTesting
} from '@angular/platform-browser-dynamic/testing';
declare const require: {
context(path: string, deep?: boolean, filter?: RegExp): {
keys(): string[];
<T>(id: string): T;
};
};
// First, initialize the Angular testing environment.
getTestBed().initTestEnvironment(
BrowserDynamicTestingModule,
platformBrowserDynamicTesting(),
{ teardown: { destroyAfterEach: true }},
);
// Then we find all the tests.
const context = require.context('./', true, /\.spec\.ts$/);
// And load the modules.
context.keys().map(context);