108 Commits

Author SHA1 Message Date
=
757d60e2d3 hw 2 done 2026-03-13 17:50:03 +10:00
=
ed21fb4694 format document 2026-03-13 17:13:12 +10:00
=
4bd2a92a0e spiral path added 2026-03-13 17:11:48 +10:00
=
c7f1192b41 task done 2026-03-13 17:01:25 +10:00
=
58b398228f removed line 2026-03-13 14:13:02 +10:00
300d5664cb adding svg to site 2026-03-13 12:39:25 +10:00
52b800df5c lab done 2026-03-13 12:20:05 +10:00
c3a72893c3 simple transforms done 2026-03-13 11:56:43 +10:00
=
0f62e7ea49 lab2 init 2026-03-12 23:02:05 +10:00
=
64b82e3982 fixes 2026-03-06 14:23:21 +10:00
627f74838c sort working 2026-03-01 16:37:58 +10:00
4b6d65a946 filters working
adding sorting
2026-03-01 16:23:58 +10:00
e64b644b33 pug update to match data 2026-03-01 10:10:41 +10:00
f17768bb8e copied js files 2026-03-01 10:10:30 +10:00
4cf18c4895 sorting done 2026-02-27 11:00:27 +10:00
c3996ec6c6 filters done 2026-02-26 22:56:12 +10:00
4f1c66e838 moving all labs to old dir 2026-02-26 21:55:48 +10:00
76555d27a4 Faaaah (copy to edit) 2026-02-26 20:12:56 +10:00
9a219093a0 moved sem 1 labs to old folder 2026-02-26 20:12:26 +10:00
3dd2a6f734 Merge branch 'lectures-walkthrough' 2026-01-09 19:39:18 +10:00
54e4e9c2a1 lectures fix 2026-01-09 19:38:48 +10:00
5c751400cb longer lectures 2026-01-09 19:38:48 +10:00
051052a75f lectures done 2026-01-09 19:38:48 +10:00
6bbcec3411 duplicated solutions in loops to match themes 2026-01-09 19:38:48 +10:00
b2bc0651ec js lec 1 added 2026-01-09 19:38:48 +10:00
b0d3fa1ecf lec 3 done 2026-01-09 19:38:48 +10:00
3f748ba05b finished up to bootstrap 2026-01-09 19:38:48 +10:00
9faac87716 added readme for lectures lec2 done 2026-01-09 19:38:48 +10:00
f82e4a7bb3 lec 1 added 2026-01-09 19:38:48 +10:00
011e19e531 lab7 added 2026-01-09 19:36:55 +10:00
df21df0fff lectures fix 2026-01-09 19:35:08 +10:00
d10f15a34a longer lectures 2026-01-03 22:59:02 +10:00
f982d05a54 lectures done 2026-01-03 15:32:33 +10:00
b6776f93fd duplicated solutions in loops to match themes 2026-01-03 13:44:33 +10:00
e6806a2366 js lec 1 added 2026-01-03 13:40:15 +10:00
cca7d57569 lec 3 done 2026-01-03 01:21:46 +10:00
1c50d7ce0e finished up to bootstrap 2026-01-01 23:38:59 +10:00
b00d7fb34d added readme for lectures lec2 done 2026-01-01 20:05:31 +10:00
7818949863 lec 1 added 2026-01-01 18:08:32 +10:00
5d7ecb1f13 math fixed 2026-01-01 15:57:09 +10:00
9fef1e5b0d finishing touches 2026-01-01 15:24:08 +10:00
c09b7b5769 images fixed 2026-01-01 15:05:20 +10:00
cf80fd251e small refactor 2026-01-01 14:37:53 +10:00
46e3696d85 reset bug fixed 2026-01-01 14:17:34 +10:00
9e5950490a values checking added 2026-01-01 14:04:53 +10:00
26bc727758 tooltip for output fields added 2026-01-01 13:52:51 +10:00
04db81b55a math fixed 2025-12-31 22:13:22 +10:00
4ab5967abe basic structure created 2025-12-31 20:42:21 +10:00
63af3ed7b4 prettify 2025-12-29 14:06:51 +10:00
b2c005e22c transpose + reverse + index fix in lab6 2025-12-29 14:05:04 +10:00
4cf2f56749 jab6 bad rotation 2025-12-29 13:58:34 +10:00
b0c3660f16 moved files (again?) 2025-12-29 12:46:23 +10:00
b47e479b95 moved files 2025-12-29 12:46:22 +10:00
eb509fb846 Merge branch 'hw3' 2025-12-29 12:42:10 +10:00
9870335263 Merge branch 'lab6' 2025-12-29 12:25:45 +10:00
7cc781c0b9 gitignore added 2025-12-29 00:27:39 +10:00
8eaf1ed628 Merge branch 'hw3' 2025-12-29 00:18:51 +10:00
7b1c5b0a50 lab 6 done 2025-12-29 00:12:40 +10:00
4316ac1636 finished table controls 2025-12-29 00:12:40 +10:00
a4f511cb45 table added 2025-12-29 00:12:39 +10:00
dd9002a61b added table controls 2025-12-29 00:12:38 +10:00
e142436ed3 details BEM 2025-12-29 00:12:38 +10:00
38e70189b8 details page in progress 2025-12-29 00:12:38 +10:00
39d023d50b stylus mixins 2025-12-29 00:12:37 +10:00
05b624120d BEM naming 2025-12-29 00:12:37 +10:00
7a83c54165 main page working 2025-12-29 00:12:36 +10:00
c495c26320 added stylus template 2025-12-29 00:12:36 +10:00
3982a97dce updating styles 2025-12-29 00:12:36 +10:00
8f89dbe429 adding pages 2025-12-29 00:12:35 +10:00
98b74ae77d aboba mv node 2025-12-29 00:12:34 +10:00
bdc5a9d55c starting hw 2025-12-29 00:11:33 +10:00
8900cf3228 gitignore fix 2025-12-29 00:09:07 +10:00
4cc6b7e9d8 lab 6 done 2025-12-29 00:03:52 +10:00
73ea28eab6 finished table controls 2025-12-12 11:35:56 +10:00
ea0b091c8a table added 2025-12-12 10:57:38 +10:00
c53abdb678 added table controls 2025-12-12 10:10:30 +10:00
2b4a36c1ff details BEM 2025-12-11 22:26:18 +10:00
03e7b28167 details page in progress 2025-12-11 21:58:56 +10:00
a25b444c01 stylus mixins 2025-12-11 20:47:40 +10:00
d54afa7b2e BEM naming 2025-12-11 20:41:43 +10:00
01dfe8011d main page working 2025-12-11 19:59:17 +10:00
5f692a4708 gitignore added 2025-12-11 18:22:29 +10:00
b98abb94cc aboba commit 2025-12-10 12:31:34 +10:00
deeb9dbd5c added stylus template 2025-12-10 12:24:54 +10:00
2f44691a98 added lecture 3 2025-12-10 10:21:18 +10:00
36fb1653d3 lab5 finished 2025-11-28 12:01:09 +10:00
0bdaa800f6 nfww that was a fake 2025-11-28 11:05:07 +10:00
ebe6566b87 updated styling for lab 2025-11-28 10:52:14 +10:00
b653d69e77 updating styles 2025-11-28 10:15:15 +10:00
6720bfd450 adding pages 2025-11-28 01:31:50 +10:00
726b6e7adb aboba mv node 2025-11-28 01:23:28 +10:00
093d57a619 starting hw 2025-11-28 01:23:09 +10:00
d468d0efb4 added lab5 2025-11-28 00:47:07 +10:00
dc039301f6 margin fix for images 2025-11-27 22:07:43 +10:00
fa9ac16021 fixed images 2025-11-27 22:06:00 +10:00
e1e6e066ce added footer 2025-11-14 13:18:52 +10:00
977735a6a3 lab-4 2025-11-14 12:52:31 +10:00
58427fae25 basic structure added for lab4 2025-11-14 02:37:59 +10:00
654e9d2734 updated hw 2025-11-14 02:23:20 +10:00
9aadb0ce16 possibly added hw 2025-11-14 02:13:50 +10:00
b3ebd075b2 adding real lab4 2025-11-14 01:03:54 +10:00
c32a4a6fc2 OMAGAH O HELL NAH WTF MAAAN 2025-11-14 00:33:21 +10:00
c36e781e05 preparations for hw 2025-11-14 00:32:28 +10:00
dd9ee56a1a stray divs removed 2025-11-13 17:00:08 +10:00
80176d2a2e Merge branch 'lab4' 2025-11-13 16:56:40 +10:00
62f1791203 Merge branch 'hw3' 2025-11-13 16:47:36 +10:00
abc37ce5a0 hw3 done 2025-11-13 16:42:59 +10:00
a8f0326fa0 hw3 start 2025-10-31 10:12:28 +10:00
197 changed files with 25096 additions and 2974 deletions

4
.gitignore vendored Normal file
View File

@@ -0,0 +1,4 @@
**node_modules
**to_reform
.vscode
current_site/dist

View File

@@ -1,37 +0,0 @@
import csv
def create_html_table_from_csv(csv_path, html_path):
try:
with open(csv_path, 'r', newline='', encoding='utf-8') as csv_file:
reader = csv.reader(csv_file)
header = next(reader)
with open(html_path, 'w', encoding='utf-8') as html_file:
html_file.write('<table class="data-table">\n')
html_file.write('<thead>\n')
html_file.write('<tr>\n')
for column_header in header:
html_file.write(f'<th>{column_header}</th>\n')
html_file.write('</tr>\n')
html_file.write('</thead>\n')
html_file.write('<tbody>\n')
for row in reader:
html_file.write('<tr>\n')
for cell in row:
html_file.write(f'<td>{cell}</td>\n')
html_file.write('</tr>\n')
html_file.write('</tbody>\n')
html_file.write('</table>\n')
print(f"Successfully created {html_path} from {csv_path}")
except:
pass
if __name__ == '__main__':
csv_file_name = 'temp_sensor_data.csv'
html_file_name = 'test.html'
create_html_table_from_csv(csv_file_name, html_file_name)

6692
current_site/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

30
current_site/package.json Normal file
View File

@@ -0,0 +1,30 @@
{
"name": "template_pug",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"build": "webpack --mode production",
"serve": "webpack serve --open --mode development",
"dev": "webpack --mode development",
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"chokidar": "^4.0.3",
"css-loader": "^6.8.1",
"filemanager-webpack-plugin": "^8.0.0",
"glob": "^11.0.3",
"html-webpack-plugin": "^5.5.3",
"pug": "^2.0.4",
"pug-loader": "^2.4.0",
"style-loader": "^3.3.3",
"stylus": "^0.61.0",
"stylus-loader": "^7.1.3",
"webpack": "^5.89.0",
"webpack-cli": "^5.1.4",
"webpack-dev-server": "^4.15.1"
}
}

View File

@@ -0,0 +1,84 @@
block variables
-
var navbarItems = {
Home: './index.html',
Embedded: './embeded_details.html',
OpenCV: './opencv_details.html',
Photography: './photography-details.html',
Table: './table.html',
Institute: './images/institute.png',
"SVG Playground": './svg_playground.html'
};
var smallCards = [
{
header: 'Заголовок',
text: '3D printers are versatile tools used for creating prototypes, custom parts, and artistic designs. They work by layering materials.',
image: require('../images/16x10/3d-printer.jpeg'),
imageAlt: '3D Printer',
link: '#',
},
{
header: 'BLHeli ESCs',
text: 'BLHeli ESCs are electronic speed controllers designed for drones, offering smooth and precise motor control.',
image: require('../images/16x10/blheli-esc.jpeg'),
imageAlt: 'BLHeli ESC',
link: '#',
},
{
header: 'Raspberry Pi Nano',
text: 'Raspberry Pi Nano is a compact computer ideal for learning, prototyping, and IoT projects. It is highly energy-efficient.',
image: require('../images/16x10/rpi-nano.jpeg'),
imageAlt: 'Raspberry Pi Nano',
link: '#',
},
{
header: 'Smart Thermostats',
text: 'Smart thermostats help regulate home temperatures efficiently, saving energy and enhancing comfort with automation.',
image: require('../images/16x10/thermostat.jpeg'),
imageAlt: 'Smart Thermostat',
link: '#',
}
];
var galleryImages= [
{ src: require('../images/sqarucos.png'), alt: 'ESP32'},
{ src: require('../images/sqstm32.png'), alt: 'Arduino'},
{ src: require('../images/sqesp32.png'), alt: 'STM32'}
];
var locationFilters = [
{ id: 'loc_kitchen', value: 'Kitchen', label: 'Kitchen' },
{ id: 'loc_living', value: 'Living Room', label: 'Living Room' },
{ id: 'loc_balcony', value: 'Balcony', label: 'Balcony' },
{ id: 'loc_bedroom', value: 'Bedroom', label: 'Bedroom' }
];
var sensorTypeFilters = [
{ id: 'type_xiaomi', value: 'Xiaomi', label: 'Xiaomi' },
{ id: 'type_bmp220', value: 'Bmp220', label: 'Bmp220' },
{ id: 'type_scd30', value: 'Scd30(Internal)', label: 'Scd30(Internal)' },
{ id: 'type_bmp280', value: 'Bmp280', label: 'Bmp280' }
];
var sortOptions = [
{ value: '', label: '-- None --' },
{ value: 'SensorID', label: 'SensorID' },
{ value: 'Timestamp', label: 'Time' },
{ value: 'BatteryLevel', label: 'BatteryLevel' },
{ value: 'Temperature', label: 'Temperature' },
{ value: 'Humidity', label: 'Humidity' }
];
var axisOptions = [
{ id: 'x_time', value: 'Time', label: 'Time', checked: true },
{ id: 'x_battery', value: 'BatteryLevel', label: 'BatteryLevel', checked: false },
{ id: 'x_temperature', value: 'Temperature', label: 'Temperature', checked: false },
{ id: 'x_humidity', value: 'Humidity', label: 'Humidity', checked: false }
];
var valueOptions = [
{ id: 'val_temperature', value: 'AverageTemperature', label: 'Average Temperature', checked: true },
{ id: 'val_humidity', value: 'Humidity', label: 'Average Humidity', checked: false },
{ id: 'val_battery', value: 'BatteryLevel', label: 'Average BatteryLevel', checked: false }
]

View File

@@ -0,0 +1,24 @@
include mixins.pug
html
head
meta(charset="UTF-8")
block title
title Details Page
body
block navbar
+navbarMixin("")
.details
header.details__header
block header
h1 Default Header
.details__content
.details__text
block text
p Default text content
.details__gallery
block gallery
p No images available

View File

