diff --git a/.gitignore b/.gitignore
index 0a811ca..645d3a5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -112,3 +112,4 @@ dist
**/.idea/tasks.xml
**/.idea/shelf
**/.idea/*.iml
+/tmp/
diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml
new file mode 100644
index 0000000..a55e7a1
--- /dev/null
+++ b/.idea/codeStyles/codeStyleConfig.xml
@@ -0,0 +1,5 @@
+
- Access is restricted. Authentication can be obtained with HTTP Basic Auth using username and password. Data access methods can also be accessed using an API key at the URL ending like ?key=xxx
+ Access is restricted. Authentication can be obtained with HTTP Basic Auth using username and password.
+ Data access methods can also be accessed using an API key at the URL ending like ?key=xxx
+ The description lists available authentication methods, also the locks of each method close correspondingly
+ if the entered authentication is allowed.
There are a number of different user levels:
+ extends core.ErrorRequestHandler
{ }
+ interface Express extends core.Express { }
+ interface Handler extends core.Handler { }
+ interface IRoute extends core.IRoute { }
+ interface IRouter extends core.IRouter { }
+ interface IRouterHandler extends core.Request { }
+ interface RequestHandler extends core.RequestHandler { }
+ interface RequestParamHandler extends core.RequestParamHandler { }
+ export interface Response
You requested to reset your password.
Your new password is:
' + newPass + '
If you did not request a password reset, talk to the sysadmin quickly!
Have a nice day.
The DFOP team', err => {
+ if (err) return next(err);
+ res.json({status: 'OK'});
+ });
+ });
+ });
+ }
+ else {
+ res.status(404).json({status: 'Not found'});
+ }
+ });
+});
+
+
+module.exports = router;
\ No newline at end of file
diff --git a/src/routes/validate/id.ts b/src/routes/validate/id.ts
new file mode 100644
index 0000000..5409993
--- /dev/null
+++ b/src/routes/validate/id.ts
@@ -0,0 +1,26 @@
+import joi from '@hapi/joi';
+
+export default class IdValidate {
+ private static id = joi.string().pattern(new RegExp('[0-9a-f]{24}')).length(24);
+
+ static get () {
+ return this.id;
+ }
+
+ static valid (id) {
+ return this.id.validate(id).error === undefined;
+ }
+
+ static parameter () { // :id url parameter
+ return ':id([0-9a-f]{24})';
+ }
+
+ static stringify (data) {
+ Object.keys(data).forEach(key => {
+ if (data[key] !== null && data[key].hasOwnProperty('_bsontype') && data[key]._bsontype === 'ObjectID') {
+ data[key] = data[key].toString();
+ }
+ });
+ return data;
+ }
+}
\ No newline at end of file
diff --git a/src/routes/validate/material.ts b/src/routes/validate/material.ts
new file mode 100644
index 0000000..54cd749
--- /dev/null
+++ b/src/routes/validate/material.ts
@@ -0,0 +1,82 @@
+import joi from '@hapi/joi';
+
+import IdValidate from './id';
+
+export default class MaterialValidate { // validate input for material
+ private static material = {
+ name: joi.string()
+ .max(128),
+
+ supplier: joi.string()
+ .max(128),
+
+ group: joi.string()
+ .max(128),
+
+ mineral: joi.number()
+ .integer()
+ .min(0)
+ .max(100),
+
+ glass_fiber: joi.number()
+ .integer()
+ .min(0)
+ .max(100),
+
+ carbon_fiber: joi.number()
+ .integer()
+ .min(0)
+ .max(100),
+
+ numbers: joi.array()
+ .items(joi.object({
+ color: joi.string()
+ .max(128),
+ number: joi.number()
+ .min(0)
+ }))
+ };
+
+ static input (data, param) { // validate data, param: new(everything required)/change(available attributes are validated)
+ if (param === 'new') {
+ return joi.object({
+ name: this.material.name.required(),
+ supplier: this.material.supplier.required(),
+ group: this.material.group.required(),
+ mineral: this.material.mineral.required(),
+ glass_fiber: this.material.glass_fiber.required(),
+ carbon_fiber: this.material.carbon_fiber.required(),
+ numbers: this.material.numbers
+ }).validate(data);
+ }
+ else if (param === 'change') {
+ return joi.object({
+ name: this.material.name,
+ supplier: this.material.supplier,
+ group: this.material.group,
+ mineral: this.material.mineral,
+ glass_fiber: this.material.glass_fiber,
+ carbon_fiber: this.material.carbon_fiber,
+ numbers: this.material.numbers
+ }).validate(data);
+ }
+ else {
+ return{error: 'No parameter specified!', value: {}};
+ }
+ }
+
+ static output (data) { // validate output from database for needed properties, strip everything else
+ data = IdValidate.stringify(data);
+ const {value, error} = joi.object({
+ _id: IdValidate.get(),
+ name: this.material.name,
+ supplier: this.material.supplier,
+ group: this.material.group,
+ mineral: this.material.mineral,
+ glass_fiber: this.material.glass_fiber,
+ carbon_fiber: this.material.carbon_fiber,
+ numbers: this.material.numbers
+ }).validate(data, {stripUnknown: true});
+ return error !== undefined? null : value;
+ }
+}
\ No newline at end of file
diff --git a/src/routes/validate/note_field.ts b/src/routes/validate/note_field.ts
new file mode 100644
index 0000000..4892f22
--- /dev/null
+++ b/src/routes/validate/note_field.ts
@@ -0,0 +1,18 @@
+import joi from '@hapi/joi';
+
+export default class NoteFieldValidate {
+ private static note_field = {
+ name: joi.string()
+ .max(128),
+
+ qty: joi.number()
+ };
+
+ static output (data) {
+ const {value, error} = joi.object({
+ name: this.note_field.name,
+ qty: this.note_field.qty
+ }).validate(data, {stripUnknown: true});
+ return error !== undefined? null : value;
+ }
+}
\ No newline at end of file
diff --git a/src/routes/validate/sample.ts b/src/routes/validate/sample.ts
new file mode 100644
index 0000000..d94cede
--- /dev/null
+++ b/src/routes/validate/sample.ts
@@ -0,0 +1,77 @@
+import joi from '@hapi/joi';
+
+import IdValidate from './id';
+
+export default class SampleValidate {
+ private static sample = {
+ number: joi.string()
+ .max(128),
+
+ color: joi.string()
+ .max(128),
+
+ type: joi.string()
+ .max(128),
+
+ batch: joi.string()
+ .max(128)
+ .allow(''),
+
+ notes: joi.object({
+ comment: joi.string()
+ .max(512),
+
+ sample_references: joi.array()
+ .items(joi.object({
+ id: IdValidate.get(),
+
+ relation: joi.string()
+ .max(128)
+ })),
+
+ custom_fields: joi.object()
+ .pattern(/.*/, joi.alternatives()
+ .try(
+ joi.string().max(128),
+ joi.number(),
+ joi.boolean(),
+ joi.date()
+ )
+ )
+ })
+ };
+
+ static input (data, param) { // validate data, param: new(everything required)/change(available attributes are validated)
+ if (param === 'new') {
+ return joi.object({
+ number: this.sample.number.required(),
+ color: this.sample.color.required(),
+ type: this.sample.type.required(),
+ batch: this.sample.batch.required(),
+ material_id: IdValidate.get().required(),
+ notes: this.sample.notes.required()
+ }).validate(data);
+ }
+ else if (param === 'change') {
+ return{error: 'Not implemented!', value: {}};
+ }
+ else {
+ return{error: 'No parameter specified!', value: {}};
+ }
+ }
+
+ static output (data) {
+ data = IdValidate.stringify(data);
+ const {value, error} = joi.object({
+ _id: IdValidate.get(),
+ number: this.sample.number,
+ color: this.sample.color,
+ type: this.sample.type,
+ batch: this.sample.batch,
+ material_id: IdValidate.get(),
+ note_id: IdValidate.get().allow(null),
+ user_id: IdValidate.get()
+ }).validate(data, {stripUnknown: true});
+ return error !== undefined? null : value;
+ }
+}
\ No newline at end of file
diff --git a/src/routes/validate/template.ts b/src/routes/validate/template.ts
new file mode 100644
index 0000000..a279dce
--- /dev/null
+++ b/src/routes/validate/template.ts
@@ -0,0 +1,59 @@
+import joi from '@hapi/joi';
+import IdValidate from './id';
+
+export default class TemplateValidate {
+ private static template = {
+ name: joi.string()
+ .max(128),
+
+ parameters: joi.array()
+ .min(1)
+ .items(
+ joi.object({
+ name: joi.string()
+ .max(128)
+ .required(),
+
+ range: joi.object({
+ values: joi.array()
+ .min(1),
+
+ min: joi.number(),
+
+ max: joi.number()
+ })
+ .oxor('values', 'min')
+ .oxor('values', 'max')
+ .required()
+ })
+ )
+ };
+
+ static input (data, param) { // validate data, param: new(everything required)/change(available attributes are validated)
+ if (param === 'new') {
+ return joi.object({
+ name: this.template.name.required(),
+ parameters: this.template.parameters.required()
+ }).validate(data);
+ }
+ else if (param === 'change') {
+ return joi.object({
+ name: this.template.name,
+ parameters: this.template.parameters
+ }).validate(data);
+ }
+ else {
+ return{error: 'No parameter specified!', value: {}};
+ }
+ }
+
+ static output (data) { // validate output from database for needed properties, strip everything else
+ data = IdValidate.stringify(data);
+ const {value, error} = joi.object({
+ _id: IdValidate.get(),
+ name: this.template.name,
+ parameters: this.template.parameters
+ }).validate(data, {stripUnknown: true});
+ return error !== undefined? null : value;
+ }
+}
\ No newline at end of file
diff --git a/src/routes/validate/user.ts b/src/routes/validate/user.ts
new file mode 100644
index 0000000..150bf64
--- /dev/null
+++ b/src/routes/validate/user.ts
@@ -0,0 +1,87 @@
+import joi from '@hapi/joi';
+import globals from '../../globals';
+
+import IdValidate from './id';
+
+export default class UserValidate { // validate input for user
+ private static user = {
+ name: joi.string()
+ .alphanum()
+ .lowercase()
+ .max(128),
+
+ email: joi.string()
+ .email({minDomainSegments: 2})
+ .lowercase()
+ .max(128),
+
+ pass: joi.string()
+ .pattern(new RegExp('^(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])(?=.*[!"#%&\'()*+,-.\\/:;<=>?@[\\]^_`{|}~])(?=\\S+$).{8,}$'))
+ .max(128),
+
+ level: joi.string()
+ .valid(...globals.levels),
+
+ location: joi.string()
+ .alphanum()
+ .max(128),
+
+ device_name: joi.string()
+ .allow('')
+ .max(128),
+ };
+
+ private static specialUsernames = ['admin', 'user', 'key', 'new', 'passreset']; // names a user cannot take
+
+ static input (data, param) {
+ if (param === 'new') {
+ return joi.object({
+ name: this.user.name.required(),
+ email: this.user.email.required(),
+ pass: this.user.pass.required(),
+ level: this.user.level.required(),
+ location: this.user.location.required(),
+ device_name: this.user.device_name.required()
+ }).validate(data);
+ }
+ else if (param === 'change') {
+ return joi.object({
+ name: this.user.name,
+ email: this.user.email,
+ pass: this.user.pass,
+ location: this.user.location,
+ device_name: this.user.device_name
+ }).validate(data);
+ }
+ else if (param === 'changeadmin') {
+ return joi.object({
+ name: this.user.name,
+ email: this.user.email,
+ pass: this.user.pass,
+ level: this.user.level,
+ location: this.user.location,
+ device_name: this.user.device_name
+ }).validate(data);
+ }
+ else {
+ return{error: 'No parameter specified!', value: {}};
+ }
+ }
+
+ static output (data) { // validate output from database for needed properties, strip everything else
+ data = IdValidate.stringify(data);
+ const {value, error} = joi.object({
+ _id: IdValidate.get(),
+ name: this.user.name,
+ email: this.user.email,
+ level: this.user.level,
+ location: this.user.location,
+ device_name: this.user.device_name
+ }).validate(data, {stripUnknown: true});
+ return error !== undefined? null : value;
+ }
+
+ static isSpecialName (name) { // true if name belongs to special names
+ return this.specialUsernames.indexOf(name) > -1;
+ }
+}
diff --git a/src/test/db.json b/src/test/db.json
new file mode 100644
index 0000000..2d8a7d0
--- /dev/null
+++ b/src/test/db.json
@@ -0,0 +1,281 @@
+{
+ "collections": {
+ "samples": [
+ {
+ "_id": {"$oid":"400000000000000000000001"},
+ "number": "1",
+ "type": "granulate",
+ "color": "black",
+ "batch": "",
+ "validated": true,
+ "material_id": {"$oid":"100000000000000000000004"},
+ "note_id": null,
+ "user_id": {"$oid":"000000000000000000000002"},
+ "__v": 0
+ },
+ {
+ "_id": {"$oid":"400000000000000000000002"},
+ "number": "21",
+ "type": "granulate",
+ "color": "natural",
+ "batch": "1560237365",
+ "validated": true,
+ "material_id": {"$oid":"100000000000000000000001"},
+ "note_id": {"$oid":"500000000000000000000001"},
+ "user_id": {"$oid":"000000000000000000000002"},
+ "__v": 0
+ },
+ {
+ "_id": {"$oid":"400000000000000000000003"},
+ "number": "33",
+ "type": "part",
+ "color": "black",
+ "batch": "1704-005",
+ "validated": false,
+ "material_id": {"$oid":"100000000000000000000005"},
+ "note_id": {"$oid":"500000000000000000000002"},
+ "user_id": {"$oid":"000000000000000000000003"},
+ "__v": 0
+ },
+ {
+ "_id": {"$oid":"400000000000000000000004"},
+ "number": "32",
+ "type": "granulate",
+ "color": "black",
+ "batch": "1653000308",
+ "validated": false,
+ "material_id": {"$oid":"100000000000000000000005"},
+ "note_id": {"$oid":"500000000000000000000003"},
+ "user_id": {"$oid":"000000000000000000000003"},
+ "__v": 0
+ }
+ ],
+ "notes": [
+ {
+ "_id": {"$oid":"500000000000000000000001"},
+ "comment": "Stoff gesperrt",
+ "sample_references": [],
+ "__v": 0
+ },
+ {
+ "_id": {"$oid":"500000000000000000000002"},
+ "comment": "",
+ "sample_references": [{
+ "id": "400000000000000000000004",
+ "relation": "granulate to sample"
+ }],
+ "custom_fields": {
+ "not allowed for new applications": true
+ },
+ "__v": 0
+ },
+ {
+ "_id": {"$oid":"500000000000000000000003"},
+ "comment": "",
+ "sample_references": [{
+ "id": "400000000000000000000003",
+ "relation": "part to sample"
+ }],
+ "custom_fields": {
+ "not allowed for new applications": true
+ },
+ "__v": 0
+ }
+ ],
+ "note_fields": [
+ {
+ "_id": {"$oid":"600000000000000000000001"},
+ "name": "not allowed for new applications",
+ "qty": 2,
+ "__v": 0
+ }
+ ],
+ "materials": [
+ {
+ "_id": {"$oid":"100000000000000000000001"},
+ "name": "Stanyl TW 200 F8",
+ "supplier": "DSM",
+ "group": "PA46",
+ "mineral": 0,
+ "glass_fiber": 40,
+ "carbon_fiber": 0,
+ "numbers": [
+ {
+ "color": "black",
+ "number": 5514263423
+ },
+ {
+ "color": "natural",
+ "number": 5514263422
+ }
+ ],
+ "__v": 0
+ },
+ {
+ "_id": {"$oid":"100000000000000000000002"},
+ "name": "Ultramid T KR 4355 G7",
+ "supplier": "BASF",
+ "group": "PA6/6T",
+ "mineral": 0,
+ "glass_fiber": 35,
+ "carbon_fiber": 0,
+ "numbers": [
+ {
+ "color": "black",
+ "number": 5514212901
+ },
+ {
+ "color": "signalviolet",
+ "number": 5514612901
+ }
+ ],
+ "__v": 0
+ },
+ {
+ "_id": {"$oid":"100000000000000000000003"},
+ "name": "PA GF 50 black (2706)",
+ "supplier": "Akro-Plastic",
+ "group": "PA66+PA6I/6T",
+ "mineral": 0,
+ "glass_fiber": 0,
+ "carbon_fiber": 0,
+ "numbers": [
+ ],
+ "__v": 0
+ },
+ {
+ "_id": {"$oid":"100000000000000000000004"},
+ "name": "Schulamid 66 GF 25 H",
+ "supplier": "Schulmann",
+ "group": "PA66",
+ "mineral": 0,
+ "glass_fiber": 25,
+ "carbon_fiber": 0,
+ "numbers": [
+ {
+ "color": "black",
+ "number": 5513933405
+ }
+ ],
+ "__v": 0
+ },
+ {
+ "_id": {"$oid":"100000000000000000000005"},
+ "name": "Amodel A 1133 HS",
+ "supplier": "Solvay",
+ "group": "PPA",
+ "mineral": 0,
+ "glass_fiber": 33,
+ "carbon_fiber": 0,
+ "numbers": [
+ {
+ "color": "black",
+ "number": 5514262406
+ }
+ ],
+ "__v": 0
+ }
+ ],
+ "treatment_templates": [
+ {
+ "_id": {"$oid":"200000000000000000000001"},
+ "name": "heat treatment",
+ "parameters": [
+ {
+ "name": "material",
+ "range": {
+ "values": [
+ "copper",
+ "hot air"
+ ]
+ }
+ },
+ {
+ "name": "weeks",
+ "range": {
+ "min": 1,
+ "max": 10
+ }
+ }
+ ]
+ },
+ {
+ "_id": {"$oid":"200000000000000000000002"},
+ "name": "heat treatment 2",
+ "parameters": [
+ {
+ "name": "material",
+ "range": {}
+ }
+ ]
+ }
+ ],
+ "measurement_templates": [
+ {
+ "_id": {"$oid":"300000000000000000000001"},
+ "name": "spectrum",
+ "parameters": [
+ {
+ "name": "dpt",
+ "range": {}
+ }
+ ]
+ },
+ {
+ "_id": {"$oid":"300000000000000000000002"},
+ "name": "kf",
+ "parameters": [
+ {
+ "name": "weight %",
+ "range": {
+ "min": 0,
+ "max": 1.5
+ }
+ },
+ {
+ "name": "standard deviation",
+ "range": {
+ "min": 0,
+ "max": 0.5
+ }
+ }
+ ]
+ }
+ ],
+ "users": [
+ {
+ "_id": {"$oid":"000000000000000000000001"},
+ "email": "user@bosch.com",
+ "name": "user",
+ "pass": "$2a$10$di26XKF63OG0V00PL1kSK.ceCcTxDExBMOg.jkHiCnXcY7cN7DlPi",
+ "level": "read",
+ "location": "Rng",
+ "device_name": "Alpha I",
+ "key": "000000000000000000001001",
+ "__v": 0
+ },
+ {
+ "_id": {"$oid":"000000000000000000000002"},
+ "email": "jane.doe@bosch.com",
+ "name": "janedoe",
+ "pass": "$2a$10$di26XKF63OG0V00PL1kSK.ceCcTxDExBMOg.jkHiCnXcY7cN7DlPi",
+ "level": "write",
+ "location": "Rng",
+ "device_name": "Alpha I",
+ "key": "000000000000000000001002",
+ "__v": 0
+ },
+ {
+ "_id": {"$oid":"000000000000000000000003"},
+ "email": "a.d.m.i.n@bosch.com",
+ "name": "admin",
+ "pass": "$2a$10$i872o3qR5V3JnbDArD8Z.eDo.BNPDBaR7dUX9KSEtl9pUjLyucy2K",
+ "level": "admin",
+ "location": "Rng",
+ "device_name": "",
+ "key": "000000000000000000001003",
+ "__v": "0"
+ }
+ ]
+ }
+}
\ No newline at end of file
diff --git a/static/img/bosch-logo.svg b/static/img/bosch-logo.svg
new file mode 100644
index 0000000..fae963f
--- /dev/null
+++ b/static/img/bosch-logo.svg
@@ -0,0 +1,201 @@
+
+
+
diff --git a/static/styles/swagger.css b/static/styles/swagger.css
new file mode 100644
index 0000000..33bebe1
--- /dev/null
+++ b/static/styles/swagger.css
@@ -0,0 +1,306 @@
+/*Bosch styling for swagger*/
+
+/*GET: dark blue*/
+/*POST: dark green*/
+/*PUT: turquoise*/
+/*DELETE: fuchsia*/
+
+:root {
+ --red: #ea0016;
+ --dark-blue: #005691;
+ --dark-blue-w75: #bfd5e3;
+ --dark-green: #006249;
+ --dark-green-w75: #bfd8d1;
+ --turquoise: #00a8b0;
+ --turquoise-w75: #bfe9eb;
+ --fuchsia: #b90276;
+ --fuchsia-w75: #edc0dd;
+ --light-grey: #bfc0c2;
+ --light-grey-w75: #efeff0;
+ --light-green: #78be20;
+}
+
+body {
+ background: #fff;
+}
+
+body:before {
+ position: absolute;
+ left: 0;
+ top: 0;
+ width: 100%;
+ height: 16px;
+ content: '';
+ background-repeat: no-repeat;
+ background-size: cover;
+ background-image: url(data:image/svg+xml;base64,PHN2ZwogIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICB4bWw6c3BhY2U9InByZXNlcnZlIgogIGhlaWdodD0iMzAwIgogIHdpZHRoPSI3MjAiCiAgdmVyc2lvbj0iMS4xIgogIHk9IjAiCiAgeD0iMCIKICB2aWV3Qm94PSIwIDAgNzIwIDMwMCI+CiAgPHN0eWxlIHR5cGU9InRleHQvY3NzIj4KICAgIC5zdDAgewogICAgICBmaWxsOiB1cmwoIiNTVkdJRF8xXyIpOwogICAgfQogICAgLnN0MSB7CiAgICAgIGZpbGw6IHVybCgiI1NWR0lEXzJfIik7CiAgICB9CiAgICAuc3QyIHsKICAgICAgZmlsbDogdXJsKCIjU1ZHSURfM18iKTsKICAgIH0KICAgIC5zdDMgewogICAgICBmaWxsOiB1cmwoIiNTVkdJRF80XyIpOwogICAgfQogICAgLnN0NCB7CiAgICAgIGZpbGw6IHVybCgiI1NWR0lEXzVfIik7CiAgICB9CiAgICAuc3Q1IHsKICAgICAgZmlsbDogI0FGMjAyNDsKICAgIH0KICAgIC5zdDYgewogICAgICBmaWxsOiB1cmwoIiNTVkdJRF82XyIpOwogICAgfQogICAgLnN0NyB7CiAgICAgIGZpbGw6ICM5NDFCMUU7CiAgICB9CiAgICAuc3Q4IHsKICAgICAgZmlsbDogI0IxMjczOTsKICAgIH0KICAgIC5zdDkgewogICAgICBmaWxsOiAjOTUyNDMyOwogICAgfQogICAgLnN0MTAgewogICAgICBmaWxsOiAjRDQyMDI3OwogICAgfQogICAgLnN0MTEgewogICAgICBmaWxsOiB1cmwoIiNTVkdJRF83XyIpOwogICAgfQogICAgLnN0MTIgewogICAgICBmaWxsOiB1cmwoIiNTVkdJRF84XyIpOwogICAgfQogICAgLnN0MTMgewogICAgICBmaWxsOiAjMUM5QTQ4OwogICAgfQogICAgLnN0MTQgewogICAgICBmaWxsOiB1cmwoIiNTVkdJRF85XyIpOwogICAgfQogICAgLnN0MTUgewogICAgICBmaWxsOiB1cmwoIiNTVkdJRF8xMF8iKTsKICAgIH0KICAgIC5zdDE2IHsKICAgICAgZmlsbDogIzJBMzg4NjsKICAgIH0KICAgIC5zdDE3IHsKICAgICAgZmlsbDogdXJsKCIjU1ZHSURfMTFfIik7CiAgICB9CiAgICAuc3QxOCB7CiAgICAgIGZpbGw6IHVybCgiI1NWR0lEXzEyXyIpOwogICAgfQogICAgLnN0MTkgewogICAgICBmaWxsOiB1cmwoIiNTVkdJRF8xM18iKTsKICAgIH0KICAgIC5zdDIwIHsKICAgICAgZmlsbDogdXJsKCIjU1ZHSURfMTRfIik7CiAgICB9CiAgPC9zdHlsZT4KICA8ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMS41NSwtMy4zKSI+CiAgICA8bGluZWFyR3JhZGllbnQgaWQ9IlNWR0lEXzFfIiB5Mj0iLTMyLjY2MyIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiIHkxPSItMzIuNjYzIiBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDEgMCAwIC0xIC0xMTguOTggMTIwLjU0KSIgeDI9Ijg0Mi4wOCIgeDE9IjExOC45OCI+PHN0b3Agc3RvcC1jb2xvcj0iIzk1MjMzMSIgb2Zmc2V0PSIwIi8+PHN0b3Agc3RvcC1jb2xvcj0iIzkyMUMxRCIgb2Zmc2V0PSIuMDM2MDk0Ii8+PHN0b3Agc3RvcC1jb2xvcj0iI0IwMjczOSIgb2Zmc2V0PSIuMDg0NjQ5Ii8+PHN0b3Agc3RvcC1jb2xvcj0iI0FEMUYyNCIgb2Zmc2V0PSIuMTIzNyIvPjxzdG9wIHN0b3AtY29sb3I9IiNDNzIwMjYiIG9mZnNldD0iLjE1MDkiLz48c3RvcCBzdG9wLWNvbG9yPSIjRDQyMDI3IiBvZmZzZXQ9Ii4xNjk3Ii8+PHN0b3Agc3RvcC1jb2xvcj0iI0NDMjQzMSIgb2Zmc2V0PSIuMTc1OCIvPjxzdG9wIHN0b3AtY29sb3I9IiNCNzJCNEMiIG9mZnNldD0iLjE4ODgiLz48c3RvcCBzdG9wLWNvbG9yPSIjOTUzMzcxIiBvZmZzZXQ9Ii4yMDc0Ii8+PHN0b3Agc3RvcC1jb2xvcj0iIzg4MzU3RiIgb2Zmc2V0PSIuMjE0MiIvPjxzdG9wIHN0b3AtY29sb3I9IiM4NTM2ODEiIG9mZnNldD0iLjI0MzYiLz48c3RvcCBzdG9wLWNvbG9yPSIjNkYzNjhCIiBvZmZzZXQ9Ii4yNjM4Ii8+PHN0b3Agc3RvcC1jb2xvcj0iIzM5NDI4RiIgb2Zmc2V0PSIuMjkxMSIvPjxzdG9wIHN0b3AtY29sb3I9IiMyMzNEN0QiIG9mZnNldD0iLjMyNDIiLz48c3RvcCBzdG9wLWNvbG9yPSIjMzIyQzZGIiBvZmZzZXQ9Ii40MTgxIi8+PHN0b3Agc3RvcC1jb2xvcj0iIzJBMzg4NSIgb2Zmc2V0PSIuNDk0Ii8+PHN0b3Agc3RvcC1jb2xvcj0iIzFENjJBMSIgb2Zmc2V0PSIuNTU4MSIvPjxzdG9wIHN0b3AtY29sb3I9IiMyNzZDQTUiIG9mZnNldD0iLjU3MDIiLz48c3RvcCBzdG9wLWNvbG9yPSIjNDM4RUIzIiBvZmZzZXQ9Ii42MTAzIi8+PHN0b3Agc3RvcC1jb2xvcj0iIzU1QTVCQyIgb2Zmc2V0PSIuNjM5OSIvPjxzdG9wIHN0b3AtY29sb3I9IiM1Q0FGQkYiIG9mZnNldD0iLjY1NTYiLz48c3RvcCBzdG9wLWNvbG9yPSIjNTZBQkJEIiBvZmZzZXQ9Ii42Nzc3Ii8+PHN0b3Agc3RvcC1jb2xvcj0iIzQzOUZCOCIgb2Zmc2V0PSIuNzA1OCIvPjxzdG9wIHN0b3AtY29sb3I9IiMxODhFQUYiIG9mZnNldD0iLjczNzIiLz48c3RvcCBzdG9wLWNvbG9yPSIjMDM4QkFFIiBvZmZzZXQ9Ii43NDI2Ii8+PHN0b3Agc3RvcC1jb2xvcj0iIzA2OTI5MiIgb2Zmc2V0PSIuNzg5OCIvPjxzdG9wIHN0b3AtY29sb3I9IiMwNUExNEIiIG9mZnNldD0iLjg4NzUiLz48c3RvcCBzdG9wLWNvbG9yPSIjMDM5MjdFIiBvZmZzZXQ9IjEiLz48L2xpbmVhckdyYWRpZW50PjxyZWN0IHdpZHRoPSI3MjMuMSIgeT0iMCIgeD0iMCIgaGVpZ2h0PSIzMDYuNCIgY2xhc3M9InN0MCIgZmlsbD0idXJsKCNTVkdJRF8xXykiLz4KICAgIDxsaW5lYXJHcmFkaWVudCBpZD0iU1ZHSURfMl8iIHkyPSItMTA5LjI2IiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeTE9Ii0xMDkuMjYiIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoMSAwIDAgLTEgLTExOC45OCAxMjAuNTQpIiB4Mj0iMjM1Ljk4IiB4MT0iMzI1LjA4Ij48c3RvcCBzdG9wLWNvbG9yPSIjODkzNjgwIiBvZmZzZXQ9IjAiLz48c3RvcCBzdG9wLWNvbG9yPSIjODkzNjgwIiBvZmZzZXQ9Ii4zMzU0Ii8+PHN0b3Agc3RvcC1jb2xvcj0iIzhEMzE2RCIgb2Zmc2V0PSIuNTAyNSIvPjxzdG9wIHN0b3AtY29sb3I9IiM5MDI5NEQiIG9mZnNldD0iLjgzOTgiLz48c3RvcCBzdG9wLWNvbG9yPSIjOTAyNTQxIiBvZmZzZXQ9IjEiLz48L2xpbmVhckdyYWRpZW50Pjxwb2x5Z29uIHBvaW50cz0iMTc1LjEgMTUzLjIgMTE3IDMwNi40IDIwNi4xIDMwNi40IiBmaWxsPSJ1cmwoI1NWR0lEXzJfKSIgY2xhc3M9InN0MSIvPgogICAgPGxpbmVhckdyYWRpZW50IGlkPSJTVkdJRF8zXyIgeTI9Ii04Mi4yODQiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIiB5MT0iMTIwLjI0IiBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDEgMCAwIC0xIC0xMTguOTggMTIwLjU0KSIgeDI9IjQ0Ni41NSIgeDE9IjQ3OC45MyI+PHN0b3Agc3RvcC1jb2xvcj0iIzMyMkM2RiIgb2Zmc2V0PSIwIi8+PHN0b3Agc3RvcC1jb2xvcj0iIzMyMkM2RiIgb2Zmc2V0PSIuMjQyNyIvPjxzdG9wIHN0b3AtY29sb3I9IiMzMDJGNzIiIG9mZnNldD0iLjQ1OTkiLz48c3RvcCBzdG9wLWNvbG9yPSIjMkEzQTdFIiBvZmZzZXQ9Ii43MTU1Ii8+PHN0b3Agc3RvcC1jb2xvcj0iIzE1NEE5MyIgb2Zmc2V0PSIuOTg5NiIvPjxzdG9wIHN0b3AtY29sb3I9IiMxMzRCOTQiIG9mZnNldD0iMSIvPjwvbGluZWFyR3JhZGllbnQ+PHBvbHlnb24gcG9pbnRzPSIyODguNCAxNTMuMiAzMTAuNyAzMDYuNCAzNTguMSAzMDYuNCAzNTguMSAwIDMxMi45IDAiIGZpbGw9InVybCgjU1ZHSURfM18pIiBjbGFzcz0ic3QyIi8+CiAgICA8bGluZWFyR3JhZGllbnQgaWQ9IlNWR0lEXzRfIiB5Mj0iLTMyLjY2MyIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiIHkxPSItMzIuNjYzIiBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDEgMCAwIC0xIC0xMTguOTggMTIwLjU0KSIgeDI9IjM3Mi44OCIgeDE9IjI5NC4wOCI+PHN0b3Agc3RvcC1jb2xvcj0iIzZGMzc4RCIgb2Zmc2V0PSIwIi8+PHN0b3Agc3RvcC1jb2xvcj0iIzNBNDI5MSIgb2Zmc2V0PSIxIi8+PC9saW5lYXJHcmFkaWVudD48cG9seWdvbiBwb2ludHM9IjE3NS4xIDE1My4yIDIwNi4xIDMwNi40IDI1My45IDE1My4yIDIwOS40IDAgMjA5LjQgMCIgZmlsbD0idXJsKCNTVkdJRF80XykiIGNsYXNzPSJzdDMiLz4KICAgIDxsaW5lYXJHcmFkaWVudCBpZD0iU1ZHSURfNV8iIHkyPSItMzIuNjYzIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeTE9Ii0zMi42NjMiIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoMSAwIDAgLTEgLTExOC45OCAxMjAuNTQpIiB4Mj0iMzI1LjA4IiB4MT0iNDMxLjg4Ij48c3RvcCBzdG9wLWNvbG9yPSIjMjMzRDdEIiBvZmZzZXQ9IjAiLz48c3RvcCBzdG9wLWNvbG9yPSIjMjkzRDdEIiBvZmZzZXQ9Ii4yNDk1Ii8+PHN0b3Agc3RvcC1jb2xvcj0iIzNBM0M4MCIgb2Zmc2V0PSIuNTQ0NiIvPjxzdG9wIHN0b3AtY29sb3I9IiM1MTNCODQiIG9mZnNldD0iLjg2MTYiLz48c3RvcCBzdG9wLWNvbG9yPSIjNUQzQTg2IiBvZmZzZXQ9IjEiLz48L2xpbmVhckdyYWRpZW50Pjxwb2x5Z29uIHBvaW50cz0iMjUzLjkgMTUzLjIgMjA2LjEgMzA2LjQgMzEwLjcgMzA2LjQgMjg4LjQgMTUzLjIgMzEyLjkgMCAyMDkuNCAwIiBmaWxsPSJ1cmwoI1NWR0lEXzVfKSIgY2xhc3M9InN0NCIvPjxwb2x5Z29uIHBvaW50cz0iMTE2LjEgMCA1NS43IDAgNTUuNyA5NC44IDg5LjkgMTUzLjIgNTUuNyAyMTEuNiA1NS43IDMwNi40IDExNyAzMDYuNCA5NS4yIDE1My4yIiBmaWxsPSIjYWYyMDI0IiBjbGFzcz0ic3Q1Ii8+CiAgICA8bGluZWFyR3JhZGllbnQgaWQ9IlNWR0lEXzZfIiB5Mj0iNDMuOTM3IiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeTE9IjQzLjkzNyIgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgxIDAgMCAtMSAtMTE4Ljk4IDEyMC41NCkiIHgyPSIyMzIuNjciIHgxPSIzMjkuMTEiPjxzdG9wIHN0b3AtY29sb3I9IiM4OTM2ODAiIG9mZnNldD0iMCIvPjxzdG9wIHN0b3AtY29sb3I9IiM4OTM2ODAiIG9mZnNldD0iLjMzNTQiLz48c3RvcCBzdG9wLWNvbG9yPSIjOEQzMTZEIiBvZmZzZXQ9Ii41MDI1Ii8+PHN0b3Agc3RvcC1jb2xvcj0iIzkwMjk0RCIgb2Zmc2V0PSIuODM5OCIvPjxzdG9wIHN0b3AtY29sb3I9IiM5MDI1NDEiIG9mZnNldD0iMSIvPjwvbGluZWFyR3JhZGllbnQ+PHBvbHlnb24gcG9pbnRzPSIxNzUuMSAxNTMuMiAyMDkuNCAwIDExNi4xIDAiIGZpbGw9InVybCgjU1ZHSURfNl8pIiBjbGFzcz0ic3Q2Ii8+PHBvbHlnb24gcG9pbnRzPSI1NS43IDk0LjggNTUuNyAwIDAgMCIgZmlsbD0iIzk0MWIxZSIgY2xhc3M9InN0NyIvPjxwb2x5Z29uIHBvaW50cz0iNTUuNyAyMTEuNiA4OS45IDE1My4yIDU1LjcgOTQuOCIgZmlsbD0iI2IxMjczOSIgY2xhc3M9InN0OCIvPjxwb2x5Z29uIHBvaW50cz0iNTUuNyAyMTEuNiAwIDMwNi40IDU1LjcgMzA2LjQiIGZpbGw9IiM5NDFiMWUiIGNsYXNzPSJzdDciLz48cG9seWdvbiBwb2ludHM9IjU1LjcgOTQuOCAwIDAgMCAzMDYuNCA1NS43IDIxMS42IiBmaWxsPSIjOTUyNDMyIiBjbGFzcz0ic3Q5Ii8+PHBvbHlnb24gcG9pbnRzPSIxMTYuMSAwIDk1LjIgMTUzLjIgMTE3IDMwNi40IDE3NS4xIDE1My4yIiBmaWxsPSIjZDQyMDI3IiBjbGFzcz0ic3QxMCIvPgogICAgPGxpbmVhckdyYWRpZW50IGlkPSJTVkdJRF83XyIgeTI9Ii0xODYuMDYiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIiB5MT0iMTIwLjQ0IiBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDEgMCAwIC0xIC0xMTguOTggMTIwLjU0KSIgeDI9Ijc0OC45NiIgeDE9Ijc0OC45NiI+PHN0b3Agc3RvcC1jb2xvcj0iIzk0QkU1NSIgb2Zmc2V0PSIwIi8+PHN0b3Agc3RvcC1jb2xvcj0iIzkzQkQ1OCIgb2Zmc2V0PSIuMDQ0MzQwIi8+PHN0b3Agc3RvcC1jb2xvcj0iIzhCQkM2QSIgb2Zmc2V0PSIuMzg5MSIvPjxzdG9wIHN0b3AtY29sb3I9IiM4NkJDNzUiIG9mZnNldD0iLjcxNDkiLz48c3RvcCBzdG9wLWNvbG9yPSIjODRCQzc5IiBvZmZzZXQ9IjEiLz48L2xpbmVhckdyYWRpZW50PjxwYXRoCiAgICAgIGQ9Im02NDEuNiAyNTkuNmMxLjctMjUuNCAxMC01NC42IDE4LjgtODUuNiAxLjQtNSAyLjgtMTAgNC4yLTE1LjEtMS40LTUuNS0yLjgtMTAuOS00LjItMTYuMi04LjgtMzMuMy0xNy02NC43LTE4LjgtOTItMS40LTIxLjIgMS40LTM3IDguOS01MC42aC00NS45Yy03LjUgMTguMy0xMC4zIDI5LjEtOC45IDUwLjMgMS43IDI3LjMgMTAgNTguNyAxOC44IDkyIDEzIDQ5LjMgMjggMTA2LjIgMjMuMiAxNjQuMmgxMi45Yy03LjYtMTIuOC0xMC40LTI3LjMtOS00N3oiCiAgICAgIGNsYXNzPSJzdDExIgogICAgICBmaWxsPSJ1cmwoI1NWR0lEXzdfKSIvPgogICAgPGxpbmVhckdyYWRpZW50IGlkPSJTVkdJRF84XyIgeTI9Ii0xODQuNDUiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIiB5MT0iMTE3LjI5IiBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDEgMCAwIC0xIC0xMTguOTggMTIwLjU0KSIgeDI9IjczMy40OSIgeDE9IjY1My43NiI+PHN0b3Agc3RvcC1jb2xvcj0iIzA4QTI0QiIgb2Zmc2V0PSIwIi8+PHN0b3Agc3RvcC1jb2xvcj0iIzBBQTE0RSIgb2Zmc2V0PSIuMTY3OCIvPjxzdG9wIHN0b3AtY29sb3I9IiMwQjlFNTciIG9mZnNldD0iLjQwNDciLz48c3RvcCBzdG9wLWNvbG9yPSIjMDk5QTY3IiBvZmZzZXQ9Ii42ODI3Ii8+PHN0b3Agc3RvcC1jb2xvcj0iIzA0OTQ3RCIgb2Zmc2V0PSIuOTg5OCIvPjxzdG9wIHN0b3AtY29sb3I9IiMwNDkzN0UiIG9mZnNldD0iMSIvPjwvbGluZWFyR3JhZGllbnQ+PHBhdGggZD0ibTYxNC41IDE0Mi4zYy04LjgtMzMuMy0xNy02NC43LTE4LjgtOTItMS40LTIxLjIgMS40LTMyIDguOS01MC4zaC0zNS40YzUuNyA1My45LTMuOCAxMDYuNy0xMy42IDE2Ni44LTUuNyAzNS0xMS43IDcxLjMtMTMuMiAxMDAuNi0xLjEgMjEuMSAwLjQgMzIuOCAxLjggMzloOTMuNWM0LjgtNTcuOS0xMC4zLTExNC44LTIzLjItMTY0LjF6IiBjbGFzcz0ic3QxMiIgZmlsbD0idXJsKCNTVkdJRF84XykiLz48cGF0aCBjbGFzcz0ic3QxMyIgZmlsbD0iIzFjOWE0OCIgZD0ibTY2NC42IDE1OC45Yy0xLjQgNS4xLTIuOCAxMC4xLTQuMiAxNS4xLTguOCAzMS0xNyA2MC4yLTE4LjggODUuNi0xLjQgMTkuNyAxLjQgMzQuMiA5IDQ2LjloMzNjNC4yLTUxLjgtNy4yLTEwMi4zLTE5LTE0Ny42eiIvPgogICAgPGxpbmVhckdyYWRpZW50IGlkPSJTVkdJRF85XyIgeTI9Ii0xODUuOTYiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIiB5MT0iMTIwLjU0IiBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDEgMCAwIC0xIC0xMTguOTggMTIwLjU0KSIgeDI9IjgxMi44MyIgeDE9IjgxMi44MyI+PHN0b3Agc3RvcC1jb2xvcj0iIzY5QTA2MCIgb2Zmc2V0PSIwIi8+PHN0b3Agc3RvcC1jb2xvcj0iIzYzOUQ1QyIgb2Zmc2V0PSIuMDM5ODk1Ii8+PHN0b3Agc3RvcC1jb2xvcj0iIzRDOTQ0RiIgb2Zmc2V0PSIuMjE5MiIvPjxzdG9wIHN0b3AtY29sb3I9IiMzNzhFNDciIG9mZnNldD0iLjQxODQiLz48c3RvcCBzdG9wLWNvbG9yPSIjMjk4QjQ0IiBvZmZzZXQ9Ii42NTE1Ii8+PHN0b3Agc3RvcC1jb2xvcj0iIzIzOEE0MyIgb2Zmc2V0PSIxIi8+PC9saW5lYXJHcmFkaWVudD48cGF0aCBkPSJtNjgwLjUgMGMxMC43IDU1LjMtMi41IDExMC40LTE1LjkgMTU4LjkgMTEuNyA0NS4zIDIzLjIgOTUuOCAxOC45IDE0Ny42aDM5LjZ2LTMwNi41aC00Mi42eiIgY2xhc3M9InN0MTQiIGZpbGw9InVybCgjU1ZHSURfOV8pIi8+CiAgICA8bGluZWFyR3JhZGllbnQgaWQ9IlNWR0lEXzEwXyIgeTI9Ii0xODUuODYiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIiB5MT0iMTIwLjU0IiBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDEgMCAwIC0xIC0xMTguOTggMTIwLjU0KSIgeDI9IjY1Mi40NSIgeDE9IjY1Mi40NSI+PHN0b3Agc3RvcC1jb2xvcj0iIzA1QjVEQyIgb2Zmc2V0PSIwIi8+PHN0b3Agc3RvcC1jb2xvcj0iIzA0QjBENyIgb2Zmc2V0PSIuMjE5NyIvPjxzdG9wIHN0b3AtY29sb3I9IiMwNUE0QzkiIG9mZnNldD0iLjUzNzEiLz48c3RvcCBzdG9wLWNvbG9yPSIjMDU5MUI0IiBvZmZzZXQ9Ii45MTIyIi8+PHN0b3Agc3RvcC1jb2xvcj0iIzA1OENBRSIgb2Zmc2V0PSIxIi8+PC9saW5lYXJHcmFkaWVudD48cGF0aCBkPSJtNTQyLjMgMjY3LjRjMS41LTI5LjQgNy41LTY1LjYgMTMuMi0xMDAuNiA5LjgtNjAuMSAxOS4zLTExMi44IDEzLjYtMTY2LjhoLTcwLjhjLTEuNCAxMS40LTIuOSAxOS4yLTEuOCA0MS44IDEuNSAzMS42IDcuNSA3MC41IDEzLjIgMTA4LjIgOC40IDU1LjQgMTYuNiAxMDguOCAxNS4xIDE1Ni40aDE5LjJjLTEuMy02LjItMi44LTE3LjktMS43LTM5eiIgY2xhc3M9InN0MTUiIGZpbGw9InVybCgjU1ZHSURfMTBfKSIvPjxwb2x5Z29uIHBvaW50cz0iMzc1LjcgMTUzLjIgMzU4LjEgMCAzNTguMSAzMDYuNCIgZmlsbD0iIzJhMzg4NiIgY2xhc3M9InN0MTYiLz4KICAgIDxsaW5lYXJHcmFkaWVudCBpZD0iU1ZHSURfMTFfIiB5Mj0iNzcuMTM2IiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeTE9Ii00LjMyODEiIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoMSAwIDAgLTEgLTExOC45OCAxMjAuNTQpIiB4Mj0iNzk2LjcxIiB4MT0iNzUxLjA1Ij48c3RvcCBzdG9wLWNvbG9yPSIjNjJCMTZFIiBvZmZzZXQ9IjAiLz48c3RvcCBzdG9wLWNvbG9yPSIjODdCOTU3IiBvZmZzZXQ9IjEiLz48L2xpbmVhckdyYWRpZW50PjxwYXRoIGQ9Im02NDEuNiA1MC42YzEuNyAyNy4zIDEwIDU4LjcgMTguOCA5MiAxLjQgNS4zIDIuOCAxMC43IDQuMiAxNi4yIDEzLjUtNDguNCAyNi42LTEwMy41IDE1LjktMTU4LjhoLTMwYy03LjUgMTMuNi0xMC4zIDI5LjQtOC45IDUwLjZ6IiBjbGFzcz0ic3QxNyIgZmlsbD0idXJsKCNTVkdJRF8xMV8pIi8+CiAgICA8bGluZWFyR3JhZGllbnQgaWQ9IlNWR0lEXzEyXyIgeTI9Ii0xODkuMjgiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIiB5MT0iMTEzLjcxIiBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDEgMCAwIC0xIC0xMTguOTggMTIwLjU0KSIgeDI9IjYzMS41OSIgeDE9IjU1MC40Ij48c3RvcCBzdG9wLWNvbG9yPSIjMDY5QUQ0IiBvZmZzZXQ9IjAiLz48c3RvcCBzdG9wLWNvbG9yPSIjMzBBMENFIiBvZmZzZXQ9Ii4zNTI1Ii8+PHN0b3Agc3RvcC1jb2xvcj0iIzVCQjBDMCIgb2Zmc2V0PSIxIi8+PC9saW5lYXJHcmFkaWVudD48cGF0aCBkPSJtNTA5LjggMTUwYy01LjctMzcuNy0xMS43LTc2LjYtMTMuMi0xMDguMi0xLjEtMjIuNyAwLjQtMzAuNCAxLjgtNDEuOGgtNDEuNWMxLjUgNDAuMS0xLjUgODUuMy03IDE2MC44LTMuMSA0My41LTggMTEwLjUtNyAxNDUuN2g4Mi4xYzEuNC00Ny43LTYuOC0xMDEuMS0xNS4yLTE1Ni41eiIgY2xhc3M9InN0MTgiIGZpbGw9InVybCgjU1ZHSURfMTJfKSIvPgogICAgPGxpbmVhckdyYWRpZW50IGlkPSJTVkdJRF8xM18iIHkyPSItMTg1Ljg2IiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeTE9IjEyMC41NCIgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgxIDAgMCAtMSAtMTE4Ljk4IDEyMC41NCkiIHgyPSI1MDUuMzMiIHgxPSI1MDUuMzMiPjxzdG9wIHN0b3AtY29sb3I9IiMxRTQ1OEUiIG9mZnNldD0iMCIvPjxzdG9wIHN0b3AtY29sb3I9IiMxRjRGOTYiIG9mZnNldD0iLjI0MTEiLz48c3RvcCBzdG9wLWNvbG9yPSIjMkI2QUFCIiBvZmZzZXQ9Ii43MjkyIi8+PHN0b3Agc3RvcC1jb2xvcj0iIzMzN0JCOSIgb2Zmc2V0PSIxIi8+PC9saW5lYXJHcmFkaWVudD48cG9seWdvbiBwb2ludHM9IjM1OC4xIDMwNi40IDQxNC42IDMwNi40IDQxNC42IDAgMzU4LjEgMCAzNzUuNyAxNTMuMiIgZmlsbD0idXJsKCNTVkdJRF8xM18pIiBjbGFzcz0ic3QxOSIvPgogICAgPGxpbmVhckdyYWRpZW50IGlkPSJTVkdJRF8xNF8iIHkyPSIxMjAuNTQiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIiB5MT0iLTE4NS44NiIgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgxIDAgMCAtMSAtMTE4Ljk4IDEyMC41NCkiIHgyPSI1NTQuOTIiIHgxPSI1NTQuOTIiPjxzdG9wIHN0b3AtY29sb3I9IiMzRjlBQzkiIG9mZnNldD0iMCIvPjxzdG9wIHN0b3AtY29sb3I9IiMyMDYyQTIiIG9mZnNldD0iMSIvPjwvbGluZWFyR3JhZGllbnQ+PHBhdGggZD0ibTQ0OS45IDE2MC44YzUuNS03NS41IDguNS0xMjAuNiA3LTE2MC44aC00Mi4ybC0wLjEgMzA2LjRoMjguM2MtMS0zNS4xIDMuOC0xMDIuMSA3LTE0NS42eiIgY2xhc3M9InN0MjAiIGZpbGw9InVybCgjU1ZHSURfMTRfKSIvPjwvZz4KPC9zdmc+Cg==);
+}
+
+body:after {
+ position: absolute;
+ right: 25px;
+ top: 36px;
+ width: 135px;
+ height: 48px;
+ content: '';
+ background-repeat: no-repeat;
+ background-size: cover;
+ background-image: url(/static/img/bosch-logo.svg);
+}
+
+.swagger-ui {
+ font-family: "Bosch Sans", sans-serif;
+}
+
+/*Remove topbar*/
+.swagger-ui .topbar {
+ display: none
+}
+
+/*Remove models view*/
+.swagger-ui .models {
+ display: none;
+}
+
+/*Remove application/json select*/
+.swagger-ui .opblock .opblock-section-header > label, .swagger-ui .response-controls {
+ display: none;
+}
+
+/*Remove border radius*/
+.swagger-ui .opblock, .swagger-ui .opblock .opblock-summary-method, .swagger-ui select {
+ border-radius: 0;
+ box-shadow: none;
+}
+
+/*remove links in response*/
+.swagger-ui .response-col_links {
+ display: none;
+}
+
+/*remove version*/
+.swagger-ui .info .title span {
+ display: none;
+}
+
+/*separator before methods*/
+.swagger-ui .scheme-container {
+ box-shadow: none;
+ border-bottom: 1px solid var(--light-grey);
+}
+
+/*tag separator*/
+.swagger-ui .opblock-tag {
+ border-bottom: 1px solid var(--light-grey);
+}
+
+/*parameters/responses bar*/
+.swagger-ui .opblock .opblock-section-header {
+ box-shadow: none;
+ background: #fff;
+}
+
+/*select*/
+.swagger-ui select {
+ background-color: var(--light-grey-w75);
+ border: none;
+ height: 36px;
+}
+
+/*button*/
+.swagger-ui .btn {
+ border-radius: 0;
+ box-shadow: none;
+}
+
+.swagger-ui .btn:hover {
+ box-shadow: none;
+}
+
+/*authorize button */
+.swagger-ui .btn.authorize {
+ color: var(--light-green);
+ border-color: var(--light-green);
+}
+
+.swagger-ui .btn.authorize svg {
+ fill: var(--light-green);
+}
+
+/*auth inputs*/
+.swagger-ui .auth-container input[type="password"], .swagger-ui .auth-container input[type="text"] {
+ border-radius: 0;
+ box-shadow: none;
+ border-color: var(--light-grey);
+}
+
+.swagger-ui .dialog-ux .modal-ux {
+ border-radius: 0;
+}
+
+/*cancel button*/
+.swagger-ui .btn.cancel {
+ color: var(--red);
+ border-color: var(--red);
+}
+
+/*download button*/
+.swagger-ui .download-contents {
+ border-radius: 0;
+ height: 28px;
+ width: 80px;
+}
+
+/*model*/
+.swagger-ui .model-box {
+ border-radius: 0;
+}
+
+/*execute button*/
+.swagger-ui .btn.execute {
+ background-color: var(--dark-blue);
+ border-color: var(--dark-blue);
+ height: 30px;
+ line-height: 0.7;
+}
+
+.swagger-ui .btn-group .btn:last-child {
+ border-radius: 0;
+ height: 30px;
+ border-color: var(--dark-blue);
+}
+
+.swagger-ui .btn-group .btn:first-child {
+ border-radius: 0;
+}
+
+.swagger-ui .btn-group {
+ padding: 0 20px;
+}
+
+/*parameter input*/
+.swagger-ui .parameters-col_description input[type="text"] {
+ border-radius: 0;
+}
+
+/*required label*/
+.swagger-ui .parameter__name.required > span {
+ color: var(--red) !important;
+}
+
+.swagger-ui .parameter__name.required::after {
+ color: var(--red);
+}
+/*Remove colored parameters bar*/
+.swagger-ui .opblock.opblock-get .tab-header .tab-item.active h4 span::after, .swagger-ui .opblock.opblock-post .tab-header .tab-item.active h4 span::after, .swagger-ui .opblock.opblock-put .tab-header .tab-item.active h4 span::after, .swagger-ui .opblock.opblock-delete .tab-header .tab-item.active h4 span::after {
+ background: none;
+}
+
+/*code*/
+.swagger-ui .opblock-body pre.microlight {
+ border-radius: 0;
+}
+
+.swagger-ui .highlight-code > .microlight {
+ min-height: 0;
+}
+
+/*request body*/
+.swagger-ui textarea {
+ border-radius: 0;
+}
+
+/*parameters smaller padding*/
+.swagger-ui .execute-wrapper {
+ padding-top: 0;
+ padding-bottom: 0;
+}
+
+.swagger-ui .btn.execute {
+ margin-bottom: 20px;
+}
+
+.swagger-ui .opblock-description-wrapper {
+ margin-top: 20px;
+}
+
+.swagger-ui .opblock-description-wrapper {
+ margin-top: 5px;
+}
+
+.opblock-section .opblock-section-request-body > div > div {
+ padding-top: 18px;
+}
+
+/*response element positions*/
+.swagger-ui .model-example {
+ position: relative;
+ margin-top: 0;
+}
+
+.swagger-ui .tab {
+ position: absolute;
+ top: -35px;
+ right: 0;
+}
+
+.swagger-ui table tbody tr td {
+ padding: 0;
+}
+
+.swagger-ui .renderedMarkdown p {
+ margin: 8px auto;
+}
+
+/*Method colors*/
+.swagger-ui .opblock.opblock-get .opblock-summary-method {
+ background: var(--dark-blue);
+}
+
+.swagger-ui .opblock.opblock-get .opblock-summary {
+ border-color: var(--dark-blue);
+}
+
+.swagger-ui .opblock.opblock-get {
+ background: var(--dark-blue-w75);
+ border-color: var(--dark-blue);
+}
+
+.swagger-ui .opblock.opblock-post .opblock-summary-method {
+ background: var(--dark-green);
+}
+
+.swagger-ui .opblock.opblock-post .opblock-summary {
+ border-color: var(--dark-green);
+}
+
+.swagger-ui .opblock.opblock-post {
+ background: var(--dark-green-w75);
+ border-color: var(--dark-green);
+}
+
+.swagger-ui .opblock.opblock-put .opblock-summary-method {
+ background: var(--turquoise);
+}
+
+.swagger-ui .opblock.opblock-put .opblock-summary {
+ border-color: var(--turquoise);
+}
+
+.swagger-ui .opblock.opblock-put {
+ background: var(--turquoise-w75);
+ border-color: var(--turquoise);
+}
+
+.swagger-ui .opblock.opblock-delete .opblock-summary-method {
+ background: var(--fuchsia);
+}
+
+.swagger-ui .opblock.opblock-delete .opblock-summary {
+ border-color: var(--fuchsia);
+}
+
+.swagger-ui .opblock.opblock-delete {
+ background: var(--fuchsia-w75);
+ border-color: var(--fuchsia);
+}
\ No newline at end of file
diff --git a/tsconfig.json b/tsconfig.json
index c49a622..b43a5fb 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -4,13 +4,21 @@
"target": "es5",
"outDir": "dist",
"sourceMap": true,
- "esModuleInterop": true
+ "esModuleInterop": true,
+ "resolveJsonModule": true,
+ "incremental": true,
+ "diagnostics": true,
+ "typeRoots": [
+ "src/customTypings",
+ "node_modules/@types"
+ ]
},
"files": [
"./node_modules/@types/node/index.d.ts"
],
"include": [
- "src/**/*.ts"
+ "src/**/*.ts",
+ "src/**/*.json"
],
"exclude": [
"node_modules"