script.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554
  1. // Store Class
  2. // Переделайте задание Store на синтаксис ES6-классов:
  3. // Добавьте нужные параметры в методы, их код, а так же геттер state, который работает аналогично
  4. // getState. Проверьте на ларьке, ведь объект, созданный из этого класса будет таким же, как и
  5. // объект, созданный createStore
  6. store_class: {
  7. class Store {
  8. #reducer(state, { type, name, amount, money }) { //объект action деструктуризируется на три переменных
  9. if (!state) { //начальная уборка в ларьке:
  10. return {
  11. products: {
  12. пиво: {
  13. amount: 100,
  14. price: 30,
  15. },
  16. чипсы: {
  17. amount: 100,
  18. price: 25,
  19. },
  20. сиги: {
  21. amount: 100,
  22. price: 35,
  23. }
  24. },
  25. balance: {
  26. amount: 0
  27. }
  28. }
  29. }
  30. if (type === 'buy') { //если тип action - КУПИТЬ, то:
  31. if (amount > state.products[name].amount) {
  32. alert('You have entered more quantity than is available');
  33. return {
  34. ...state,
  35. }
  36. }
  37. if (money < amount * state.products[name].price) {
  38. alert('You don`t have enought money');
  39. return {
  40. ...state,
  41. }
  42. }
  43. if (money > amount * state.products[name].price) {
  44. alert('You gave more money');
  45. return {
  46. ...state,
  47. }
  48. }
  49. let updatedProducts = state.products
  50. let updatedField = updatedProducts[name];
  51. updatedField.amount -= amount;
  52. let updatedBalance = state.balance;
  53. updatedBalance.amount += +money;
  54. return {
  55. ...state, //берем все что было из ассортимента
  56. products: updatedProducts, //и уменьшаем то, что покупается на количество
  57. balance: updatedBalance,
  58. }
  59. }
  60. return state; //если мы не поняли, что от нас просят в `action` - оставляем все как есть
  61. }
  62. #state = this.#reducer(undefined, {});
  63. #cbs = [];
  64. constructor() {
  65. }
  66. get state() {
  67. return this.#state;
  68. }
  69. subscribe(cb) {
  70. this.#cbs.push(cb);
  71. return () => this.#cbs = this.#cbs.filter(c => c !== cb);
  72. }
  73. dispatch(action) {
  74. const newState = this.#reducer(this.#state, action); //пробуем запустить редьюсер
  75. if (newState !== this.#state) { //проверяем, смог ли редьюсер обработать action
  76. this.#state = newState; //если смог, то обновляем state
  77. for (let cb of this.#cbs) cb(); //и запускаем подписчиков
  78. }
  79. }
  80. }
  81. const store = new Store;
  82. const unsubscribe = store.subscribe(() => console.log(store.state));
  83. setTimeout(unsubscribe, 10000); //отпишемся через 10 секунд, например
  84. const buyAction = (name, amount, money) => ({ type: 'buy', name, amount, money });
  85. store.dispatch(buyAction('пиво', 1, 30));
  86. store.dispatch(buyAction('чипсы', 4, 100));
  87. }
  88. // Password Class
  89. // По аналогии, переделайте код задания Password в синтаксис классов ES6. Спрячьте все что можно в
  90. // #приватные свойства объектов класса. Проверьте на форме логина - ведь она использует Password
  91. password_class: {
  92. class Password {
  93. #passInputEl = document.createElement('input');
  94. #passVisibilityCheckboxEl = document.createElement('input');
  95. constructor(parent, open) {
  96. parent.append(this.#passInputEl);
  97. this.#passVisibilityCheckboxEl.type = 'checkbox';
  98. this.#passVisibilityCheckboxEl.checked = open;
  99. parent.append(this.#passVisibilityCheckboxEl);
  100. if (open) {
  101. this.#passInputEl.type = 'text';
  102. } else {
  103. this.#passInputEl.type = 'password';
  104. }
  105. this.#passVisibilityCheckboxEl.addEventListener('change', (event) => {
  106. if (event.currentTarget.checked) {
  107. this.#passInputEl.type = 'text';
  108. } else {
  109. this.#passInputEl.type = 'password';
  110. }
  111. if (this.onOpenChange) {
  112. this.onOpenChange(event.currentTarget.checked);
  113. }
  114. });
  115. this.#passInputEl.addEventListener('input', (event) => {
  116. this.onChange();
  117. });
  118. }
  119. set value(value) {
  120. this.#passInputEl.value = value;
  121. }
  122. get value() {
  123. return this.#passInputEl.value;
  124. }
  125. set open(value) {
  126. this.#passVisibilityCheckboxEl.checked = value;
  127. }
  128. get open() {
  129. return this.#passVisibilityCheckboxEl.checked;
  130. }
  131. }
  132. function Login(parent) {
  133. let loginInputEl = document.createElement('input');
  134. loginInputEl.type = 'text';
  135. parent.append(loginInputEl);
  136. loginInputEl.addEventListener('input', (event) => {
  137. this.onChange();
  138. });
  139. this.setValue = (value) => {
  140. loginInputEl.value = value;
  141. }
  142. this.getValue = () => {
  143. return loginInputEl.value;
  144. }
  145. }
  146. let form = document.createElement('form');
  147. document.body.append(form);
  148. let loginLabel = document.createElement('label');
  149. loginLabel.innerText = 'Login: ';
  150. form.append(loginLabel);
  151. let login = new Login(form);
  152. let passwordLabel = document.createElement('label');
  153. passwordLabel.innerText = 'Password: ';
  154. form.append(passwordLabel);
  155. let password = new Password(form, false);
  156. let submit = document.createElement('button');
  157. submit.innerText = 'Submit';
  158. form.append(submit);
  159. function validateForm() {
  160. if (login.getValue() == '' || password.value == '') {
  161. submit.disabled = true;
  162. } else {
  163. submit.disabled = false;
  164. }
  165. }
  166. validateForm();
  167. login.onChange = validateForm;
  168. password.onChange = validateForm;
  169. }
  170. // StoreThunk Class
  171. // Унаследуйте класс Store в новом классе StoreThunk. Новый класс должен перекрывать метод dispatch, проверять
  172. // тип переданного экшона и если это функция, запускать её, передав у неё this.dispatch и this.getState. Данное
  173. // условие написано тут. Учтите, что в thunk передаются функции dispatch и getState без объекта до точечки, а
  174. // эти методы в классе Store являются обычными функциями, склонными к потере this. Для прибития this намертво к
  175. // функции используйте метод bind. Посмотреть можно тут и тут Проверьте на модульном проекте
  176. store_thunk_class: {
  177. class Store {
  178. #reducer(state, { type, name, amount, money }) { //объект action деструктуризируется на три переменных
  179. if (!state) { //начальная уборка в ларьке:
  180. return {
  181. products: {
  182. пиво: {
  183. amount: 100,
  184. price: 30,
  185. },
  186. чипсы: {
  187. amount: 100,
  188. price: 25,
  189. },
  190. сиги: {
  191. amount: 100,
  192. price: 35,
  193. }
  194. },
  195. balance: {
  196. amount: 0
  197. }
  198. }
  199. }
  200. if (type === 'buy') { //если тип action - КУПИТЬ, то:
  201. if (amount > state.products[name].amount) {
  202. alert('You have entered more quantity than is available');
  203. return {
  204. ...state,
  205. }
  206. }
  207. if (money < amount * state.products[name].price) {
  208. alert('You don`t have enought money');
  209. return {
  210. ...state,
  211. }
  212. }
  213. if (money > amount * state.products[name].price) {
  214. alert('You gave more money');
  215. return {
  216. ...state,
  217. }
  218. }
  219. let updatedProducts = state.products
  220. let updatedField = updatedProducts[name];
  221. updatedField.amount -= amount;
  222. let updatedBalance = state.balance;
  223. updatedBalance.amount += +money;
  224. return {
  225. ...state, //берем все что было из ассортимента
  226. products: updatedProducts, //и уменьшаем то, что покупается на количество
  227. balance: updatedBalance,
  228. }
  229. }
  230. return state; //если мы не поняли, что от нас просят в `action` - оставляем все как есть
  231. }
  232. #state = this.#reducer(undefined, {});
  233. #cbs = [];
  234. constructor() {
  235. }
  236. get state() {
  237. return this.#state;
  238. }
  239. subscribe(cb) {
  240. this.#cbs.push(cb);
  241. return () => this.#cbs = this.#cbs.filter(c => c !== cb);
  242. }
  243. dispatch(action) {
  244. const newState = this.#reducer(this.#state, action); //пробуем запустить редьюсер
  245. if (newState !== this.#state) { //проверяем, смог ли редьюсер обработать action
  246. this.#state = newState; //если смог, то обновляем state
  247. for (let cb of this.#cbs) cb(); //и запускаем подписчиков
  248. }
  249. }
  250. }
  251. class StoreThunk extends Store {
  252. constructor(...params) {
  253. super(...params);
  254. console.log(this)
  255. }
  256. dispatch(action) {
  257. if (typeof action === 'function') { //если action - не объект, а функция
  258. return action(this.dispatch.bind(this), this.state) //запускаем эту функцию и даем ей dispatch и getState для работы
  259. }
  260. super.dispatch(action);
  261. }
  262. }
  263. const storeTh = new StoreThunk;
  264. const unsubscribe = storeTh.subscribe(() => console.log(storeTh.state));
  265. setTimeout(unsubscribe, 10000);
  266. const buyAction = (name, amount, money) => ({ type: 'buy', name, amount, money });
  267. const actionЗатарится = () =>
  268. dispatch => {
  269. dispatch(buyAction('пиво', 1, 30));
  270. dispatch(buyAction('чипсы', 5, 125));
  271. }
  272. storeTh.dispatch(actionЗатарится());
  273. }
  274. // RGB Class
  275. // Напишите класс RGB, приватными свойствами которого являются три числа #r, #g, #b. Класс должен обладать
  276. // следующими геттерами и сеттерами:
  277. // r. Служит для чтения/изменения #r
  278. // g. Служит для чтения/изменения #g
  279. // b. Служит для чтения/изменения #b
  280. // rgb. Служит для чтения/изменения всех трех цветовых каналов. Используется строковой CSS синтаксис типа
  281. // rgb(128,255,64)
  282. // hex. Служит для чтения/изменения всех трех цветовых каналов. Используется строковой CSS синтаксис типа
  283. // #RRGGBB
  284. rgb_class: {
  285. class CheckCondition extends Error {
  286. constructor(msg) {
  287. super(msg);
  288. }
  289. }
  290. class RGB {
  291. #r;
  292. #g;
  293. #b;
  294. get r() {
  295. return this.#r;
  296. }
  297. set r(value) {
  298. if (typeof value == 'number' && value >= 0 && value <= 255) {
  299. this.#r = value;
  300. } else {
  301. throw new RangeError();
  302. }
  303. }
  304. get g() {
  305. return this.#g;
  306. }
  307. set g(value) {
  308. if (typeof value == 'number' && value >= 0 && value <= 255) {
  309. this.#g = value;
  310. } else {
  311. throw new RangeError();
  312. }
  313. }
  314. get b() {
  315. return this.#b;
  316. }
  317. set b(value) {
  318. if (typeof value == 'number' && value >= 0 && value <= 255) {
  319. this.#b = value;
  320. } else {
  321. throw new RangeError();
  322. }
  323. }
  324. get rgb() {
  325. return `rgb(${this.#r}, ${this.#g}, ${this.#b})`;
  326. }
  327. set rgb(value) {
  328. let regex = /^(rgb)?\(?([01]?\d\d?|2[0-4]\d|25[0-5])(\W+)([01]?\d\d?|2[0-4]\d|25[0-5])\W+(([01]?\d\d?|2[0-4]\d|25[0-5])\)?)$/;
  329. if (regex.test(value)) {
  330. let valueArr = value.match(regex);
  331. this.#r = parseInt(valueArr[2]);
  332. this.#g = parseInt(valueArr[4]);
  333. this.#b = parseInt(valueArr[6]);
  334. } else {
  335. throw new SyntaxError();
  336. }
  337. }
  338. get hex() {
  339. let color = ((this.#r >= 0 && this.#r < 16) ? ('0' + Math.floor(this.#r).toString(16)) : Math.floor(this.#r).toString(16)) +
  340. ((this.#g >= 0 && this.#g < 16) ? ('0' + Math.floor(this.#g).toString(16)) : Math.floor(this.#g).toString(16))
  341. + ((this.#b >= 0 && this.#b < 16) ? ('0' + Math.floor(this.#b).toString(16)) : Math.floor(this.#b).toString(16));
  342. return '#' + color;
  343. }
  344. set hex(value) {
  345. let regex = /^#([0-9A-Fa-f]{2})([0-9A-Fa-f]{2})([0-9A-Fa-f]{2})$/;
  346. if (regex.test(value)) {
  347. let valueArr = value.match(regex);
  348. this.#r = parseInt(valueArr[1], 16);
  349. this.#g = parseInt(valueArr[2], 16);
  350. this.#b = parseInt(valueArr[3], 16);
  351. } else {
  352. throw new SyntaxError();
  353. }
  354. }
  355. }
  356. const rgb = new RGB;
  357. rgb.r = 15;
  358. rgb.g = 128;
  359. rgb.b = 192;
  360. console.log(rgb.hex);
  361. console.log(rgb.rgb);
  362. console.log(rgb.r, rgb.g, rgb.b);
  363. rgb.hex = '#203040';
  364. console.log(rgb.rgb);
  365. console.log(rgb.hex);
  366. rgb.rgb = 'rgb(100, 90, 50)';
  367. console.log(rgb.r, rgb.g, rgb.b);
  368. }
  369. // RGBA Class
  370. // Создайте класс-наследник класса RGB под названием RGBA. В нем должно добавиться новое приватное поле #a,
  371. // содержащее значение прозрачности в диапазоне от 0 до 1. Создайте сеттер и геттер a. Перекройте сеттер и
  372. // геттер hex, что бы в классе-наследнике работал синтаксис #RRGGBBAA. Учтите, что сеттер и геттер предка
  373. // могут вам помочь. Также, сеттер hex должен поддерживать синтаксис #RRGGBB без прозрачности. Добавьте
  374. // сеттер и геттер rgba, которые работают с CSS-синтаксисом вида rgba(128,255,64, 0.5). Добавьте сеттер color,
  375. // в который можно присваивать любой из синтаксисов CSS - #RRGGBB, #RRGGBBAA, rgb(1,2,3) и rgba(1,2,3,0.5).
  376. // Сеттер a должен проверять диапазон и выбрасывать исключение в случае несоответствия диапазона.
  377. rgba_class: {
  378. class CheckCondition extends Error {
  379. constructor(msg) {
  380. super(msg);
  381. }
  382. }
  383. class RGB {
  384. #r;
  385. #g;
  386. #b;
  387. constructor() { }
  388. get r() {
  389. return this.#r;
  390. }
  391. set r(value) {
  392. if (typeof value == 'number' && value >= 0 && value <= 255) {
  393. this.#r = value;
  394. } else {
  395. throw new RangeError();
  396. }
  397. }
  398. get g() {
  399. return this.#g;
  400. }
  401. set g(value) {
  402. if (typeof value == 'number' && value >= 0 && value <= 255) {
  403. this.#g = value;
  404. } else {
  405. throw new RangeError();
  406. }
  407. }
  408. get b() {
  409. return this.#b;
  410. }
  411. set b(value) {
  412. if (typeof value == 'number' && value >= 0 && value <= 255) {
  413. this.#b = value;
  414. } else {
  415. throw new RangeError();
  416. }
  417. }
  418. get rgb() {
  419. return `rgb(${this.#r}, ${this.#g}, ${this.#b})`;
  420. }
  421. set rgb(value) {
  422. let regex = /^(rgb)?\(?([01]?\d\d?|2[0-4]\d|25[0-5])(\W+)([01]?\d\d?|2[0-4]\d|25[0-5])\W+(([01]?\d\d?|2[0-4]\d|25[0-5])\)?)$/;
  423. if (regex.test(value)) {
  424. let valueArr = value.match(regex);
  425. this.#r = parseInt(valueArr[2]);
  426. this.#g = parseInt(valueArr[4]);
  427. this.#b = parseInt(valueArr[6]);
  428. } else {
  429. throw new SyntaxError();
  430. }
  431. }
  432. get hex() {
  433. let color = ((this.#r >= 0 && this.#r < 16) ? ('0' + Math.floor(this.#r).toString(16)) : Math.floor(this.#r).toString(16)) +
  434. ((this.#g >= 0 && this.#g < 16) ? ('0' + Math.floor(this.#g).toString(16)) : Math.floor(this.#g).toString(16))
  435. + ((this.#b >= 0 && this.#b < 16) ? ('0' + Math.floor(this.#b).toString(16)) : Math.floor(this.#b).toString(16));
  436. return '#' + color;
  437. }
  438. set hex(value) {
  439. let regex = /^#([0-9A-Fa-f]{2})([0-9A-Fa-f]{2})([0-9A-Fa-f]{2})$/;
  440. if (regex.test(value)) {
  441. let valueArr = value.match(regex);
  442. this.#r = parseInt(valueArr[1], 16);
  443. this.#g = parseInt(valueArr[2], 16);
  444. this.#b = parseInt(valueArr[3], 16);
  445. } else {
  446. throw new SyntaxError();
  447. }
  448. }
  449. }
  450. class RGBA extends RGB {
  451. constructor(...params) {
  452. super(...params);
  453. }
  454. #a;
  455. get a() {
  456. return this.#a;
  457. }
  458. set a(value) {
  459. if (typeof value == 'number' && value >= 0 && value <= 1) {
  460. this.#a = value;
  461. }
  462. }
  463. get rgba() {
  464. return `rbda(${super.r}, ${super.g}, ${super.b}, ${this.#a})`
  465. }
  466. set rgba(value) {
  467. let regex = /^rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*(\d+(?:\.\d+)?))?\)$/;
  468. if (regex.test(value)) {
  469. let valueArr = value.match(regex);
  470. super.r = +valueArr[1];
  471. super.g = +valueArr[2];
  472. super.b = +valueArr[3];
  473. this.#a = valueArr[4];
  474. } else {
  475. throw new SyntaxError();
  476. }
  477. }
  478. get hex() {
  479. if (this.#a) {
  480. return super.hex + Math.ceil(this.#a * 255).toString(16);
  481. } else {
  482. return super.hex;
  483. }
  484. }
  485. set hex(value) {
  486. if (value.length == 9) {
  487. let opacity = value.slice(-2);
  488. this.#a = +(parseInt(opacity, 16) / 255).toFixed(2);
  489. super.hex = value.slice(0, 7);
  490. } else if (value.length == 7) {
  491. this.#a = null;
  492. super.hex = value;
  493. } else {
  494. throw new SyntaxError();
  495. }
  496. }
  497. set color(value) {
  498. if (value.slice(0, 1) == '#') {
  499. this.hex = value;
  500. } else if (value.slice(0, 4) == 'rgb(') {
  501. super.rgb = value;
  502. } else if (value.slice(0, 4) == 'rgba') {
  503. this.rgba = value;
  504. } else {
  505. throw new SyntaxError();
  506. }
  507. }
  508. }
  509. const rgba = new RGBA();
  510. rgba.hex = '#80808080';
  511. console.log(rgba.hex)
  512. console.log(rgba.a); //0.5
  513. rgba.rgba = 'rgba(128, 128, 128, 0.5)';
  514. console.log(rgba.rgba); //rgba(128,128,128,0.5)
  515. rgba.r = 192;
  516. rgba.a = 0.25;
  517. console.log(rgba.hex); //#C0808040
  518. console.log(rgba.a);
  519. rgba.color = 'rgba(1,2,3,0.70)';
  520. rgba.b *= 10;
  521. console.log(rgba.hex); //#01021EB3
  522. rgba.color = '#e1b9c9';
  523. console.log(rgba.hex);
  524. }