@@ -0,0 +1,112 @@
include data.pug
mixin navbarMixin(selectedItem)
.navbar
each navbarUrl,navbarName in navbarItems
if navbarName == selectedItem
a.navbar__link-wrapper(href=navbarUrl)
.navbar__item.navbar__item_selected #{navbarName}
else
a.navbar__link-wrapper(href=navbarUrl)
.navbar__item #{navbarName}
mixin infoCardsMinxin()
each cardData in smallCards
.small-card
.small-card__content
h3.small-card__header #{cardData.header}
p.small-card__text #{cardData.text}
a.small-card__link(href=cardData.link) Подробнее»
.small-card__image
img(src=cardData.image alt=cardData.imageAlt)
mixin galleryMixin()
each img in galleryImages
img.gallery__image(src=img.src alt=img.alt)
mixin filtersMixin()
details.filters
summary.filters__summary Filters
form(id='filters').filters__form
p
label(for='type') Type:
input(type='text' id='type')
p
label(for='name') Name:
input(type='text' id='name')
p
label(for='manufacturer') Manufacturer name:
input(type='text' id='manufacturer')
p
label(for='sizeFrom') Size:
| from
input(type='number' id='sizeFrom')
| to
input(type='number' id='sizeTo')
p
label(for='releaseDateFrom') Release Date:
| from
input(type='number' id='releaseDateFrom')
| to
input(type='number' id='releaseDateTo')
p
label(for='priceFrom') Release Date:
| from
input(type='number' id='priceFrom')
| to
input(type='number' id='priceTo')
input.filter-group__button--apply(type='button' id='applyFiltersButton' value="Apply Filters")
input.filter-group__button--reset(type='button' id='clearFiltersButton' value="Reset Filters")
mixin sortLevel(level)
p
select.sort-level__select(id=`sort_${level}`)
| ascending?
input(type="checkbox", id=`sort_${level}Desc`)
mixin sortingMixin()
- const sortLevels = ['1', '2', '3'];
details.sorting
summary.sorting__summary Sort
form(id='sorting')
each level in sortLevels
+sortLevel(level)
input.sort-group__button--apply(type='button' id='applySortButton' value="Apply Sort")
input.sort-group__button--reset(type='button' id='resetSortButton' value="Reset Sort")
mixin graphXOption(option)
if option.checked
input.axis-group__radio(type="radio" id=option.id name="x_axis" value=option.value checked)
else
input.axis-group__radio(type="radio" id=option.id name="x_axis" value=option.value)
label.axis-group__option-label(for=option.id) #{option.label}
br
mixin graphYOption(option)
if option.checked
input.values-group__checkbox(type="checkbox" id=option.id name="values" value=option.value checked)
else
input.values-group__checkbox(type="checkbox" id=option.id name="values" value=option.value)
label.values-group__option-label(for=option.id) #{option.label}
br
mixin graphMixin()
details.graph
summary.graph__summary Graph
form.graph__form
fieldset.graph__fieldset
.axis-group
i.axis-group__label
b X axis
br
.axis-group__options
each option in axisOptions
+graphXOption(option)
.values-group
i.values-group__label
b Values
br
.values-group__options
each option in valueOptions
+graphYOption(option)
button.values-group__button(type="submit" id="build_graph" name="build_graph") Build

Binary file not shown.

After

Width:  |  Height:  |  Size: 198 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 159 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 229 KiB

View File

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 22 KiB

View File

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

View File

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 17 KiB

View File

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

View File

Before

Width:  |  Height:  |  Size: 1.2 MiB

After

Width:  |  Height:  |  Size: 1.2 MiB

View File

Before

Width:  |  Height:  |  Size: 80 KiB

After

Width:  |  Height:  |  Size: 80 KiB

View File

Before

Width:  |  Height:  |  Size: 347 KiB

After

Width:  |  Height:  |  Size: 347 KiB

View File

Before

Width:  |  Height:  |  Size: 41 KiB

After

Width:  |  Height:  |  Size: 41 KiB

View File

Before

Width:  |  Height:  |  Size: 1.1 MiB

After

Width:  |  Height:  |  Size: 1.1 MiB

View File

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

View File

Before

Width:  |  Height:  |  Size: 2.3 MiB

After

Width:  |  Height:  |  Size: 2.3 MiB

View File

Before

Width:  |  Height:  |  Size: 5.2 MiB

After

Width:  |  Height:  |  Size: 5.2 MiB

View File

Before

Width:  |  Height:  |  Size: 99 KiB

After

Width:  |  Height:  |  Size: 99 KiB

View File

Before

Width:  |  Height:  |  Size: 3.9 MiB

After

Width:  |  Height:  |  Size: 3.9 MiB

View File

Before

Width:  |  Height:  |  Size: 4.4 MiB

After

Width:  |  Height:  |  Size: 4.4 MiB

View File

Before

Width:  |  Height:  |  Size: 1.9 MiB

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

View File

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

View File

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 50 KiB

View File

Before

Width:  |  Height:  |  Size: 99 KiB

After

Width:  |  Height:  |  Size: 99 KiB

View File

@@ -0,0 +1,3 @@
import './styles/index.styl'
import './styles/details.styl'
import './styles/table.styl'

View File

@@ -0,0 +1,51 @@
include ../components/mixins.pug
-
const infoBlockA = {
header: "Embedded Systems",
text: "Had a serval projects with different MCUs and wrote firmware for them. My main stack consists of Platformio+EspIdf or Arduino, also tried STM32 asdadas",
description: "I love embeded development beceuse of autonomity of produced devices, the redices are not dependant on any other hardware and can work autunomously",
img: require('../images/AQ_monitor.png'),
imgAlt: "ESP32"
}
const infoBlockB = {
header: "Embedded Systems",
text: "Had a serval projects with different MCUs and wrote firmware for them. My main stack consists of Platformio+EspIdf or Arduino, also tried STM32",
description: "I love embeded development device because of autonomity of produced devices, the devices are not dependant on any other hardware and can work autunomously or off the grid",
img: require('../images/remote.jpeg'),
imgAlt: "ESP32"
}
html(lang="en")
head
meta(charset="UTF-8")
title Portfolio
body
+navbarMixin("Home")
.gallery
+galleryMixin()
.main-container
.main-container__content
article.info-block
h2.info-block__header #{infoBlockA.header}
.info-block__container.info-block__container_layout-a
p.info-block__text #{infoBlockA.text}
.info-block__details
p.info-block__description #{infoBlockA.description}
a.info-block__link(href="#") More info
img.info-block__image(src=infoBlockA.img alt=infoBlockA.imgAlt)
article.info-block
h2.info-block__header #{infoBlockB.header}
.info-block__container.info-block__container_layout-b
p.info-block__text #{infoBlockB.text}
img.info-block__image(src=infoBlockB.img alt=infoBlockB.imgAlt)
.info-block__details
p.info-block__description #{infoBlockB.description}
a.info-block__link(href="./photography-details.html") More info
.sidebar
+infoCardsMinxin()

View File

@@ -0,0 +1,22 @@
extends ../components/details-template.pug
block title
title Photography Portfolio - Details
block navbar
+navbarMixin("Photography")
block header
h1.details__title Photography
p.details__description I prefer landscape photography.
block text
p.details__text-content D3100 -> SLT A58.
block gallery
.details__gallery-item
img.details__gallery-image(src=require('../images/photos/image0.png') alt="Photography 1")
.details__gallery-item
img.details__gallery-image(src=require('../images/photos/image1.png') alt="Photography 2")
.details__gallery-item
img.details__gallery-image(src=require('../images/photos/image2.png') alt="Photography 3")

View File

@@ -0,0 +1,81 @@
include ../components/mixins.pug
doctype html
html(lang="ru")
head
meta(charset="utf-8")
title Трансформация и анимация
script(src="svg_playground/d3.v7.min.js")
script(src="svg_playground/main.js")
script(src="svg_playground/image.js")
script(src="svg_playground/path.js")
body
+navbarMixin("SVG Playground")
form#setting
p.path-anim-related-inverse
| Координаты рисунка
br
label(for="cx") x:
input#cx(type="number", value="300", max="600", min="0")
label.anim-related(for="cx_finish") до:
input#cx_finish.anim-related(type="number", value="300", max="600", min="0")
br
label(for="cy") y:
input#cy(type="number", value="300", max="600", min="0")
label.anim-related(for="cy_finish") до:
input.anim-related#cy_finish(type="number", value="300", max="600", min="0")
p.path-anim-related
| Пути перемещения
br
select#pathSelect
option(value="0") Буквой "Г"
option(value="1") По кругу
option(value="2") Спиралью
p
| Масштаб
br
label(for="sx") по x:
input#sx(type="number", value="1", max="100", min="-100")
label.anim-related(for="sx_finish") до:
input#sx_finish.anim-related(type="number", value="1.5", max="100", min="-100")
br
label(for="sy") по y:
input#sy(type="number", value="1", max="100", min="-100")
label.anim-related(for="sy_finish") до:
input#sy_finish.anim-related(type="number", value="1.5", max="100", min="-100")
p
| Поворот
br
label(for="r") x:
input#r(type="number", value="0", max="360", min="-360")
label.anim-related(for="r_finish") до:
input#r_finish.anim-related(type="number", value="360", max="360", min="-360")
p.anim-related
| Время анамации (мс)
br
label(for="duration") x:
input#duration(type="number", value="2000", max="10000", min="1")
p
input#applyButton.anim-related-inverse(type="button", value="Нарисовать")
input#clearButton(type="button", value="Отчистить")
p
| Включить Анимацию?
input#enableAnimCheckbox(type="checkbox")
br
label.anim-related(for="animAlongPathCheckbox") Перемещение вдоль пути?
input#animAlongPathCheckbox.anim-related(type="checkbox")
select#animTypeSelect.anim-related
option(value="0") linear
option(value="1") elastic
option(value="2") bounce
br
input#startAnimButton.anim-related(type="button", value="Анимировать")
svg

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,33 @@
// создаем изображение смайлик
// рисуем его относительно точки (0, 0)
function drawSmile(svg) {
let smile = svg.append("g")
.style("stroke", "brown")
.style("stroke-width", 0)
.style("fill", "brown");
for (let t = 0; t <= Math.PI * 6; t += .8) {
let cords = {
x: 50 / 3 * (1 - (t / (Math.PI * 6))) * -Math.sin(t),
y: 50 / 3 * (1 - (t / (Math.PI * 6))) * Math.cos(t)
}
smile.append("circle")
.attr("cx", cords["x"])
.attr("cy", cords["y"])
.attr("r", (1 - (t / (Math.PI * 6))))
.style("fill", "red");
}
for (let t = .4; t <= Math.PI * 6; t += .8) {
let cords = {
x: 50 / 3 * (1 - (t / (Math.PI * 6))) * -Math.sin(t),
y: 50 / 3 * (1 - (t / (Math.PI * 6))) *Math.cos(t)
}
smile.append("circle")
.attr("cx", cords["x"])
.attr("cy", cords["y"])
.attr("r", (1 - (t / (Math.PI * 6))))
.style("fill", "green");
}
return smile
}

View File

@@ -0,0 +1,93 @@
document.addEventListener("DOMContentLoaded", function () {
const width = 600;
const height = 600;
const svg = d3.select("svg")
.attr("width", width)
.attr("height", height);
// let pict1 = drawSmile(svg);
// pict1.attr("transform", `translate(400, 400) scale(1.5, 1.5) rotate(180)`);
// let pict = drawSmile(svg);
// pict.attr("transform", "translate(200, 200)");
setting.applyButton.addEventListener("click", () => {
draw(setting);
})
setting.clearButton.addEventListener("click", () => {
svg.selectAll('*').remove()
})
updateAnimationControllsDisplay();
enableAnimCheckbox.addEventListener("change", updateAnimationControllsDisplay);
animAlongPathCheckbox.addEventListener("change", updateAnimationControllsDisplay);
startAnimButton.addEventListener("click", () => {
animRouter(setting)
})
})
const updateAnimationControllsDisplay = () => {
let isChecked = enableAnimCheckbox.checked;
let isPathAnim = animAlongPathCheckbox.checked;
// console.log(isChecked);
document.querySelectorAll(".anim-related").forEach((elem) => { elem.hidden = !isChecked });
document.querySelectorAll(".anim-related-inverse").forEach((elem) => { elem.hidden = isChecked });
document.querySelectorAll(".path-anim-related").forEach((elem) => { elem.hidden = !(isPathAnim && isChecked) });
document.querySelectorAll(".path-anim-related-inverse").forEach((elem) => { elem.hidden = (isPathAnim && isChecked) });
}
const draw = (dataForm) => {
const svg = d3.select("svg")
let pict = drawSmile(svg);
pict.attr("transform", `
translate(${dataForm.cx.value},
${dataForm.cy.value})
scale(${dataForm.sx.value},
${dataForm.sy.value})
rotate(${dataForm.r.value})`);
}
const animRouter = (dataForm) => {
if (dataForm.animAlongPathCheckbox.checked) {
let path = drawPath(Number(dataForm.pathSelect.value));
const svg = d3.select("svg")
let pict = drawSmile(svg);
const parameters = {
sx:[Number(dataForm.sx.value),Number(dataForm.sx_finish.value)],
sy:[Number(dataForm.sy.value),Number(dataForm.sy_finish.value)],
r:[Number(dataForm.r.value),Number(dataForm.r_finish.value)],
};
pict.transition()
.ease([d3.easeLinear, d3.easeElastic, d3.easeBounce][Number(animTypeSelect.value)])
.duration(Number(duration.value))
.attrTween('transform', translateAlong(path.node(),parameters));
}
else {
runAnimation(dataForm)
}
}
const runAnimation = (dataForm) => {
const svg = d3.select("svg")
let pict = drawSmile(svg);
pict.attr("transform", `
translate(${dataForm.cx.value},
${dataForm.cy.value})
scale(${dataForm.sx.value},
${dataForm.sy.value})
rotate(${dataForm.r.value})`)
.transition()
.duration(Number(duration.value))
.ease([d3.easeLinear, d3.easeElastic, d3.easeBounce][Number(animTypeSelect.value)])
.attr("transform", `
translate(${dataForm.cx_finish.value},
${dataForm.cy_finish.value})
scale(${dataForm.sx_finish.value},
${dataForm.sy_finish.value})
rotate(${dataForm.r_finish.value})`);
}

View File

@@ -0,0 +1,99 @@
/* массив точек пути будет иметь следующий вид:
[
{x: координата, y: координата},
{x: координата, y: координата},
...
]
*/
// создаем массив точек, расположенных буквой "Г"
function createPathG() {
const svg = d3.select("svg")
const width = svg.attr("width")
const height = svg.attr("height")
let data = [];
const padding = 100;
//начальное положение рисунка
let posX = padding;
let posY = height - padding;
const h = 5;
// координаты y - уменьшаются, x - постоянны
while (posY > padding) {
data.push({ x: posX, y: posY });
posY -= h;
}
// координаты y - постоянны, x - увеличиваются
while (posX < width - padding) {
data.push({ x: posX, y: posY });
posX += h;
}
return data
}
// создаем массив точек, расположенных по кругу
function createPathCircle() {
const svg = d3.select("svg")
const width = svg.attr("width")
const height = svg.attr("height")
let data = [];
// используем параметрическую форму описания круга
// центр расположен в центре svg-элемента, а радиус равен трети высоты/ширины
for (let t = 0; t <= Math.PI * 2; t += 0.1) {
data.push(
{
x: width / 2 + width / 3 * Math.sin(t),
y: height / 2 + height / 3 * Math.cos(t)
}
);
}
return data
}
function createPathSpiral() {
const svg = d3.select("svg")
const width = svg.attr("width")
const height = svg.attr("height")
let data = [];
// используем параметрическую форму описания круга
// центр расположен в центре svg-элемента, а радиус равен трети высоты/ширины
for (let t = 0; t <= Math.PI * 6; t += 0.1) {
data.push(
{
x: width / 2 + width / 3 * (1 - (t / (Math.PI * 6))) * -Math.sin(t),
y: height / 2 + height / 3 * (1 - (t / (Math.PI * 6))) * Math.cos(t)
}
);
}
return data
}
const drawPath = (typePath) => {
// создаем массив точек
const dataPoints = [createPathG, createPathCircle, createPathSpiral][Number(typePath)]()
const line = d3.line()
.x((d) => d.x)
.y((d) => d.y);
const svg = d3.select("svg")
// создаем путь на основе массива точек
const path = svg.append('path')
.attr('d', line(dataPoints))
.attr('stroke', 'black')
.attr('fill', 'none');
return path;
}
function translateAlong(path, params) {
const length = path.getTotalLength();
return function () {
return function (t) {
const { x, y } = path.getPointAtLength(t * length);
return `
translate(${x},${y})
scale(${params.sx[0] +(params.sx[1] - params.sx[0])*t},
${params.sy[0] +(params.sy[1] - params.sy[0])*t})
rotate(${params.r[0] +(params.r[1] - params.r[0])*t})
`;
}
}
}

View File

@@ -0,0 +1,19 @@
include ../components/mixins.pug
head
title table
script(src='./table/data.js')
script(src='./table/filter.js')
script(src='./table/main.js')
script(src='./table/sort.js')
script(src='./table/table.js')
body
+navbarMixin("Table")
.table-controls
+filtersMixin()
+sortingMixin()
+graphMixin()
table(id='list')

View File

@@ -0,0 +1,68 @@
let ram_sticks = [
{ "type": "DDR3", "name": "DDR3-1600-4GB-A1", "size": 4, "maker": "Kingston", "release": "2014-03", "price": 18, },
{ "type": "DDR3", "name": "DDR3-1600-8GB-A2", "size": 8, "maker": "Corsair", "release": "2015-06", "price": 26, },
{ "type": "DDR3", "name": "DDR3-1866-8GB-A3", "size": 8, "maker": "G.Skill", "release": "2016-02", "price": 29, },
{ "type": "DDR4", "name": "DDR4-2133-8GB-B1", "size": 8, "maker": "Crucial", "release": "2017-01", "price": 24, },
{ "type": "DDR4", "name": "DDR4-2400-8GB-B2", "size": 8, "maker": "Kingston", "release": "2017-09", "price": 27, },
{ "type": "DDR4", "name": "DDR4-2666-16GB-B3", "size": 16, "maker": "Corsair", "release": "2018-04", "price": 48, },
{ "type": "DDR4", "name": "DDR4-3000-16GB-B4", "size": 16, "maker": "G.Skill", "release": "2018-11", "price": 52, },
{ "type": "DDR4", "name": "DDR4-3200-16GB-B5", "size": 16, "maker": "HyperX", "release": "2019-03", "price": 55, },
{ "type": "DDR4", "name": "DDR4-3200-32GB-B6", "size": 32, "maker": "Crucial", "release": "2019-08", "price": 92, },
{ "type": "DDR4", "name": "DDR4-3600-32GB-B7", "size": 32, "maker": "Corsair", "release": "2020-02", "price": 99, },
{ "type": "DDR5", "name": "DDR5-4800-16GB-C1", "size": 16, "maker": "Kingston", "release": "2021-01", "price": 78, },
{ "type": "DDR5", "name": "DDR5-5200-16GB-C2", "size": 16, "maker": "Corsair", "release": "2021-06", "price": 84, },
{ "type": "DDR5", "name": "DDR5-5600-32GB-C3", "size": 32, "maker": "G.Skill", "release": "2022-02", "price": 145, },
{ "type": "DDR5", "name": "DDR5-6000-32GB-C4", "size": 32, "maker": "Crucial", "release": "2022-07", "price": 158, },
{ "type": "DDR5", "name": "DDR5-6400-32GB-C5", "size": 32, "maker": "Corsair", "release": "2023-01", "price": 172, },
{ "type": "DDR5", "name": "DDR5-6600-64GB-C6", "size": 64, "maker": "Kingston", "release": "2023-05", "price": 310, },
{ "type": "DDR5", "name": "DDR5-6800-64GB-C7", "size": 64, "maker": "G.Skill", "release": "2023-09", "price": 329, },
{ "type": "DDR5", "name": "DDR5-7200-64GB-C8", "size": 64, "maker": "Corsair", "release": "2024-02", "price": 355, },
{ "type": "DDR5", "name": "DDR5-7600-96GB-C9", "size": 96, "maker": "Crucial", "release": "2024-06", "price": 520, },
{ "type": "DDR5", "name": "DDR5-8000-96GB-C10", "size": 96, "maker": "Kingston", "release": "2024-10", "price": 560, },
{ "type": "LPDDR4", "name": "LP4-3200-8GB-D1", "size": 8, "maker": "Samsung", "release": "2019-01", "price": 34, },
{ "type": "LPDDR4", "name": "LP4-4266-8GB-D2", "size": 8, "maker": "Micron", "release": "2019-07", "price": 39, },
{ "type": "LPDDR5", "name": "LP5-5500-12GB-D3", "size": 12, "maker": "Samsung", "release": "2020-03", "price": 58, },
{ "type": "LPDDR5", "name": "LP5-6400-16GB-D4", "size": 16, "maker": "SKHynix", "release": "2021-01", "price": 74, },
{ "type": "LPDDR5X", "name": "LP5X-7500-24GB-D5", "size": 24, "maker": "Micron", "release": "2022-05", "price": 118, },
{ "type": "DDR4", "name": "DDR4-2666-8GB-E1", "size": 8, "maker": "Patriot", "release": "2018-05", "price": 25, },
{ "type": "DDR4", "name": "DDR4-3000-8GB-E2", "size": 8, "maker": "ADATA", "release": "2018-09", "price": 28, },
{ "type": "DDR4", "name": "DDR4-3200-8GB-E3", "size": 8, "maker": "TeamGroup", "release": "2019-04", "price": 30, },
{ "type": "DDR4", "name": "DDR4-3600-16GB-E4", "size": 16, "maker": "ADATA", "release": "2020-01", "price": 53, },
{ "type": "DDR4", "name": "DDR4-4000-16GB-E5", "size": 16, "maker": "Patriot", "release": "2020-06", "price": 61, },
{ "type": "DDR5", "name": "DDR5-5200-8GB-F1", "size": 8, "maker": "TeamGroup", "release": "2021-03", "price": 52, },
{ "type": "DDR5", "name": "DDR5-5600-16GB-F2", "size": 16, "maker": "ADATA", "release": "2021-10", "price": 88, },
{ "type": "DDR5", "name": "DDR5-6000-16GB-F3", "size": 16, "maker": "Patriot", "release": "2022-03", "price": 95, },
{ "type": "DDR5", "name": "DDR5-6400-32GB-F4", "size": 32, "maker": "TeamGroup", "release": "2022-09", "price": 168, },
{ "type": "DDR5", "name": "DDR5-7200-32GB-F5", "size": 32, "maker": "ADATA", "release": "2023-04", "price": 185, },
{ "type": "DDR3", "name": "DDR3-1333-4GB-G1", "size": 4, "maker": "Samsung", "release": "2013-02", "price": 15, },
{ "type": "DDR3", "name": "DDR3-1600-4GB-G2", "size": 4, "maker": "Micron", "release": "2014-08", "price": 17, },
{ "type": "DDR3", "name": "DDR3-1866-8GB-G3", "size": 8, "maker": "Samsung", "release": "2015-11", "price": 28, },
{ "type": "DDR5", "name": "DDR5-8400-128GB-H1", "size": 128, "maker": "Corsair", "release": "2025-01", "price": 890, },
{ "type": "DDR5", "name": "DDR5-8800-128GB-H2", "size": 128, "maker": "G.Skill", "release": "2025-03", "price": 940, },
{ "type": "DDR5", "name": "DDR5-9200-128GB-H3", "size": 128, "maker": "Kingston", "release": "2025-06", "price": 990, },
{ "type": "DDR5", "name": "DDR5-6000-48GB-X1", "size": 48, "maker": "Corsair", "release": "2024-01", "price": 210, },
{ "type": "DDR5", "name": "DDR5-6000-48GB-X2", "size": 48, "maker": "Kingston", "release": "2024-02", "price": 215, },
{ "type": "DDR5", "name": "DDR5-6000-48GB-X3", "size": 48, "maker": "G.Skill", "release": "2024-03", "price": 218, },
{ "type": "DDR5", "name": "DDR5-6000-48GB-X4", "size": 48, "maker": "ADATA", "release": "2024-04", "price": 222, },
{ "type": "DDR5", "name": "DDR5-6000-48GB-X5", "size": 48, "maker": "Patriot", "release": "2024-05", "price": 225, },
{ "type": "DDR5", "name": "DDR5-6000-48GB-X6", "size": 48, "maker": "TeamGroup", "release": "2024-06", "price": 228, },
{ "type": "DDR5", "name": "DDR5-6000-48GB-X7", "size": 48, "maker": "Crucial", "release": "2024-07", "price": 230, },
{ "type": "DDR5", "name": "DDR5-6000-48GB-X8", "size": 48, "maker": "Samsung", "release": "2024-08", "price": 235, },
{ "type": "DDR5", "name": "DDR5-6000-48GB-X9", "size": 48, "maker": "Micron", "release": "2024-09", "price": 238, },
{ "type": "DDR5", "name": "DDR5-6000-48GB-X10", "size": 48, "maker": "SKHynix", "release": "2024-10", "price": 240, },
{ "type": "DDR4", "name": "DDR4-3200-64GB-Y1", "size": 64, "maker": "Corsair", "release": "2021-01", "price": 180, },
{ "type": "DDR4", "name": "DDR4-3200-64GB-Y2", "size": 64, "maker": "Kingston", "release": "2021-02", "price": 182, },
{ "type": "DDR4", "name": "DDR4-3200-64GB-Y3", "size": 64, "maker": "G.Skill", "release": "2021-03", "price": 185, },
{ "type": "DDR4", "name": "DDR4-3200-64GB-Y4", "size": 64, "maker": "ADATA", "release": "2021-04", "price": 188, },
{ "type": "DDR4", "name": "DDR4-3200-64GB-Y5", "size": 64, "maker": "Patriot", "release": "2021-05", "price": 190, },
{ "type": "DDR4", "name": "DDR4-3200-64GB-Y6", "size": 64, "maker": "TeamGroup", "release": "2021-06", "price": 192, },
{ "type": "DDR4", "name": "DDR4-3200-64GB-Y7", "size": 64, "maker": "Crucial", "release": "2021-07", "price": 195, },
{ "type": "DDR4", "name": "DDR4-3200-64GB-Y8", "size": 64, "maker": "Samsung", "release": "2021-08", "price": 198, },
{ "type": "DDR4", "name": "DDR4-3200-64GB-Y9", "size": 64, "maker": "Micron", "release": "2021-09", "price": 200, },
{ "type": "DDR4", "name": "DDR4-3200-64GB-Y10", "size": 64, "maker": "SKHynix", "release": "2021-10", "price": 205, }
]
ram_sticks = ram_sticks.map((x) => ({
...x,
release: Number(x.release.split("-")[0]),
}))

View File

@@ -0,0 +1,92 @@
const correspond = {
"type": "type",
"name": "name",
"maker": "manufacturer",
"size": ["sizeFrom", "sizeTo"],
"release": ["releaseDateFrom", "releaseDateTo"],
"price": ["priceFrom", "priceTo"]
}
/* Структура возвращаемого ассоциативного массива:
{
input_id: input_value,
...
}
*/
const dataFilter = (dataForm) => {
let dictFilter = {};
// перебираем все элементы формы с фильтрами
for (const item of dataForm.elements) {
// получаем значение элемента
let valInput = item.value;
// если поле типа text - приводим его значение к нижнему регистру
if (item.type === "text") {
valInput = valInput.toLowerCase();
}
if (item.type === "number") {
if (valInput === "") {
if (item.id.includes("From")) {
valInput = -Infinity;
}
else {
valInput = Infinity;
}
}
else {
valInput = Number(valInput);
}
}
// формируем очередной элемент ассоциативного массива
dictFilter[item.id] = valInput;
}
return dictFilter;
}
// фильтрация таблицы
const filterTable = (data, idTable, dataForm) => {
// получаем данные из полей формы
const datafilter = dataFilter(dataForm);
// выбираем данные соответствующие фильтру и формируем таблицу из них
let tableFilter = data.filter(item => {
/* в этой переменной будут "накапливаться" результаты сравнения данных
с параметрами фильтра */
let result = true;
// строка соответствует фильтру, если сравнение всех значения из input
// со значением ячейки очередной строки - истина
Object.entries(item).map(([key, val]) => {
// текстовые поля проверяем на вхождение
if (typeof val == 'string') {
result &&= val.toLowerCase().includes(datafilter[correspond[key]])
}
// САМОСТОЯТЕЛЬНО проверить числовые поля на принадлежность интервалу
if (typeof val == 'number') {
result &&= datafilter[correspond[key][0]] <= Number(val) && datafilter[correspond[key][1]] >= Number(val)
}
// console.log(result, key, )
});
return result;
});
// САМОСТОЯТЕЛЬНО вызвать функцию, которая удаляет все строки таблицы с id=idTable
clearTable(idTable);
// показать на странице таблицу с отфильтрованными строками
createTable(tableFilter, idTable);
}
const clearFilters = (form) => {
form.querySelectorAll(`& input[type="text"],& input[type="number"]`).forEach((elem) => elem.value = null)
}

View File

@@ -0,0 +1,122 @@
document.addEventListener("DOMContentLoaded", function () {
doInit()
})
function doInit() {
createTable(ram_sticks, 'list');
const clearFiltersButton = document.getElementById("clearFiltersButton");
clearFiltersButton.addEventListener("click", (event) => {
clearFilters(filters);
createTable(ram_sticks, 'list');
sortTable('list', sorting)
})
const applyFiltersButton = document.getElementById("applyFiltersButton");
applyFiltersButton.addEventListener("click", (event) => {
filterTable(ram_sticks, 'list', filters)
sortTable('list', sorting)
})
setSortSelects(ram_sticks[0], sorting)
let i = 0;
for (const elem of sorting.querySelectorAll(`& select`)) {
const counter = i;
elem.addEventListener("change", () => {
updateOptions(counter, sorting)
})
i+=1;
}
sorting.applySortButton.addEventListener("click", () => {
clearTable('list');
filterTable(ram_sticks, 'list', filters)
sortTable('list', sorting)
});
sorting.resetSortButton.addEventListener("click", () => {
resetSort(sorting);
clearTable('list');
filterTable(ram_sticks, 'list', filters)
});
}
const createOption = (str, val) => {
let item = document.createElement('option');
item.text = str;
item.value = val;
return item;
}
const resetSortSelect = (sortSelect) => {
sortSelect.querySelectorAll(`& option`).forEach((elem) => {
elem.parentElement.removeChild(elem)
})
}
const all_fields = Object.keys(ram_sticks[0]);
const setSortSelect = (arr, sortSelect) => {
// создаем OPTION Нет и добавляем ее в SELECT
sortSelect.append(createOption('none', 0));
// перебираем массив со значениями опций
arr.forEach((item, index) => {
sortSelect.append(createOption(item, all_fields.indexOf(item) + 1));
});
}
const updateOptions = (current_index, sort_form) => {
const elem_collection = sort_form.querySelectorAll(`& select`);
let used_options_list = []
let i = 0;
for (let elem_ of elem_collection) {
if (i++ < current_index) {
used_options_list.push(all_fields[elem_.value-1])
}
}
if (current_index >= elem_collection.length) {
return;
}
const elem = elem_collection[current_index]
const available_options = all_fields.filter((x) => !used_options_list.includes(x));
elem.disabled = available_options.length == 0;
if(current_index>0){
elem.disabled |= elem_collection[current_index-1].value==0;
}
if(elem.disabled){
elem.value = 0;
}
save_val = 0;
if (!used_options_list.includes(all_fields[elem.value - 1])) {
save_val = elem.value;
}
resetSortSelect(elem)
setSortSelect(available_options, elem)
elem.value = save_val;
updateOptions(current_index + 1, sort_form)
}
// формируем поля со списком для многоуровневой сортировки
const setSortSelects = (data, dataForm) => {
const head = Object.keys(data);
const allSelect = dataForm.getElementsByTagName('select');
for (const item of dataForm.elements) {
setSortSelect(head, item);
}
let skipped = false
for (const elem of allSelect) {
if (!skipped) {
skipped = true;
continue;
}
elem.disabled = true;
}
}
const resetSort = (form) => {
form.querySelectorAll(`& select`).forEach((curSelect, index) => { if (index != 0) { curSelect.disabled = true } curSelect.value = 0 })
}

View File

@@ -0,0 +1,87 @@
/*формируем массив для сортировки по двум уровням вида
[
{column: номер столбца, по которому осуществляется сортировка,
direction: порядок сортировки (true по убыванию, false по возрастанию)
},
...
]
*/
const createSortArr = (data) => {
let sortArr = [];
const sortSelects = data.getElementsByTagName('select');
for (const item of sortSelects) {
// получаем номер выбранной опции
const keySort = item.value;
// в случае, если выбрана опция Нет, заканчиваем формировать массив
if (keySort == 0) {
break;
}
// получаем порядок сортировки очередного уровня
// имя флажка сформировано как имя поля SELECT и слова Desc
const desc = document.getElementById(item.id + 'Desc').checked;
// очередной элемент массива - по какому столбцу и в каком порядке сортировать
sortArr.push(
{
column: keySort - 1,
direction: desc
}
);
}
return sortArr;
};
const numberColumns = [2, 4, 5]
const sortTable = (idTable, formData) => {
// формируем управляющий массив для сортировки
const sortArr = createSortArr(formData);
// сортировать таблицу не нужно, во всех полях выбрана опция Нет
if (sortArr.length === 0) {
// clearTable(idTable)
return false;
}
//находим нужную таблицу
let table = document.getElementById(idTable);
// преобразуем строки таблицы в массив
let rowData = Array.from(table.rows);
// удаляем элемент с заголовками таблицы
const headerRow = rowData.shift();
//сортируем данные по всем уровням сортировки
rowData.sort((first, second) => {
for (let { column, direction } of sortArr) {
const firstCell = first.cells[column].innerHTML;
const secondCell = second.cells[column].innerHTML;
// используем localeCompare для корректного сравнения
let comparison = null;
if (numberColumns.includes(column)) {
comparison = Number(firstCell) - Number(secondCell);
}
else {
comparison = firstCell.localeCompare(secondCell);
}
// учитываем направление сортировки
if (comparison !== 0) {
return (direction ? -comparison : comparison);
}
}
return 0;
});
clearTable(idTable)
//выводим отсортированную таблицу на страницу
let tbody = document.createElement('tbody');
rowData.forEach(item => {
tbody.append(item);
});
table.append(tbody);
return true;
}

View File

@@ -0,0 +1,51 @@
const createTable = (data, idTable) => {
const table = document.getElementById(idTable);
if (data.length == 0) {
return;
}
const header = Object.keys(data[0]);
/* создание шапки таблицы */
if (!table.firstElementChild) {
const headerRow = createHeaderRow(header);
table.append(headerRow);
}
/* создание тела таблицы */
const bodyRows = createBodyRows(data);
table.append(bodyRows);
};
const clearTable = (idTable) => {
const table = document.getElementById(idTable);
if (table.children.length > 1) {
table.removeChild(table.children[1]);
}
}
const createHeaderRow = (headers) => {
const tr = document.createElement('tr');
headers.forEach(header => {
const th = document.createElement('th');
th.innerHTML = header.slice(0,1).toUpperCase()+header.slice(1);
tr.append(th);
});
const thead = document.createElement('thead')
thead.appendChild(tr)
return thead;
};
const createBodyRows = (rows) => {
const body = document.createElement('tbody');
rows.forEach(row => {
const rowElement = document.createElement('tr');
for (let key in row) {
const td = document.createElement('td');
td.innerHTML = row[key];
rowElement.appendChild(td);
}
body.appendChild(rowElement);
})
return body;
}

View File

@@ -0,0 +1,58 @@
.details
margin 0 auto
padding 20px
.details__header
margin-bottom 20px
text-align center
.details__title
margin-bottom 5px
.details__description
margin auto
.details__content
display flex
gap 40px
align-items flex-start
.details__text
flex 1
text-align justify
.details__text-content
margin auto 30px
.details__gallery
display flex
flex-direction column
gap 20px
margin auto
.details__gallery-item
overflow hidden
border-radius 8px
box-shadow 0 4px 12px rgba(0, 0, 0, 0.1)
transition transform 0.3s ease, box-shadow 0.3s ease
&:hover
transform translateY(-5px)
box-shadow 0 8px 20px rgba(0, 0, 0, 0.15)
.details__gallery-image
width 100%
height 250px
object-fit cover
display block
@media screen and (max-width: 700px)
.details__content
flex-direction column
.details__gallery
order -1 // Move gallery above text
.details__gallery-image
height 300px

View File

@@ -0,0 +1,166 @@
item_h=3em;
double-border()
border-top double thick silver
border-bottom double thick silver
flex-center()
display flex
align-items center
justify-content center
text-center-bold()
text-align center
font-weight bold
display block
grid-responsive(columns)
display grid
grid-template-columns columns
grid-template-rows 1fr
.navbar
double-border()
font-size 0
grid-responsive(repeat(7, min-content))
justify-items start
&__link-wrapper
color black
text-decoration none
&__item
display inline-block
width max-content
height item_h
line-height item_h
text-align center
vertical-align middle
padding 0 10px
margin @padding
overflow hidden
text-overflow ellipsis
font-size medium
&_selected
color green
font-weight bold
border-left double thin silver
border-right double thin silver
.gallery
display flex
flex-direction row
width 90%
margin auto
&__image
margin auto
width 100%
border thin solid black
&:nth-child(2n+1)
margin-top 10px
.main-container
grid-responsive(3fr 1fr)
.info-block
border solid thin black
padding 2vw
margin 1vw 0
&__header
text-center-bold()
&__container
&_layout-a
grid-responsive(3fr 3fr 2fr)
&_layout-b
grid-responsive(4fr 7fr 4fr)
& > *
margin 2vw
&__text, &__description
text-align justify
&__image
margin auto
width 100%
&__link
color inherit
.sidebar
border-left thin solid black
margin 30px 0 0 50px
.small-card
margin 20px 10px 0
grid-responsive(1fr 1fr)
&__content
text-align justify
&__header
text-center-bold()
text-align right
&__link
display block
text-align right
color inherit
&__image
& img
width 100%
margin-left:10px
@media screen and (max-width: 1000px)
.small-card
display flex
flex-direction column
.sidebar
margin 0
width:100%
@media screen and (max-width: 800px)
.navbar
grid-responsive(1fr 1fr)
grid-template-rows 1fr 1fr 1fr
justify-items center
&__item
&_selected
border-bottom double thick silver
border-left none
border-right none
.gallery
display inline-flex
flex-direction column
width 100%
&__image
width 100%
height 100%
&:nth-child(2n+1)
margin-top 0px
.main-container
display flex
flex-direction column
.info-block
&__container
&_layout-a, &_layout-b
display flex
flex-direction column

View File

@@ -0,0 +1,6 @@
.table, .table-content *, .table-content
border: 1px solid black;
border-collapse: collapse;
.table-content__table-row:nth-child(2n)
background: #F0FFF0;

View File

@@ -0,0 +1,82 @@
const HtmlWebpackPlugin = require("html-webpack-plugin");
const FileManagerPlugin = require("filemanager-webpack-plugin");
const path = require("path");
const fs = require("fs");
const pagesDir = path.join(__dirname, "src", "pages");
const pages = fs.readdirSync(pagesDir).filter(file => file.endsWith(".pug"));
module.exports = {
output: {
path: path.join(__dirname, "dist"),
filename: "index.[contenthash].js",
assetModuleFilename: path.join("images", "[name].[contenthash][ext]"),
},
entry: path.join(__dirname, "src", "index.js"),
module: {
rules: [
{
test: /\.pug$/,
loader: "pug-loader",
},
{
test: /\.(png|jpg|jpeg|gif)$/i,
type: "asset/resource",
},
{
test: /\.svg$/,
type: "asset/resource",
generator: {
filename: path.join("icons", "[name].[contenthash][ext]"),
},
},
{
test: /\.styl$/,
use: [
{
loader: "style-loader",
},
{
loader: "css-loader",
},
{
loader: "stylus-loader",
},
],
},
],
},
plugins: [
...pages.map(
(page) =>
new HtmlWebpackPlugin({
template: path.join(pagesDir, page),
filename: page.replace(".pug", ".html"),
})
),
new FileManagerPlugin({
events: {
onStart: {
delete: ["dist"],
},
onEnd: {
copy: [
{
source: path.join(__dirname, "src", "pages", "table", "*.js"),
destination: path.join(__dirname, "dist", "table"),
},
],
},
},
}),
],
devServer: {
watchFiles: path.join(__dirname, "src"),
port: 9000,
static: {
directory: path.join(__dirname, "src", "pages"),
publicPath: "/",
watch: true,
},
},
};

View File

@@ -1,104 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>
Embedded Details
</title>
</head>
<body bgcolor="#F0F8FF">
<details>
<summary>Menu</summary>
<ul>
<li><a href="./index.html">Home</a></li>
<li><a href="./embeded_details.html"><h4>Embeded Details</h4></a></li>
<li><a href="./opencv_details.html">OpenCV Details</a></li>
<li><a href="./photography_details.html">Photography Details</a></li>
<li><a href="./table.html">Table with filters (not working yet)</a></li>
<li><a href="./images/institute.png">Institute</a></li>
</ul>
</details>
<h2 align="center">Embedded Details</h2>
<hr width="800" align="center">
<p align="center">
<img src="./images/esp32.png" alt="ESP32" height="150">
<img src="./images/arduino.png" alt="Arduino" height="150">
<img src="./images/stm32.png" alt="STM32" height="150">
</p>
<p align="justify">My programming path started with embeded systems,
i love all the low level things that Mcu could do with just a little bit of code,
sometimes build or flashing progress could take some time to get a hand of , but after that everething usualy
goes smooth</p>
<p align="justify">The first MCU i tried was a ATmega328P,
after some time i was surprised with the debug capabilities on the Stm32
and now i'm working with esp32-s3 , that's a good chip, has internal JTAG and all the shiny wireless interfaces
but feels faulty
for some reason <i> regular segfaults with CDC</i></p>
<h3 align="center">Microcontroller Comparison Table</h3>
<table border="1" align="center" cellpadding="6" cellspacing="0">
<tr>
<th>Board/MCU</th>
<th>UART Count</th>
<th>Dynamic UART Assignment</th>
<th>ADC Channels</th>
<th>Debug Support</th>
<th>Flash Size</th>
<th>Wireless Connectivity</th>
</tr>
<tr>
<td>ESP32</td>
<td>3</td>
<td>Yes (via IO MUX)</td>
<td>18</td>
<td>JTAG, Serial</td>
<td>4MB (typical)</td>
<td>Wi-Fi, Bluetooth</td>
</tr>
<tr>
<td>ESP32-S3</td>
<td>3</td>
<td>Yes (via IO MUX)</td>
<td>20</td>
<td>JTAG, Serial</td>
<td>8MB (typical)</td>
<td>Wi-Fi, Bluetooth LE</td>
</tr>
<tr>
<td>ESP-01</td>
<td>1</td>
<td>No</td>
<td>1</td>
<td>Serial only</td>
<td>512KB/1MB</td>
<td>Wi-Fi</td>
</tr>
<tr>
<td>Arduino Uno (ATmega328P)</td>
<td>1</td>
<td>No</td>
<td>6</td>
<td>Serial, DebugWire</td>
<td>32KB</td>
<td>None</td>
</tr>
<tr>
<td>Blue Pill (STM32F103C8T6)</td>
<td>3</td>
<td>Yes (remappable)</td>
<td>10</td>
<td>SWD, Serial</td>
<td>64KB/128KB</td>
<td>None</td>
</tr>
</table>
<p align="right"> no rpi pico here , the build system has won over my temper</p>
</body>
</html>

214
index.css
View File

@@ -1,214 +0,0 @@
/* menu part */
.menu {
border-top: double 4px silver;
border-bottom: double 4px silver;
text-align: right;
font-size: 0;
}
.menu-item {
display: inline-block;
width: 8%;
height: 3em;
line-height: 3em; /* to align to center */
text-align: center;
vertical-align: middle;
margin: 0 1%;
overflow: hidden;
text-overflow: ellipsis ;
font-size: medium;
}
.menu .selected {
color: green;
font-weight: bold;
border-left: double 4px silver;
border-right: double 4px silver;
}
.menu .menu-header {
color: green;
font-weight: bold;
float: left;
}
.menu a {
color: black;
}
/* 4 images part */
.images-container *{
border:solid thin black;
}
.images-container {
width: 90%;
height: 200pt;
margin: auto;
overflow: hidden;
}
.half-size {
width: 49%;
margin: 0.2%;
float: left;
}
.quarter-size {
width: 24%;
margin: 0.2%;
float: left;
}
.image-column-container {
width: 24%;
height:100%;
margin: 0.15%;
float: left;
}
.image-column-container > div {
width: 100%;
height: 50%;
margin-bottom: 1.4%;
overflow: hidden;
/* text-align: center; */
}
.images-container img{
width:100%
}
/* main block part */
.main-block{
width:80%;
margin:1.2% auto;
}
.main-column{
display: inline-block;
width:25%;
}
.main-center{
vertical-align: top;
width: 48%;
height:100%;
display: inline-block;
}
.img-margin-fix {
margin-right:10px;
}
.info-block{
height:10%;
padding: 5%;
}
.bg-aqua{
background: #80DEEA;
}
.bg-grey{
background: #B0BFC6;
}
.bg-blue{
background: #BBDEFA;
}
.bg-yellow{
background: #FFE0B2;
}
.info-block-header{
display: block;
margin:auto;
text-align: center;
font-size: 1.5em;
font-weight: bold;
}
.info-block-text{
display: block;
text-align: justify;
}
.info-block-image{
margin: 0% 5% 5%;
width:90%;
overflow: hidden;
}
.info-block-image >img{
width: 100%;
}
.rounded{
border-radius: 20px;
overflow: hidden;
}
.info-block-button{
float: right;
color:white;
background-color: #2294F4;
border-radius: 5px;
}
.button-left{
float: left;
}
.pad-down{
margin-top: 40%;
bottom: 0;
}
/* main block center part */
.center-upper, .center-lower, .center-center{
/* background-color: lightblue; DEBUG*/
margin:5%;
}
.info-block-image-small{
display: inline-block;
width: 20%;
height: 100%;
background-color:black ;
overflow: hidden;
vertical-align: top;
}
.info-block-image-small >img{
width: 100%;
}
.align-left{
text-align: left;
}
.align-right{
text-align: right;
}
.inline {
display: inline-block;
}
.info-block-wide-text-container{
width: 75%;
margin: 0 1%;
}
.link{
display: block;
color:gray;
margin-bottom: 0px;
white-space: nowrap;
}
.info-block-image-center{
display: inline-block;
width: 50%;
/* height: 100px; */
background-color:black ;
overflow: hidden;
vertical-align: top;
}
.info-block-image-center >img{
width: 100%;
}
.small-w{
vertical-align: top;
width: 23%;
}

View File

@@ -1,169 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>
"Portfolio"
</title>
<link rel="stylesheet" href="./index.css">
</head>
<body>
<div class="menu">
<div class="menu-item menu-header">Portfolio Site </div>
<a href="./index.html">
<div class="menu-item selected">Home</div>
</a>
<a href="./embeded_details.html">
<div class="menu-item">Embedded</div>
</a>
<a href="./opencv_details.html">
<div class="menu-item">OpenCV</div>
</a>
<a href="./photography_details.html">
<div class="menu-item">Photography</div>
</a>
<a href="./table.html">
<div class="menu-item">Table</div>
</a>
<a href="./images/institute.png">
<div class="menu-item">Institute</div>
</a>
</div>
<div class="images-container">
<div class="half-size">
<img src="./images/arduino.png" alt="Arduino">
</div>
<div class="quarter-size">
<img src="./images/esp32.png" alt="ESP32">
</div>
<div class="image-column-container">
<div>
<img src="./images/institute.png" alt="ESP32">
</div>
<div>
<img src="./images/arucos.png" alt="ESP32">
</div>
</div>
</div>
<div class="main-block">
<div class="main-column">
<div class="info-block rounded bg-aqua">
<span class="info-block-header">Embedded Systems</span>
<span class="info-block-text">THad a serval projects with different MCUs and wrote firmware for them.
My main stack consists of Platformio+EspIdf or Arduino, also tried STM32
</span>
<div class="info-block-image rounded">
<img src="./images/16x9/esp32.png" alt="ESP32">
</div>
<button type="button" class="info-block-button">More info»</button>
</div>
<div class="info-block rounded bg-grey pad-down">
<span class="info-block-header">OpenCV</span>
<span class="info-block-text">I love when programs are able to see things or recognise them
so i had serval projects involding image processing
Sadly i'm used to opencv binding in python and not C++ , but that is solvable)
</span>
<div class="info-block-image rounded">
<img src="./images/16x9/opencv.png" alt="ESP32">
</div>
<button type="button" class="info-block-button">More info»</button>
</div>
</div>
<div class="main-center">
<div class="center-upper">
<div class="info-block-image-small ">
<img src="./images/sqstm32.png" alt="stm32">
</div>
<div class="inline info-block-wide-text-container ">
<span class="info-block-header align-left">STM 32</span>
<span class="info-block-text ">Chinese STM32 microcontrollers are affordable and compatible with
STM32 tools but may face flashing issues due to bootloader differences. Proper connections and
reliable tools can help resolve these.</span>
</span>
<a class="link align-left" href="./index.html">More info»</a>
</div>
</div>
<div class="center-center">
<span class="info-block-header">Stephen King's books</span>
<span class="info-block-text inline small-w">Stephen King's books are a fascinating blend of horror,
suspense, and deep
character development. His storytelling captivates readers and keeps them on the edge of their
seats.</span>
<div class="info-block-image-center">
<img src="./images/institute.png" alt="ESP32">
</div>
<span class="info-block-text inline small-w">
Especially I love the institute, its architecture, and the vibrant community it fosters. The
institute has been a source of inspiration and growth for me, providing countless opportunities to
learn and collaborate.
<a class="link small-w" href="./index.html">More info»</a>
</span>
</div>
<div class="center-lower">
<div class="inline info-block-wide-text-container">
<span class="info-block-header align-right">Arucos</span>
<span class="info-block-text ">ArUco markers are widely used in computer vision for camera
calibration, pose estimation, and augmented reality applications. They are square fiducial
markers with a unique binary pattern that can be easily detected and identified in
images.</span>
</span>
<a class="link align-right" href="./index.html">More info»</a>
</div>
<div class="info-block-image-small">
<img src="./images/sqarucos.png" alt="ESP32">
</div>
</div>
</div>
<div class="main-column">
<div class="info-block rounded bg-blue">
<div class="info-block-image rounded">
<img src="./images/16x9/image0.png" alt="Photography">
</div>
<span class="info-block-header">Photography</span>
<span class="info-block-text">Capturing moments through the lens is a passion of mine. I enjoy exploring
different perspectives and techniques to create different visuals.</span>
<button type="button" class="info-block-button button-left">More info»</button>
</div>
<div class="info-block rounded bg-yellow pad-down">
<div class="info-block-image rounded">
<img src="./images/16x9/AQ_monitor.png" alt="Air Quality Monitor">
</div>
<span class="info-block-header">IoT and Air Quality Monitoring</span>
<span class="info-block-text">The Internet of Things (IoT) enables devices to communicate and share data
seamlessly. One of the applications is air quality monitoring, where sensors collect data on
pollutants and environmental conditions, providing real-time insights to improve health and
safety.</span>
<button type="button" class="info-block-button button-left">More info»</button>
</div>
</div>
</div>
<p>Author: Kulesh <br>
Group: Б9123-09.03.04</p>
</body>
</html>

14
labs/lab1/CSS/style.css Normal file
View File

@@ -0,0 +1,14 @@
th{
padding: 5px;
background-color: gray;
}
td{
padding: 5px;
background-color: lightgray;
}
th, td ,table{
border: solid black thin;
border-collapse: collapse;
}

View File

@@ -0,0 +1,514 @@
buildings= [
{
"Название": "Бурдж-Халифа",
"Тип": "Небоскрёб",
"Страна": "ОАЭ",
"Город": "Дубай",
"Год": 2010,
"Высота": 828
},
{
"Название": "Варшавская радиомачта",
"Тип": "Антенная мачта",
"Страна": "Польша",
"Город": "Константинов",
"Год": 1974,
"Высота": 646.38
},
{
"Название": "Tokyo Skytree",
"Тип": "Бетонная башня",
"Страна": "Япония",
"Город": "Токио",
"Год": 2012,
"Высота": 634
},
{
"Название": "Шанхайская башня",
"Тип": "Небоскрёб",
"Страна": "КНР",
"Город": "Шанхай",
"Год": 2013,
"Высота": 632
},
{
"Название": "Телерадиомачта KVLY-TV",
"Тип": "Радиомачта",
"Страна": "США",
"Город": "Бланчард",
"Год": 1963,
"Высота": 629
},
{
"Название": "Телебашня Гуанчжоу",
"Тип": "Гиперболоидная башня",
"Страна": "КНР",
"Город": "Гуанчжоу",
"Год": 2009,
"Высота": 600
},
{
"Название": "Международный финансовый центр Пинань",
"Тип": "Небоскрёб",
"Страна": "КНР",
"Город": "Шэньчжэнь",
"Год": 2017,
"Высота": 600
},
{
"Название": "Lotte World Tower",
"Тип": "Небоскрёб",
"Страна": "Южная Корея",
"Город": "Сеул",
"Год": 2017,
"Высота": 555
},
{
"Название": "Си-Эн Тауэр",
"Тип": "Бетонная башня",
"Страна": "Канада",
"Город": "Торонто",
"Год": 1976,
"Высота": 553
},
{
"Название": "Останкинская башня",
"Тип": "Бетонная башня",
"Страна": "Россия",
"Город": "Москва",
"Год": 1967,
"Высота": 540.1
},
{
"Название": "Уиллис-тауэр",
"Тип": "Небоскрёб",
"Страна": "США",
"Город": "Чикаго",
"Год": 1974,
"Высота": 527.3
},
{
"Название": "Тайбэй 101",
"Тип": "Небоскрёб",
"Страна": "Тайвань",
"Город": "Тайбэй",
"Год": 2004,
"Высота": 509.2
},
{
"Название": "Шанхайский всемирный финансовый центр",
"Тип": "Небоскрёб",
"Страна": "КНР",
"Город": "Шанхай",
"Год": 2008,
"Высота": 492
},
{
"Название": "Международный коммерческий центр",
"Тип": "Небоскрёб",
"Страна": "Гонконг",
"Город": "Гонконг",
"Год": 2009,
"Высота": 484
},
{
"Название": "Восточная жемчужина",
"Тип": "Бетонная башня",
"Страна": "КНР",
"Город": "Шанхай",
"Год": 1994,
"Высота": 467.9
},
{
"Название": "Лахта-центр",
"Тип": "Небоскрёб",
"Страна": "Россия",
"Город": "Санкт-Петербург",
"Год": 2018,
"Высота": 462
},
{
"Название": "Landmark 81",
"Тип": "Небоскрёб",
"Страна": "Вьетнам",
"Город": "Хошимин",
"Год": 2018,
"Высота": 461.2
},
{
"Название": "875 Норт-Мичиган-авеню",
"Тип": "Небоскрёб",
"Страна": "США",
"Город": "Чикаго",
"Год": 1969,
"Высота": 457.2
},
{
"Название": "Петронас. башня 1 и 2",
"Тип": "Небоскрёб",
"Страна": "Малайзия",
"Город": "Куала-Лумпур",
"Год": 1998,
"Высота": 452
},
{
"Название": "Финансовый центр Наньцзин-Гринлэнд",
"Тип": "Небоскрёб",
"Страна": "КНР",
"Город": "Нанкин",
"Год": 2009,
"Высота": 450
},
{
"Название": "Эмпайр-стейт-билдинг",
"Тип": "Небоскрёб",
"Страна": "США",
"Город": "Нью-Йорк",
"Год": 1931,
"Высота": 448.7
},
{
"Название": "Международный финансовый центр. башня зап.",
"Тип": "Небоскрёб",
"Страна": "КНР",
"Город": "Гуанчжоу",
"Год": 2010,
"Высота": 437.5
},
{
"Название": "Kingkey 100",
"Тип": "Небоскрёб",
"Страна": "КНР",
"Город": "Шэньчжэнь",
"Год": 2011,
"Высота": 439.8
},
{
"Название": "Бордже Милад",
"Тип": "Бетонная башня",
"Страна": "Иран",
"Город": "Тегеран",
"Год": 2003,
"Высота": 435
},
{
"Название": "Парк-авеню. 432",
"Тип": "Небоскрёб",
"Страна": "США",
"Город": "Нью-Йорк",
"Год": 2015,
"Высота": 425.5
},
{
"Название": "Международная гостиница и башня Трампа",
"Тип": "Небоскрёб",
"Страна": "США",
"Город": "Чикаго",
"Год": 2009,
"Высота": 423.4
},
{
"Название": "Менара Куала-Лумпур",
"Тип": "Бетонная башня",
"Страна": "Малайзия",
"Город": "Куала-Лумпур",
"Год": 1995,
"Высота": 421
},
{
"Название": "Цзинь Мао",
"Тип": "Небоскрёб",
"Страна": "КНР",
"Город": "Шанхай",
"Год": 1999,
"Высота": 420.5
},
{
"Название": "Экибастузская ГРЭС-2",
"Тип": "Дымовая труба",
"Страна": "Казахстан",
"Город": "Экибастуз",
"Год": 1987,
"Высота": 419.7
},
{
"Название": "Международный финансовый центр",
"Тип": "Небоскрёб",
"Страна": "Гонконг",
"Город": "Гонконг",
"Год": 2003,
"Высота": 415.8
},
{
"Название": "Тяньцзиньская телебашня",
"Тип": "Бетонная башня",
"Страна": "КНР",
"Город": "Тяньцзинь",
"Год": 1991,
"Высота": 415.2
},
{
"Название": "Башня Аль-Хамра",
"Тип": "Небоскрёб",
"Страна": "Кувейт",
"Город": "Кувейт",
"Год": 2010,
"Высота": 412
},
{
"Название": "Пекинская телебашня",
"Тип": "Бетонная башня",
"Страна": "КНР",
"Город": "Пекин",
"Год": 1992,
"Высота": 405
},
{
"Название": "Башня CITIC",
"Тип": "Небоскрёб",
"Страна": "КНР",
"Город": "Гуанчжоу",
"Год": 1997,
"Высота": 391.1
},
{
"Название": "Киевская телебашня",
"Тип": "Решётчатая мачта",
"Страна": "Украина",
"Город": "Киев",
"Год": 1973,
"Высота": 385
},
{
"Название": "Башня Сёньхин",
"Тип": "Небоскрёб",
"Страна": "КНР",
"Город": "Шэньчжэнь",
"Год": 1996,
"Высота": 384
},
{
"Название": "Абу-Даби Плаза",
"Тип": "Небоскрёб",
"Страна": "Казахстан",
"Город": "Астана",
"Год": 2015,
"Высота": 382
},
{
"Название": "Бурдж-Мохаммед-бин-Рашид",
"Тип": "Небоскрёб",
"Страна": "ОАЭ",
"Город": "Абу-Даби",
"Год": 2014,
"Высота": 381
},
{
"Название": "Inco Superstack",
"Тип": "Дымовая труба",
"Страна": "Канада",
"Город": "Copper Cliff",
"Год": 1971,
"Высота": 380
},
{
"Название": "Тантекс-Скай-Тауэр",
"Тип": "Небоскрёб",
"Страна": "Тайвань",
"Город": "Гаосюн",
"Год": 1997,
"Высота": 378
},
{
"Название": "JW Marriott Marquis Dubai. 1 и 2",
"Тип": "Небоскрёб",
"Страна": "ОАЭ",
"Город": "Дубай",
"Год": 2010,
"Высота": 376
},
{
"Название": "Ташкентская телебашня",
"Тип": "Башня",
"Страна": "Узбекистан",
"Город": "Ташкент",
"Год": 1985,
"Высота": 374.9
},
{
"Название": "Башня Федерация «Восток»",
"Тип": "Небоскрёб",
"Страна": "Россия",
"Город": "Москва",
"Год": 2016,
"Высота": 374
},
{
"Название": "Сентрал-плаза",
"Тип": "Небоскрёб",
"Страна": "Гонконг",
"Город": "Гонконг",
"Год": 1992,
"Высота": 374
},
{
"Название": "Башня Освобождения",
"Тип": "Бетонная башня",
"Страна": "Кувейт",
"Город": "Кувейт",
"Год": 1996,
"Высота": 372
},
{
"Название": "Телебашня «Коктобе»",
"Тип": "Башня",
"Страна": "Казахстан",
"Город": "Алматы",
"Год": 1983,
"Высота": 371.5
},
{
"Название": "Дымовая труба электростанции",
"Тип": "Дымовая труба",
"Страна": "США",
"Город": "Homer City",
"Год": 1977,
"Высота": 371
},
{
"Название": "Дымовая труба Берёзовской ГРЭС",
"Тип": "Дымовая труба",
"Страна": "Россия",
"Город": "Шарыпово",
"Год": 1985,
"Высота": 370
},
{
"Название": "Рижская телебашня",
"Тип": "Бетонная башня",
"Страна": "Латвия",
"Город": "Рига",
"Год": 1987,
"Высота": 368.5
},
{
"Название": "Берлинская телебашня",
"Тип": "Бетонная башня",
"Страна": "Германия",
"Город": "Берлин",
"Год": 1969,
"Высота": 368
},
{
"Название": "Дымовая труба электростанции.",
"Тип": "Дымовая труба",
"Страна": "США",
"Город": "Маундсвилл",
"Год": 1968,
"Высота": 367.6
},
{
"Название": "Башня Банка Китая",
"Тип": "Небоскрёб",
"Страна": "Гонконг",
"Город": "Гонконг",
"Год": 1990,
"Высота": 367.4
},
{
"Название": "Башня Банка Америки",
"Тип": "Небоскрёб",
"Страна": "США",
"Город": "Нью-Йорк",
"Год": 2008,
"Высота": 366
},
{
"Название": "Башня Алмас",
"Тип": "Небоскрёб",
"Страна": "ОАЭ",
"Город": "Дубай",
"Год": 2008,
"Высота": 363
},
{
"Название": "Дымовая труба электростанции в Трбовле",
"Тип": "Дымовая труба",
"Страна": "Словения",
"Город": "Трбовле",
"Год": 1976,
"Высота": 360
},
{
"Название": "Endesa Termic ",
"Тип": "Дымовая труба",
"Страна": "Испания",
"Город": "Ферроль",
"Год": 1974,
"Высота": 356
},
{
"Название": "SEG Plaza",
"Тип": "Небоскрёб",
"Страна": "КНР",
"Город": "Шэньчжэнь",
"Год": 2000,
"Высота": 355.8
},
{
"Название": "First Canadian Place",
"Тип": "Небоскрёб",
"Страна": "Канада",
"Город": "Торонто",
"Год": 1976,
"Высота": 355
},
{
"Название": "Эмиратская офисная башня",
"Тип": "Небоскрёб",
"Страна": "ОАЭ",
"Город": "Дубай",
"Год": 2000,
"Высота": 354.6
},
{
"Название": "ОКО Южная башня",
"Тип": "Небоскрёб",
"Страна": "Россия",
"Город": "Москва",
"Год": 2015,
"Высота": 354
},
{
"Название": "Виннцкая телемачта",
"Тип": "Радиомачта",
"Страна": "Украина",
"Город": "Винница",
"Год": 1961,
"Высота": 354
},
{
"Название": "Медеплавильный завод",
"Тип": "Дымовая труба",
"Страна": "Румыния",
"Город": "Бая-Маре",
"Год": 1995,
"Высота": 351.5
},
{
"Название": "Стратосфера Лас-Вегас",
"Тип": "Бетонная башня",
"Страна": "США",
"Город": "Лас-Вегас",
"Год": 1996,
"Высота": 350.2
},
{
"Название": "Дымовая труба Сырдарьинской электростанции",
"Тип": "Дымовая труба",
"Страна": "Узбекистан",
"Город": "Сырдарья",
"Год": 1980,
"Высота": 350
}
]

View File

@@ -0,0 +1,91 @@
const correspond = {
"Название": "structure",
"Тип": "category",
"Страна": "country",
"Город": "city",
"Год": ["yearFrom", "yearTo"],
"Высота": ["heightFrom", "heightTo"]
}
/* Структура возвращаемого ассоциативного массива:
{
input_id: input_value,
...
}
*/
const dataFilter = (dataForm) => {
let dictFilter = {};
// перебираем все элементы формы с фильтрами
for (const item of dataForm.elements) {
// получаем значение элемента
let valInput = item.value;
// если поле типа text - приводим его значение к нижнему регистру
if (item.type === "text") {
valInput = valInput.toLowerCase();
}
if (item.type === "number") {
if (valInput === "") {
if (item.id.includes("From")) {
valInput = -Infinity;
}
else {
valInput = Infinity;
}
}
else {
valInput = Number(valInput);
}
}
// формируем очередной элемент ассоциативного массива
dictFilter[item.id] = valInput;
}
return dictFilter;
}
// фильтрация таблицы
const filterTable = (data, idTable, dataForm) => {
// получаем данные из полей формы
const datafilter = dataFilter(dataForm);
// выбираем данные соответствующие фильтру и формируем таблицу из них
let tableFilter = data.filter(item => {
/* в этой переменной будут "накапливаться" результаты сравнения данных
с параметрами фильтра */
let result = true;
// строка соответствует фильтру, если сравнение всех значения из input
// со значением ячейки очередной строки - истина
Object.entries(item).map(([key, val]) => {
// текстовые поля проверяем на вхождение
if (typeof val == 'string') {
result &&= val.toLowerCase().includes(datafilter[correspond[key]])
}
// САМОСТОЯТЕЛЬНО проверить числовые поля на принадлежность интервалу
if (typeof val == 'number') {
result &&= datafilter[correspond[key][0]] <= Number(val) && datafilter[correspond[key][1]] >= Number(val)
}
});
return result;
});
// САМОСТОЯТЕЛЬНО вызвать функцию, которая удаляет все строки таблицы с id=idTable
clearTable(idTable);
// показать на странице таблицу с отфильтрованными строками
createTable(tableFilter, idTable);
}
const clearFilters = (form) => {
form.querySelectorAll(`& input[type="text"],& input[type="number"]`).forEach((elem) => elem.value = null)
}

View File

@@ -0,0 +1,106 @@
document.addEventListener("DOMContentLoaded", function () {
doInit()
})
function doInit() {
createTable(buildings, 'list');
const clearFiltersButton = document.getElementById("clearFiltersButton");
clearFiltersButton.addEventListener("click", (event) => {
clearFilters(filter);
filterTable(buildings,'list',filter)
resetSort(sort);
})
const applyFiltersButton = document.getElementById("applyFiltersButton");
applyFiltersButton.addEventListener("click", (event) => {
filterTable(buildings,'list',filter)
resetSort(sort);
})
setSortSelects(buildings[0], sort)
sort.fieldsFirst.addEventListener("change",()=>{
changeNextSelect(sort.fieldsFirst,sort.fieldsSecond.id)
})
sort.doSortButton.addEventListener("click", ()=>{if (!sortTable('list',sort)){
clearTable('list');
createTable(buildings, 'list');
}});
sort.resetSortButton.addEventListener("click", ()=>{resetSort(sort);
clearTable('list');
createTable(buildings, 'list');
});
}
// формирование полей элемента списка с заданным текстом и значением
const createOption = (str, val) => {
let item = document.createElement('option');
item.text = str;
item.value = val;
return item;
}
// формирование поля со списком
// параметры массив со значениями элементов списка и элемент select
const setSortSelect = (arr, sortSelect) => {
// создаем OPTION Нет и добавляем ее в SELECT
sortSelect.append(createOption('Нет', 0));
// перебираем массив со значениями опций
arr.forEach((item, index) => {
// создаем OPTION из очередного ключа и добавляем в SELECT
// значение атрибута VALUE увеличиваем на 1, так как значение 0 имеет опция Нет
sortSelect.append(createOption(item, index + 1));
});
}
// формируем поля со списком для многоуровневой сортировки
const setSortSelects = (data, dataForm) => {
// выделяем ключи словаря в массив
const head = Object.keys(data);
// находим все SELECT в форме
const allSelect = dataForm.getElementsByTagName('select');
for(const item of dataForm.elements){
// формируем очередной SELECT
setSortSelect(head, item);
// САМОСТОЯТЕЛЬНО все SELECT, кроме первого, сделать неизменяемым
}
let skipped = false
for(const elem of allSelect){
if(!skipped){
skipped = true;
continue;
}
elem.disabled = true;
}
}
const changeNextSelect = (curSelect, nextSelectId) => {
let nextSelect = document.getElementById(nextSelectId);
nextSelect.disabled = false;
// в следующем SELECT выводим те же option, что и в текущем
nextSelect.innerHTML = curSelect.innerHTML;
// удаляем в следующем SELECT уже выбранную в текущем опцию
// если это не первая опция - отсутствие сортировки
if (curSelect.value != 0) {
nextSelect.remove(curSelect.value);
} else {
nextSelect.disabled = true;
}
}
const resetSort = (form)=>{
form.querySelectorAll(`& select`).forEach((curSelect,index) => {if(index!=0){curSelect.disabled = true} curSelect.value = 0})
}

View File

@@ -0,0 +1,87 @@
/*формируем массив для сортировки по двум уровням вида
[
{column: номер столбца, по которому осуществляется сортировка,
direction: порядок сортировки (true по убыванию, false по возрастанию)
},
...
]
*/
const createSortArr = (data) => {
let sortArr = [];
const sortSelects = data.getElementsByTagName('select');
for (const item of sortSelects) {
// получаем номер выбранной опции
const keySort = item.value;
// в случае, если выбрана опция Нет, заканчиваем формировать массив
if (keySort == 0) {
break;
}
// получаем порядок сортировки очередного уровня
// имя флажка сформировано как имя поля SELECT и слова Desc
const desc = document.getElementById(item.id + 'Desc').checked;
// очередной элемент массива - по какому столбцу и в каком порядке сортировать
sortArr.push(
{
column: keySort - 1,
direction: desc
}
);
}
return sortArr;
};
const numberColumns = [4, 5]
const sortTable = (idTable, formData) => {
// формируем управляющий массив для сортировки
const sortArr = createSortArr(formData);
// сортировать таблицу не нужно, во всех полях выбрана опция Нет
if (sortArr.length === 0) {
clearTable(idTable)
return false;
}
//находим нужную таблицу
let table = document.getElementById(idTable);
// преобразуем строки таблицы в массив
let rowData = Array.from(table.rows);
// удаляем элемент с заголовками таблицы
const headerRow = rowData.shift();
//сортируем данные по всем уровням сортировки
rowData.sort((first, second) => {
for (let { column, direction } of sortArr) {
const firstCell = first.cells[column].innerHTML;
const secondCell = second.cells[column].innerHTML;
// используем localeCompare для корректного сравнения
let comparison = null;
if (numberColumns.includes(column)) {
comparison = Number(firstCell) - Number(secondCell);
}
else {
comparison = firstCell.localeCompare(secondCell);
}
// учитываем направление сортировки
if (comparison !== 0) {
return (direction ? -comparison : comparison);
}
}
return 0;
});
clearTable(idTable)
//выводим отсортированную таблицу на страницу
let tbody = document.createElement('tbody');
rowData.forEach(item => {
tbody.append(item);
});
table.append(tbody);
return true;
}

View File

@@ -0,0 +1,50 @@
const createTable = (data, idTable) => {
const table = document.getElementById(idTable);
if (data.length == 0) {
return;
}
const header = Object.keys(data[0]);
/* создание шапки таблицы */
if (!table.firstElementChild) {
const headerRow = createHeaderRow(header);
table.append(headerRow);
}
/* создание тела таблицы */
const bodyRows = createBodyRows(data);
table.append(bodyRows);
};
const clearTable = (idTable) => {
const table = document.getElementById(idTable);
if (table.children.length > 1) {
table.removeChild(table.children[1]);
}
}
const createHeaderRow = (headers) => {
const tr = document.createElement('tr');
headers.forEach(header => {
const th = document.createElement('th');
th.innerHTML = header;
tr.append(th);
});
const thead = document.createElement('thead')
thead.appendChild(tr)
return thead;
};
const createBodyRows = (rows) => {
const body = document.createElement('tbody');
rows.forEach(row => {
const rowElement = document.createElement('tr');
for (let key in row) {
const td = document.createElement('td');
td.innerHTML = row[key];
rowElement.appendChild(td);
}
body.appendChild(rowElement);
})
return body;
}

76
labs/lab1/index.html Normal file
View File

@@ -0,0 +1,76 @@
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="utf-8">
<title>Фильтры и сортировка</title>
<link rel="stylesheet" href="CSS/style.css">
<script src="JavaScript/data.js"></script>
<script src="JavaScript/main.js"></script>
<script src="JavaScript/table.js"></script>
<script src="JavaScript/filter.js"></script>
<script src="JavaScript/sort.js"></script>
</head>
<body>
<div>
<h3>Фильтр</h3>
<form id="filter" name="filter">
<p>
<label for="structure">Название:</label>
<input type="text" id="structure">
</p>
<p>
<label for="category">Тип:</label>
<input type="text" id="category">
</p>
<p>
<label for="country">Страна:</label>
<input type="text" id="country">
</p>
<p>
<label for="city">Город:</label>
<input type="text" id="city">
</p>
<p>
<label for="yearFrom">Год: </label>
от <input type="number" id="yearFrom">
до <input type="number" id="yearTo">
</p>
<p>
<label for="heightFrom">Высота:</label>
от <input type="number" id="heightFrom">
до <input type="number" id="heightTo">
</p>
<input id="applyFiltersButton" type="button" value="Найти">
<input id="clearFiltersButton" type="button" value="Очистить фильтры">
</form>
</div>
<div>
<h3>Сортировка</h3>
<form id="sort">
<p> Сортировать по</p>
<p>
<select id="fieldsFirst">
</select>
по убыванию? <input type="checkbox" id="fieldsFirstDesc">
</p>
<p>
<select id="fieldsSecond">
</select>
по убыванию? <input type="checkbox" id="fieldsSecondDesc">
</p>
<input type="button" value="Сортировать" id="doSortButton">
<input type="button" value="Сбросить сортировку" id="resetSortButton">
</form>
<br>
</div>
<table id="list">
</table>
</body>
</html>

0
labs/lab2/CSS/style.css Normal file
View File

2
labs/lab2/JavaScript/d3.v7.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,33 @@
// создаем изображение смайлик
// рисуем его относительно точки (0, 0)
function drawSmile(svg) {
let smile = svg.append("g")
.style("stroke", "brown")
.style("stroke-width", 2)
.style("fill", "brown");
//лицо
smile.append("circle")
.attr("cx", 0)
.attr("cy", 0)
.attr("r", 50)
.style("fill", "yellow");
//левый глаз
smile.append("circle")
.attr("cx", -20)
.attr("cy", -10)
.attr("r", 5);
//правый глаз
smile.append("circle")
.attr("cx", 20)
.attr("cy", -10)
.attr("r", 5);
// улыбка
let arc = d3.arc()
.innerRadius(35)
.outerRadius(35);
smile.append("path")
.attr("d", arc({startAngle: Math.PI /3 * 2, endAngle: Math.PI/3 * 4}))
.style("stroke", "brown")
return smile
}

View File

@@ -0,0 +1,88 @@
document.addEventListener("DOMContentLoaded", function () {
const width = 600;
const height = 600;
const svg = d3.select("svg")
.attr("width", width)
.attr("height", height);
// let pict1 = drawSmile(svg);
// pict1.attr("transform", `translate(400, 400) scale(1.5, 1.5) rotate(180)`);
// let pict = drawSmile(svg);
// pict.attr("transform", "translate(200, 200)");
setting.applyButton.addEventListener("click", () => {
draw(setting);
})
setting.clearButton.addEventListener("click", () => {
svg.selectAll('*').remove()
})
updateAnimationControllsDisplay();
enableAnimCheckbox.addEventListener("change", updateAnimationControllsDisplay);
animAlongPathCheckbox.addEventListener("change", updateAnimationControllsDisplay);
startAnimButton.addEventListener("click", () => {
animRouter(setting)
})
})
const updateAnimationControllsDisplay = () => {
let isChecked = enableAnimCheckbox.checked;
let isPathAnim = animAlongPathCheckbox.checked;
// console.log(isChecked);
document.querySelectorAll(".anim-related").forEach((elem) => { elem.hidden = !isChecked });
document.querySelectorAll(".anim-related-inverse").forEach((elem) => { elem.hidden = isChecked });
document.querySelectorAll(".path-anim-related").forEach((elem) => { elem.hidden = !(isPathAnim && isChecked) });
document.querySelectorAll(".path-anim-related-inverse").forEach((elem) => { elem.hidden = (isPathAnim && isChecked) });
}
const draw = (dataForm) => {
const svg = d3.select("svg")
let pict = drawSmile(svg);
pict.attr("transform", `
translate(${dataForm.cx.value},
${dataForm.cy.value})
scale(${dataForm.sx.value},
${dataForm.sy.value})
rotate(${dataForm.r.value})`);
}
const animRouter = (dataForm) => {
if (dataForm.animAlongPathCheckbox.checked) {
let path = drawPath(Number(dataForm.pathSelect.value));
const svg = d3.select("svg")
let pict = drawSmile(svg);
pict.transition()
.ease([d3.easeLinear, d3.easeElastic, d3.easeBounce][Number(animTypeSelect.value)])
.duration(6000)
.attrTween('transform', translateAlong(path.node()));
}
else {
runAnimation(dataForm)
}
}
const runAnimation = (dataForm) => {
const svg = d3.select("svg")
let pict = drawSmile(svg);
pict.attr("transform", `
translate(${dataForm.cx.value},
${dataForm.cy.value})
scale(${dataForm.sx.value},
${dataForm.sy.value})
rotate(${dataForm.r.value})`)
.transition()
.duration(6000)
.ease([d3.easeLinear, d3.easeElastic, d3.easeBounce][Number(animTypeSelect.value)])
.attr("transform", `
translate(${dataForm.cx_finish.value},
${dataForm.cy_finish.value})
scale(${dataForm.sx_finish.value},
${dataForm.sy_finish.value})
rotate(${dataForm.r_finish.value})`);
}

View File

@@ -0,0 +1,90 @@
/* массив точек пути будет иметь следующий вид:
[
{x: координата, y: координата},
{x: координата, y: координата},
...
]
*/
// создаем массив точек, расположенных буквой "Г"
function createPathG() {
const svg = d3.select("svg")
const width = svg.attr("width")
const height = svg.attr("height")
let data = [];
const padding = 100;
//начальное положение рисунка
let posX = padding;
let posY = height - padding;
const h = 5;
// координаты y - уменьшаются, x - постоянны
while (posY > padding) {
data.push( {x: posX, y: posY});
posY -= h;
}
// координаты y - постоянны, x - увеличиваются
while (posX < width - padding) {
data.push( {x: posX, y: posY});
posX += h;
}
return data
}
// создаем массив точек, расположенных по кругу
function createPathCircle() {
const svg = d3.select("svg")
const width = svg.attr("width")
const height = svg.attr("height")
let data = [];
// используем параметрическую форму описания круга
// центр расположен в центре svg-элемента, а радиус равен трети высоты/ширины
for (let t = 0 ; t <= Math.PI * 2; t += 0.1) {
data.push(
{x: width / 2 + width / 3 * Math.sin(t),
y: height / 2 + height / 3 * Math.cos(t)}
);
}
return data
}
function createPathSpiral() {
const svg = d3.select("svg")
const width = svg.attr("width")
const height = svg.attr("height")
let data = [];
// используем параметрическую форму описания круга
// центр расположен в центре svg-элемента, а радиус равен трети высоты/ширины
for (let t = 0 ; t <= Math.PI * 6; t += 0.1) {
data.push(
{x: width / 2 + width / 3*(1-(t/Math.PI * 6)) * Math.sin(t),
y: height / 2 + height / 3*(1-(t/Math.PI * 6)) * Math.cos(t)}
);
}
return data
}
const drawPath =(typePath) => {
// создаем массив точек
const dataPoints = [createPathG,createPathCircle,createPathSpiral][Number(typePath)]()
const line = d3.line()
.x((d) => d.x)
.y((d) => d.y);
const svg = d3.select("svg")
// создаем путь на основе массива точек
const path = svg.append('path')
.attr('d', line(dataPoints))
// .attr('stroke', 'black')
.attr('fill', 'none');
return path;
}
function translateAlong(path) {
const length = path.getTotalLength();
return function() {
return function(t) {
const {x, y} = path.getPointAtLength(t * length);
return `translate(${x},${y})`;
}
}
}

72
labs/lab2/index.html Normal file
View File

@@ -0,0 +1,72 @@
<!DOCTYPE html>
<html lang="ru<head>
<meta charset=" utf-8">
<title> Трансформация и анимация</title>
<link rel="stylesheet" href="CSS/style.css">
<script src="JavaScript/d3.v7.min.js"> </script>
<script src="JavaScript/main.js"></script>
<script src="JavaScript/image.js"></script>
<script src="JavaScript/path.js"></script>
</head>
<body>
<form id="setting">
<p class="path-anim-related-inverse">Координаты рисунка<br>
<label for="cx">x: </label>
<input type="number" id="cx" value="300" max="600" min="0">
<label class="anim-related" for="cx_finish">до: </label>
<input class="anim-related" type="number" id="cx_finish" value="300" max="600" min="0">
<br>
<label for="cy">y: </label>
<input type="number" id="cy" value="300" max="600" min="0">
<label class="anim-related" for="cy_finish">до: </label>
<input class="anim-related" type="number" id="cy_finish" value="300" max="600" min="0">
</p>
<p class="path-anim-related">Пути перемещения<br>
<select id="pathSelect">
<option value="0">Буквой "Г"</option>
<option value="1">По кругу</option>
</select>
</p>
<p class="path-anim-related-inverse" >Масштаб<br>
<label for="sx">по x: </label>
<input type="number" id="sx" value="1" max="100" min="-100">
<label class="anim-related" for="sx_finish">до: </label>
<input class="anim-related" type="number" id="sx_finish" value="1.5" max="100" min="-100">
<br>
<label for="sy">по y: </label>
<input type="number" id="sy" value="1" max="100" min="-100">
<label class="anim-related" for="sy_finish">до: </label>
<input class="anim-related" type="number" id="sy_finish" value="1.5" max="100" min="-100">
</p>
<p class="path-anim-related-inverse">Поворот<br>
<label for="r">x: </label>
<input type="number" id="r" value="0" max="360" min="-360">
<label class="anim-related" for="r_finish">до: </label>
<input class="anim-related" type="number" id="r_finish" value="360" max="360" min="-360">
</p>
<p>
<input type="button" class="anim-related-inverse" id="applyButton" value="Нарисовать">
<input type="button" id="clearButton" value="Отчистить">
</p>
<p>
Включить Анимацию? <input type="checkbox" id="enableAnimCheckbox"><br>
<label class="anim-related" for="animAlongPathCheckbox"> Перемещение вдоль пути? </label><input type="checkbox" id="animAlongPathCheckbox" class="anim-related">
<select class="anim-related" id="animTypeSelect">
<option value="0">linear</option>
<option value="1">elastic</option>
<option value="2">bounce</option>
</select>
<br>
<input class="anim-related" type="button" id="startAnimButton" value="Анимировать">
</p>
</form>
<svg></svg>
</body>
</html>

2
labs/lab2/task/d3.v7.min.js vendored Normal file

File diff suppressed because one or more lines are too long

12
labs/lab2/task/index.html Normal file
View File

@@ -0,0 +1,12 @@
<!DOCTYPE html>
<html lang="ru<head>
<meta charset=" utf-8">
<title> Практика 2</title>
<script src="d3.v7.min.js"> </script>
<script src="main.js"></script>
</head>
<body>
</body>
</html>

40
labs/lab2/task/main.js Normal file
View File

@@ -0,0 +1,40 @@
const books = [
{
title: 'Мастер и Маргарита',
author: 'Булгаков М.А.'
},
{
title: 'Белая гвардия',
author: 'Булгаков М.А.'
},
{
title: 'Война и мир',
author: 'Толстой Л.Н.'
},
{
title: 'Анна Каренина',
author: 'Толстой Л.Н.'
},
{
title: 'Игрок',
author: 'Достоевский Ф.М.'
}
];
const booksByAuthor = d3.group(books, d => d["author"])
document.addEventListener("DOMContentLoaded", function () {
temp = d3.selectAll("p")
.data(booksByAuthor)
.enter()
.append("p")
.text(d => d[0])
.append("select")
.selectAll("option")
.data(d => [{ title: "выбрать все" }, ...d[1]])
.enter()
.append("option")
.text(d => d["title"])
})

112
old/hw/hw6/hw6.js Normal file
View File

@@ -0,0 +1,112 @@
let templates = [
[
"X--X----",
"-XXX-X--",
"----X---",
"----X---",
"------XX",
"----XX--",
"X-------",
"-XX--X--"
],
[
"---XX---",
"----X-X-",
"---XX---",
"---X----",
"-X------",
"X----X-X",
"--X--X--",
"-X----XX"
],
[
"-X-X----",
"---X-X--",
"X-XX--X-",
"--X-X---",
"------X",
"--------",
"X-----X-",
"-X-X---X"
],
[
"-XX-X---",
"-----X--",
"----X---",
"-----X-X",
"-X-X--X-",
"-XX----X",
"-X------",
"XX------"
], [
"----XX-X",
"X-------",
"--------",
"-----XX-",
"---X-X--",
"-X---XX-",
"-X-X----",
"----XXX-"
], [
"XX------",
"-----X--",
"-------X",
"-X-X----",
"X-X----X",
"-X-X-X-X",
"----X-X-",
"-X------"
],
];
const squareSize = templates[0].length;
//convert to arrays for mutability
let temp = []
for (let n = 0; n < templates.length; n++) {
temp.push([])
for (let i = 0; i < squareSize; i++) {
temp[n].push([]);
for (let j = 0; j < squareSize; j++) {
temp[n][i].push(templates[n][i][j] == "X" ? 1 : 0);
}
}
}
templates = temp;
const encodedMsg = "Р_НАЙА_СЛЗДЕОСЖСТОИКНОЛЬЛЬТКУЮО__КЗАЕВАОКАЧОЖЗ_УЧАСМОДУ_ТЮЖЕ";
function rotateTemplate(template) {
for (let i = 0; i < squareSize; i++) {
for (let j = 0; j < i; j++) {
let temp = template[i][j];
template[i][j] = template[j][i];
template[j][i] = temp;
}
}
for (let i = 0; i < squareSize; i++) {
for (let j = 0; j < squareSize / 2; j++) {
let temp = template[i][j];
template[i][j] = template[i][(squareSize - 1) - j];
template[i][(squareSize - 1) - j] = temp;
}
}
}
for (let n = 0; n < templates.length; n++) {
let out = ""
for (let rot = 0; rot < 4; rot++) {
for (let i = 0; i < squareSize; i++) {
for (let j = 0; j < squareSize; j++) {
if (templates[n][i][j]) {
out += encodedMsg[i * squareSize + j];
}
}
}
rotateTemplate(templates[n]);
}
console.log(out);
console.log("=======");
}

BIN
old/hw/hw7/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

108
old/hw/hw7/hw7.css Normal file
View File

@@ -0,0 +1,108 @@
.desctription-heading{
font-size: medium;
text-align: center;
}
.main-container {
display: flex;
gap: 20px;
align-items: flex-start;
}
.scheme-image{
flex: 1;
max-width: 400px;
height: auto;
object-fit: contain;
}
.controls-container {
display: flex;
flex-direction: column;
gap: 20px;
flex: 1;
min-width: 300px;
max-width: 400px;
}
.inputs{
min-width: 300px;
}
.outputs{
min-width: 300px;
}
.radio-label{
display: inline-block;
}
p{
line-height: 0;
}
.max-w{
width: 100%;
}
.bad-input-tooltip {
display: block;
background: #ffffff;
color: #721c24;
border: 1px solid #8f0c19;
padding: 4px 8px;
max-width: 220px;
white-space: normal;
}
.bad-input-highlight {
outline: 2px solid #990917;
}
.hidden{
display: none;
}
body{
display: flex;
flex-direction: column;
gap: 20px;
}
table {
width: 100%;
border-collapse: collapse;
margin: 16px 0;
}
th, td {
border: 1px solid #ccc;
text-align: left;
}
th {
background-color: #f5f5f5;
font-weight: bold;
}
tr:nth-child(even) {
background-color: #fafafa;
}
@media (max-width: 800px) {
.main-container {
flex-direction: column;
align-items: center;
}
.scheme-image {
max-width: 100%;
width: 100%;
}
.controls-container {
width: 100%;
max-width: none;
}
.inputs, .outputs {
min-width: unset;
width: 100%;
}
}

71
old/hw/hw7/hw7.html Normal file
View File

@@ -0,0 +1,71 @@
<html>
<head>
<title>калькулятор равнобедренного треугольника</title>
<link rel="stylesheet" href="./hw7.css">
<link rel="icon" href="favicon.ico">
<link rel="stylesheet" href="https://unpkg.com/xp.css/dist/98.css"
>
</head>
<body>
<h3 class="desctription-heading">калькулятор равнобедренного треугольника</h3>
<div class="main-container">
<div class="image-container">
<img src="./imgs/type1.png" class="scheme-image" id="schemeImageA">
<img src="./imgs/type2.png" class="scheme-image hidden" id="schemeImageB">
</div>
<div class="controls-container">
<div class="inputs" id="inputsForm">
<p>Выберете варинат входных данных:</p>
<input type="radio" name="input-type" id="radioButtonA" checked>
<label class="radio-label" for="radioButtonA">
<span>боковая сторона и угол при основании</span>
</label><br>
<input type="radio" name="input-type" id="radioButtonB">
<label class="radio-label" for="radioButtonB">
<span>основание и прилежащий к нему угол</span>
</label>
<form>
<fieldset id="inputTypeFieldsetA">
<legend>входные данные</legend>
<label for="equalSideLength">Длина боковой стороны B:<span class="bad-input-tooltip hidden" id="equalSideLengthErr"></span></label><br>
<input class="max-w" type="number" min="0" id="equalSideLength">
<br>
<label for="baseAngle">Угол при основании α (в градусах):<span class="bad-input-tooltip hidden" id="baseAngleErr"></span></label><br>
<input class="max-w" type="number" min="0" max="180" id="baseAngle">
</fieldset>
<fieldset id="inputTypeFieldsetB" class="hidden">
<legend>входные данные</legend>
<label for="baseSideLength">Длина основания A:<span class="bad-input-tooltip hidden" id="baseSideLengthErr"></span></label><br>
<input class="max-w" type="number" min="0" id="baseSideLength">
<br>
<label for="baseAdjacentAngle">Угол прилежащий основанию β:<span class="bad-input-tooltip hidden" id="baseAdjacentAngleErr"></span></label><br>
<input class="max-w" type="number" min="0" max="180" id="baseAdjacentAngle">
</fieldset>
<fieldset id="outputsFieldset">
<legend>что вычислить</legend>
<span class="bad-input-tooltip hidden" id="outputValuesErr"><br></span>
<input type="checkbox" name="output-valuesR" id="outputValuesR" checked>
<label for="outputValuesR">радиус вписанной окружности</label><br>
<input type="checkbox" name="output-valuesH" id="outputValuesH" checked>
<label for="outputValuesH">высоты треугольника</label><br>
<input type="checkbox" name="output-valuesL" id="outputValuesL" checked>
<label for="outputValuesL">длины сторон треугольника</label><br>
</fieldset>
<button id="calculateButton" disabled>Вычислить</button>
<button id="clearButton">Отчислить</button>
</form>
</div>
<table class="outputs hidden" id="outputsTable">
</table>
</div>
</div>
</body>
<script src="./hw7.js">
</script>
</html>

357
old/hw/hw7/hw7.js Normal file
View File

@@ -0,0 +1,357 @@
let elements = {
radioA: document.getElementById('radioButtonA'),
radioB: document.getElementById('radioButtonB'),
fieldsetA: document.getElementById('inputTypeFieldsetA'),
fieldsetB: document.getElementById('inputTypeFieldsetB'),
imageA: document.getElementById('schemeImageA'),
imageB: document.getElementById('schemeImageB'),
inputsForm: document.getElementById('inputsForm'),
equalSideLength: document.getElementById('equalSideLength'),
equalSideLengthErr: document.getElementById('equalSideLengthErr'),
baseAngle: document.getElementById('baseAngle'),
baseAngleErr: document.getElementById('baseAngleErr'),
baseSideLength: document.getElementById('baseSideLength'),
baseSideLengthErr: document.getElementById('baseSideLengthErr'),
baseAdjacentAngle: document.getElementById('baseAdjacentAngle'),
baseAdjacentAngleErr: document.getElementById('baseAdjacentAngleErr'),
outputsFieldset: document.getElementById('outputsFieldset'),
outputValuesErr: document.getElementById('outputValuesErr'),
outputR: document.getElementById('outputValuesR'),
outputH: document.getElementById('outputValuesH'),
outputL: document.getElementById('outputValuesL'),
calculateButton: document.getElementById('calculateButton'),
clearButton: document.getElementById('clearButton'),
outputsTable: document.getElementById('outputsTable')
}
console.log("loaded js!");
let currentModeIsA = true;
let outputFields = {};
let inputsA = {
l: null,
angle: null
};
let inputsB = {
l: null,
angle: null
};
function highlightErrorElement(errorFieldIndex, add = true) {
let fields = [
elements.equalSideLength,
elements.baseAngle,
elements.baseSideLength,
elements.baseAdjacentAngle,
outputsFieldset
];
if (fields[errorFieldIndex]) {
if (add) {
fields[errorFieldIndex].classList.add("bad-input-highlight");
}
else {
fields[errorFieldIndex].classList.remove("bad-input-highlight");
}
}
}
function setErrTooltip(errorFieldIndex, tooltip) {
let errElem = [
elements.equalSideLengthErr,
elements.baseAngleErr,
elements.baseSideLengthErr,
elements.baseAdjacentAngleErr,
elements.outputValuesErr
][errorFieldIndex];
if (errElem) {
highlightErrorElement(errorFieldIndex, tooltip !== "");
if (tooltip !== "") {
errElem.classList.remove("hidden");;
}
else {
errElem.classList.add("hidden");;
}
if (errorFieldIndex == 4) {
tooltip += "<br>"
}
errElem.innerHTML = tooltip;
}
}
function deg2rad(degrees) {
return degrees * (Math.PI / 180);
}
function getCalculations() {
let heights = [];//base related, side realted
let lengths = [];// base side
let radius = 0;
let baseAngle = 0;
let sideAngle = 0;
if (currentModeIsA) {
lengths[0] = 2 * Math.sin(deg2rad(inputsA.angle) / 2) * inputsA.l;
lengths[1] = inputsA.l;
baseAngle = inputsA.angle;
sideAngle = (180 - baseAngle) / 2;
}
else {
lengths[0] = inputsB.l;
lengths[1] = inputsB.l / (2 * Math.cos(deg2rad(inputsB.angle)));
sideAngle = inputsB.angle;
baseAngle = 180 - 2 * sideAngle;
}
let a = lengths[0];
let b = lengths[1];
let p = (a + b + b) / 2;
radius = Math.sqrt((p - a) * (p - b) * (p - b) / p);
heights[0] = lengths[1] * Math.sin(deg2rad(sideAngle));
heights[1] = lengths[0] * Math.sin(deg2rad(baseAngle));
return {
radius: radius,
heights: heights,
lengths: lengths
};
}
function updateCheckboxes() {
outputFields["radius"] = elements.outputR.checked;
outputFields["heights"] = elements.outputH.checked;
outputFields["lengths"] = elements.outputL.checked;
let ret = !outputFields.radius && !outputFields.heights && !outputFields.lengths;
if (ret) {
setErrTooltip(4, "Выберите хотя бы один параметр для вывода.");
} else {
setErrTooltip(4, "");
}
updateCalculateButton();
}
function updateCalculateButton() {
let ret = false;
if (currentModeIsA) {
ret = inputsA["l"] !== null && inputsA["angle"] !== null
}
else {
ret = inputsB["l"] !== null && inputsB["angle"] !== null
}
ret &= elements.outputR.checked || elements.outputH.checked || elements.outputL.checked;
elements.calculateButton.disabled = !ret;
}
function switchInputs(selectA) {
if (selectA) {
elements.imageA.classList.remove("hidden");
elements.imageB.classList.add("hidden");
elements.fieldsetA.classList.remove("hidden");
elements.fieldsetB.classList.add("hidden");
}
else {
elements.imageA.classList.add("hidden");
elements.imageB.classList.remove("hidden");
elements.fieldsetA.classList.add("hidden");
elements.fieldsetB.classList.remove("hidden");
}
updateCalculateButton();
}
function displayTable(calcResults) {
let table = elements.outputsTable;
table.innerHTML = ""; // Clear previous content
let header = "<tr><th>Параметр</th><th>Значение</th></tr>";
let rows = "";
if (outputFields.radius) {
rows += `<tr><td>R</td><td>${calcResults.radius.toFixed(3)}</td></tr>`;
}
if (outputFields.heights) {
rows += `<tr><td>h1</td><td>${calcResults.heights[0].toFixed(3)}</td></tr>`;
rows += `<tr><td>h2</td><td>${calcResults.heights[1].toFixed(3)}</td></tr>`;
}
if (outputFields.lengths) {
rows += `<tr><td>A</td><td>${calcResults.lengths[0].toFixed(3)}</td></tr>`;
rows += `<tr><td>B</td><td>${calcResults.lengths[1].toFixed(3)}</td></tr>`;
}
table.innerHTML = header + rows;
if (rows === "") {
table.innerHTML = `<h3>Выберите параметры для вычислений, сейчас таблица пустая</h3>`;
}
table.classList.remove("hidden");
}
function clearCurrentInputs() {
if (currentModeIsA) {
elements.equalSideLength.value = null;
elements.baseAngle.value = null;
setErrTooltip(0, "");
setErrTooltip(1, "");
inputsA = {
l: null,
angle: null
};
}
else {
inputsB = {
l: null,
angle: null
};
setErrTooltip(2, "");
setErrTooltip(3, "");
elements.baseSideLength.value = null;
elements.baseAdjacentAngle.value = null;
}
elements.outputsTable.classList.add("hidden");
updateCalculateButton()
}
function check180Angle(angleString) {
let angle = parseFloat(angleString);
if (isNaN(angle)) {
return "Введите корректное число";
}
if (angle <= 0) {
return "Угол должен быть больше 0";
}
if (angle >= 180) {
return "Угол должен быть меньше 180";
}
return "";
}
function check90Angle(angleString) {
let angle = parseFloat(angleString);
if (isNaN(angle)) {
return "Введите корректное число";
}
if (angle <= 0) {
return "Угол должен быть больше 0";
}
if (angle >= 90) {
return "Угол должен быть меньше 90";
}
return "";
}
function checkLength(lengthString) {
let length = parseFloat(lengthString);
if (!isFinite(length)) {
return "Введите корректное число";
}
if (length <= 0) {
return "Длина должна быть больше 0";
}
return "";
}
function handleSwitchInputs(event) {
if (event.target.id[event.target.id.length - 1].toLowerCase() == "a") {
currentModeIsA = true;
switchInputs(true);
console.log("Switching inputs to A");
}
else {
currentModeIsA = false;
switchInputs(false);
console.log("Switching inputs to B");
}
}
function handleInputsUpdate(event) {
switch (event.target.id) {
case "equalSideLength":
{
const err = checkLength(event.target.value);
setErrTooltip(0, err);
if (err === "") {
inputsA.l = parseFloat(event.target.value);
} else {
inputsA.l = null;
}
}
break;
case "baseAngle":
{
const err = check180Angle(event.target.value);
setErrTooltip(1, err);
if (err === "") {
inputsA.angle = parseFloat(event.target.value);
} else {
inputsA.angle = null;
}
}
break;
case "baseSideLength":
{
const err = checkLength(event.target.value);
setErrTooltip(2, err);
if (err === "") {
inputsB.l = parseFloat(event.target.value);
} else {
inputsB.l = null;
}
}
break;
case "baseAdjacentAngle":
{
const err = check90Angle(event.target.value);
setErrTooltip(3, err);
if (err === "") {
inputsB.angle = parseFloat(event.target.value);
} else {
inputsB.angle = null;
}
}
break;
default:
console.log(event.target.id)
break;
}
updateCalculateButton();
}
function handleFieldsetEvent(event) {
if (event.target.type === 'radio') {
handleSwitchInputs(event);
} else if (event.target.type === 'number') {
handleInputsUpdate(event);
}
}
elements.inputsForm.addEventListener('change', handleFieldsetEvent);
elements.inputsForm.addEventListener('input', handleFieldsetEvent);
elements.outputsFieldset.addEventListener('change', (event) => {
if (event.target.type === 'checkbox') {
updateCheckboxes();
}
});
elements.clearButton.addEventListener('click', (event) => {
event.preventDefault();
clearCurrentInputs()
});
elements.calculateButton.addEventListener('click', (event) => {
event.preventDefault();
displayTable(getCalculations());
});
// чтобы не сконфузило
updateCheckboxes();
switchInputs(elements.radioA.checked);

BIN
old/hw/hw7/imgs/type1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

BIN
old/hw/hw7/imgs/type2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

View File

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

View File

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

View File

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 28 KiB

View File

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 31 KiB

View File

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 17 KiB

View File

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

Before

Width:  |  Height:  |  Size: 35 KiB

After

Width:  |  Height:  |  Size: 35 KiB

View File

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

View File

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 28 KiB

View File

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 24 KiB

View File

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 27 KiB

1
old/labs/fake-lab5/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
node_modules

6695
old/labs/fake-lab5/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,30 @@
{
"name": "template_pug",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"build": "webpack --mode production",
"serve": "webpack serve --open --mode development",
"dev": "webpack --mode development",
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"chokidar": "^4.0.3",
"css-loader": "^6.8.1",
"filemanager-webpack-plugin": "^8.0.0",
"glob": "^11.0.3",
"html-webpack-plugin": "^5.5.3",
"pug": "^2.0.4",
"pug-loader": "^2.4.0",
"style-loader": "^3.3.3",
"stylus": "^0.61.0",
"stylus-loader": "^7.1.3",
"webpack": "^5.89.0",
"webpack-cli": "^5.1.4",
"webpack-dev-server": "^4.15.1"
}
}

View File

@@ -0,0 +1,36 @@
block variables
- var title = 'Самые высокие здания и сооружения'
-
var dictMenu = {'Главная': 'index.html',
'Небоскребы' : "#",
'Башни': "#",
'Список': "#"
}
- var listImages = ['image1.jpg', 'image2.jpg', 'image3.jpg', 'image4.jpg', 'image5.jpg']
-
var listSections = [
{'header' : 'Бурдж-Халифа в Дубае, ОАЭ',
'image' : 'image7.jpg',
'text' : 'Небоскрёб высотой 828 метров, самое высокое сооружение в мире. Форма здания напоминает сталагмит.'
},
{'header' : 'Токийское небесное дерево, Япония',
'image' : 'image6.jpg',
'text' : 'Телевизионная башня в районе Сумида самая высокая среди телебашен мира. Высота телебашни вместе с антенной составляет 634 метра.'
},
{'header' : 'CN Tower, Канада',
'image' : 'image8.jpg',
'text' : 'Си-Эн Тауэр — самое высокое свободно стоящее сооружение в мире с 1976 по 2007 год. Его высота составляет 553,33 метра.'
}
]
-
var structures = [
{
menuItem: "Ворота Запада, США",
pict: "image1_big.jpg",
firstP: `Арка в Сент-Луисе, также известная под именем «Врата на запад» — мемориал, являющийся частью Джефферсоновского национального экспансиального мемориала, а также визитной карточкой Сент-Луиса, штат Миссури, США.`,
secondP: `Арка была спроектирована финско-американским архитектором Ээро Саариненом в 1947 году. Её высота 192 метра в самой высокой точке, ширина её основания также 192 метра.
Таким образом арка является самым высоким памятником на территории США. Её строительство началось 12 февраля 1963 года и было закончено 28 октября 1965 года. Памятник открылся для посетителей 24 июля 1967 года.`
}
]

View File

@@ -0,0 +1,20 @@
include ../components/data.pug
mixin createMenu(menu, active)
each value, key in menu
if active == key
a.active(href= value)= key
else
a(href= value)= key
mixin createMenuBuild(menu, active)
each value, key in menu
a(href= value)= key
a.active(href= "")= active
mixin createSection()
each item in listSections
section
h3= item.header
img(src= require("../images/" + item.image))
p= item.text
a(href="#") &hellip;

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Some files were not shown because too many files have changed in this diff Show More