· 6 years ago · Mar 30, 2020, 12:06 AM
1"use strict";
2
3var _this8 = void 0;
4
5function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
6
7function _createForOfIteratorHelper(o) { if (typeof Symbol === "undefined" || o[Symbol.iterator] == null) { if (Array.isArray(o) || (o = _unsupportedIterableToArray(o))) { var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var it, normalCompletion = true, didErr = false, err; return { s: function s() { it = o[Symbol.iterator](); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it.return != null) it.return(); } finally { if (didErr) throw err; } } }; }
8
9function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(n); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
10
11function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }
12
13/*
14*
15* Методы класса Assignment
16*
17*/
18window.theOneAssignmentMethods = {};
19/*
20*
21* Методы класса Task
22*
23*/
24
25window.theOneTaskPrototype = {
26 //Тут проводятся все манипуляции со входной спекой и ШВшными JSONками.
27 getTemplateData: function getTemplateData() {
28 window.currentRenderingTask = this;
29
30 if (!this.theOneSolution) {
31 this.theOneSolution = {
32 __theOneCustomStorage: {
33 __actionLog: [],
34 __timingsStorage: {
35 total: 0
36 },
37 __uploadingFiles: {}
38 }
39 };
40 }
41
42 var templateData = TolokaHandlebarsTask.prototype.getTemplateData.call(this); //Объект, в котором будут храниться все обработчики событий для элементов из JSONок
43
44 this.elementEventHandlers = {}; //Установка флагов режима только для чтения и просмотра выполненных заданий
45
46 if (this.getWorkspaceOptions().isReadOnly) {
47 this.isReadOnly = true;
48 }
49
50 if (this.getWorkspaceOptions().isReviewMode) {
51 this.isReviewMode = true;
52 } //Установка мобильного флага для Handlebars
53
54
55 if (this.getWorkspaceOptions().agent !== 'FRONTEND') {
56 templateData.thisIsMobileDevice = true;
57 window.thisIsMobileDevice = true;
58 } //Если это режим просмотра выполненных заданий - нужно позаботиться о том, чтобы в шаблоне отображалась соответствующая солюшке картина
59
60
61 if (this.isReviewMode || this.isReadOnly) {
62 if (usingOneRawData && this.getSolution().output_values.__theOneRawData) {
63 this.startingSolution = JSON.parse(this.getSolution().output_values.__theOneRawData);
64
65 if (printSolution) {
66 console.log('Стартовая солюшка из __theOneRawData: ', this.startingSolution);
67 }
68 } else {
69 this.startingSolution = this.flattenSolution(this.getSolution().output_values);
70
71 if (printSolution) {
72 console.log('Стартовая солюшка: ', this.getSolution().output_values, ', преобразована в: ', this.startingSolution);
73 }
74 }
75 }
76
77 templateData = setTemplateData.apply(this, [templateData]);
78 return templateData;
79 },
80 //Распрямляет солюшку, превращая её в одноэтажный объект. Это чтобы когда солюшка со сложной структурой пришла на вход, ШВ мог из неё вытянуть состояние всех своих контролов.
81 //Вам сюда лезть не надо.
82 flattenSolution: function flattenSolution(solution) {
83 var flatSolution = {};
84
85 function addToFlat(o, name) {
86 if (Object.prototype.toString.call(o) === "[object Object]") {
87 for (var subO in o) {
88 addToFlat(o[subO], subO);
89 }
90 } else {
91 flatSolution[name] = o;
92 }
93 }
94
95 for (var o in solution) {
96 addToFlat(solution[o], o);
97 } //Если одно и то же поле маппится в несколько разных - предпочтение отдаем тому, которое маппится без функции.
98
99
100 var thereWasAFunction = [];
101
102 if (useSpecMapper) {
103 for (var prop in solution) {
104 if (specMapper[prop] && specMapper[prop].from) {
105 var fromNames = Array.isArray(specMapper[prop].from) ? specMapper[prop].from : [specMapper[prop].from];
106
107 var _iterator = _createForOfIteratorHelper(fromNames),
108 _step;
109
110 try {
111 for (_iterator.s(); !(_step = _iterator.n()).done;) {
112 var origName = _step.value;
113
114 if (!flatSolution[origName] || !Array.isArray(flatSolution[origName]) && thereWasAFunction[origName] && !specMapper[prop].f && !specMapper[prop].function) {
115 flatSolution[origName] = solution[prop];
116 thereWasAFunction[origName] = specMapper[prop].f || specMapper[prop].function;
117 } else if (Array.isArray(flatSolution[origName]) && Array.isArray(solution[prop])) {
118 var _iterator2 = _createForOfIteratorHelper(solution[prop]),
119 _step2;
120
121 try {
122 for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
123 var e = _step2.value;
124
125 if (!flatSolution[origName].includes(e)) {
126 flatSolution[origName].push(e);
127 }
128 }
129 } catch (err) {
130 _iterator2.e(err);
131 } finally {
132 _iterator2.f();
133 }
134 }
135 }
136 } catch (err) {
137 _iterator.e(err);
138 } finally {
139 _iterator.f();
140 }
141 }
142 }
143 }
144
145 return flatSolution;
146 },
147 //"Придумывает" уникальное в рамках таска имя для чего-нибудь
148 comeUpWithAnUniqueName: function comeUpWithAnUniqueName(justNumber) {
149 if (!this.uniqueNameCounter) {
150 this.uniqueNameCounter = 0;
151 }
152
153 return "".concat(justNumber ? '' : 'uniqueAutoName').concat(this.uniqueNameCounter++);
154 },
155 //Для случаев, когда для имён нужно получить слаги, а потом сконвертировать обратно в имена - эти две функции
156 getSlugForName: function getSlugForName(name) {
157 var dictionary = this.getStorageItem('__uniqueSlugsDictionary') || {};
158
159 for (var _slug in dictionary) {
160 if (dictionary[_slug] === name) {
161 return _slug;
162 }
163 }
164
165 var slug = this.comeUpWithAnUniqueName();
166 dictionary[slug] = name;
167 this.setStorageItem('__uniqueSlugsDictionary', dictionary);
168 return slug;
169 },
170 getNameForSlug: function getNameForSlug(slug) {
171 var dictionary = this.getStorageItem('__uniqueSlugsDictionary') || {};
172 return dictionary[slug] || false;
173 },
174 //Парсит входную JSONку и делает с ней необходимые операции прежде чем отдать её во входную спеку.
175 //Вам сюда тоже лезть не надо.
176 getJSON: function getJSON(JSON, namesPostfix) {
177 var task = this,
178 json =
179 /*stringified*/
180 JSON,
181 postfix = '__clone_' + namesPostfix;
182
183 var getElementByName = function getElementByName(name) {
184 var _iterator3 = _createForOfIteratorHelper(this),
185 _step3;
186
187 try {
188 for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
189 var e = _step3.value;
190
191 if (e.name && e.name === name) {
192 return e;
193 }
194 }
195 } catch (err) {
196 _iterator3.e(err);
197 } finally {
198 _iterator3.f();
199 }
200
201 return false;
202 };
203
204 var getElementIndexByName = function getElementIndexByName(name) {
205 for (var i in this) {
206 if (this[i].name && this[i].name === name) {
207 return i;
208 }
209 }
210
211 return false;
212 };
213
214 var setPropValue = function setPropValue(propName, value) {
215 this[propName] = value;
216 };
217
218 function initEventHandlers(element, fNames, namePrefix) {
219 var eHandlers = {};
220
221 var _iterator4 = _createForOfIteratorHelper(fNames),
222 _step4;
223
224 try {
225 for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
226 var hName = _step4.value;
227
228 if (element[hName] && typeof element[hName] == 'function') {
229 eHandlers[hName] = element[hName];
230 }
231 }
232 } catch (err) {
233 _iterator4.e(err);
234 } finally {
235 _iterator4.f();
236 }
237
238 if (Object.keys(eHandlers)) {
239 if (!element.name) {
240 element.name = task.comeUpWithAnUniqueName();
241 }
242
243 task.elementEventHandlers[namePrefix ? "".concat(namePrefix, "___").concat(element.name) : element.name] = eHandlers;
244 }
245 }
246
247 function _initJSONArray(json, isRecursive) {
248 Array.from(json).forEach(function (element) {
249 if (!element.name) {
250 element.name = task.comeUpWithAnUniqueName();
251 } //Первым делом добавляем к имени постфикс, если он есть (когда мы в setTemplateData множим какую-то джсонку)
252
253
254 if (namesPostfix || namesPostfix === 0) {
255 element.name += postfix;
256 } //Еслим мы в режиме просмотра размеченного задания и есть солюшка
257
258
259 if (!isRecursive && task.startingSolution) {
260 element = task.prepareJSONElement(element);
261 }
262
263 if (element.type && element.type === 'group') {
264 initJSONArray(element.groupFields, 'recursive'); //Функция получения порядкового номера элемента по имени
265
266 element.groupFields.getElementIndexByName = getElementIndexByName; //Функция получения элемента по имени
267
268 element.groupFields.getElementByName = getElementByName;
269 } else {
270 //В целях оптимизации запоминаем, в каких именно элементах JSONки спеки содержится элемент с данным именем
271 task.theOneSpec.specByName[element.name] = JSON; //Функция установки свойству определённого значения
272
273 element.setPropValue = setPropValue; //Если это элемент с массивом кнопок
274
275 if (element.buttons) {
276 //Функция получения кнопки по имени
277 element.buttons.getByName = getElementByName; //Функция получения порядкового номера кнопки по имени
278
279 element.buttons.getIndexByName = getElementIndexByName; //Функция добавления кнопки элементу в конец
280
281 element.buttons.add = function (buttJSON) {
282 buttJSON.setPropValue = setPropValue;
283 initEventHandlers(buttJSON, ['onClick', 'onActivation', 'onDeactivation'], element.name);
284 element.buttons.push(buttJSON);
285 }; //Функция добавления кнопки элементу в начало
286
287
288 element.buttons.addToStart = function (buttJSON) {
289 buttJSON.setPropValue = setPropValue;
290 element.buttons.unshift(buttJSON);
291 }; //Функция удаления кнопки у элемента по имени или индексу
292
293
294 element.buttons.remove = function (nameOrIndex) {
295 var index;
296
297 if (typeof nameOrIndex == "string") {
298 index = element.buttons.getButtonIndexByName(nameOrIndex);
299
300 if (!index) {
301 return;
302 }
303 } else {
304 index = nameOrIndex;
305 }
306
307 delete task.elementEventHandlers[this[index].name];
308 element.buttons.splice(index, 1);
309 }; //Проверяем кнопки на наличие обработчиков, и при необходимости обрабатываем их
310
311
312 var _iterator5 = _createForOfIteratorHelper(element.buttons),
313 _step5;
314
315 try {
316 for (_iterator5.s(); !(_step5 = _iterator5.n()).done;) {
317 var butt = _step5.value;
318
319 if (!butt.name) {
320 butt.name = task.comeUpWithAnUniqueName();
321 } //Добавляем указанный постфикс ссылкам на другие элементы, которым мы уже постфикс добавили
322
323
324 if (namesPostfix || namesPostfix === 0) {
325 if (butt.properties) {
326 for (var _i = 0, _arr = ['includes', 'excludes']; _i < _arr.length; _i++) {
327 var propName = _arr[_i];
328
329 if (butt.properties[propName]) {
330 for (var e in butt.properties[propName]) {
331 butt.properties[propName][e] += postfix;
332 }
333 }
334 }
335 }
336 } //Функция установки свойству определённого значения
337
338
339 butt.setPropValue = setPropValue;
340 initEventHandlers(butt, ['onClick', 'onActivation', 'onDeactivation'], element.name);
341 }
342 } catch (err) {
343 _iterator5.e(err);
344 } finally {
345 _iterator5.f();
346 }
347 } //Обработка инпутов и текстарей
348
349
350 if (['text', 'textarea', 'integer', 'float', 'link', 'date', 'email'].includes(element.type)) {
351 initEventHandlers(element, ['onInput', 'validation']);
352 }
353 }
354 });
355 }
356
357 var initJSONArray = _initJSONArray.bind(task);
358
359 initJSONArray(json); //Функция получения порядкового номера элемента по имени
360
361 json.getElementIndexByName = getElementIndexByName; //Функция получения элемента по имени
362
363 json.getElementByName = getElementByName;
364 return json;
365 },
366 //Рекурсивная функция для подготовки входной JSONки. Если на вход пришла солюшка - раскидывает значения из неё по описанным в JSONке элементам.
367 //Вам сюда уж точно лезть не нужно.
368 prepareJSONElement: function prepareJSONElement(e, solution) {
369 var task = this;
370
371 if (!solution) {
372 solution = task.startingSolution;
373 }
374
375 if (!e.type) {
376 return e;
377 }
378
379 function putSelectedToFlags(flags) {
380 if (!flags) {
381 flags = [];
382 }
383
384 if (!flags.includes('selected')) {
385 flags.push('selected');
386 }
387
388 return flags;
389 }
390
391 function removeSelectedFromFlags(flags) {
392 if (flags && flags.includes('selected')) {
393 flags.splice(flags.indexOf('selected'), 1);
394 }
395
396 return flags;
397 }
398
399 switch (e.type) {
400 case 'date': //Поле со вводом даты. Обычный инпут.
401
402 case 'text': //Поле со вводом текста. Обычный инпут.
403
404 case 'email': //Поле со вводом мыла. Обычный инпут.
405
406 case 'link': //Поле со вводом ссылки. Обычный инпут.
407
408 case 'integer': //Поле со вводом целого числа. Обычный инпут.
409
410 case 'float': //Поле со вводом дробного числа. Обычный инпут.
411
412 case 'textarea':
413 if (solution[e.name]) {
414 e.value = escapeHTML(solution[e.name]);
415 } else {
416 e.value = '';
417 }
418
419 break;
420
421 case 'switchButton':
422 if (solution[e.name]) {
423 e.flags = putSelectedToFlags(e.flags);
424 } else {
425 e.flags = removeSelectedFromFlags(e.flags);
426 }
427
428 break;
429
430 case 'radioGroup':
431 e.buttons.forEach(function (butt) {
432 if (butt.name === solution[e.name]) {
433 butt.flags = putSelectedToFlags(butt.flags);
434 } else {
435 butt.flags = removeSelectedFromFlags(butt.flags);
436 }
437 });
438 break;
439
440 case 'checkboxGroup':
441 e.buttons.forEach(function (butt) {
442 if (solution[e.name] && solution[e.name].includes(butt.name)) {
443 butt.flags = putSelectedToFlags(butt.flags);
444 } else {
445 butt.flags = removeSelectedFromFlags(butt.flags);
446 }
447 });
448 break;
449
450 case 'buttonGroup':
451 e.buttons.forEach(function (butt) {
452 if (solution[butt.name]) {
453 butt.flags = putSelectedToFlags(butt.flags);
454 } else {
455 butt.flags = removeSelectedFromFlags(butt.flags);
456 }
457 });
458 break;
459
460 case 'group':
461 if (e.name && solution[e.name] && e.flags && e.flags.includes('infinite')) {
462 //Обработка "бесконечных" групп
463 var newGroupFields = [];
464 Array.from(solution[e.name]).forEach(function (sol) {
465 e.groupFields.forEach(function (groupField) {
466 var newField = $.extend(true, {}, groupField);
467 newField = task.prepareJSONElement(newField, sol);
468 newGroupFields.push(newField);
469 });
470 });
471 e.groupFields = newGroupFields;
472 } else {
473 //Обработка обычных групп
474 e.groupFields.forEach(function (groupField) {
475 groupField = task.prepareJSONElement(groupField);
476 });
477 }
478
479 break;
480 }
481
482 return e;
483 },
484 //Классический Толоковский онРендер. Дополняйте как хотите после комментариев "Кастомные штуки" в зависимости от того, когда именно должен выполниться ваш код.
485 onRender: function onRender() {
486 var _this2 = this;
487
488 var root = this.getDOMElement(),
489 task = this;
490 this.errorMSGs = []; //Инициализируем модальные окна
491
492 this.initModalWindows(); //Биндим события на статические элементы
493
494 this.initFieldsHere(root); //Инициализация кастомных элементов заголовка
495
496 if (window.customTitleElements) {
497 var _iterator6 = _createForOfIteratorHelper(customTitleElements),
498 _step6;
499
500 try {
501 for (_iterator6.s(); !(_step6 = _iterator6.n()).done;) {
502 var e = _step6.value;
503
504 if (e.onRender) {
505 e.onRender.apply(this, [root.querySelector('.blockTitle.' + e.name)]);
506 }
507 }
508 } catch (err) {
509 _iterator6.e(err);
510 } finally {
511 _iterator6.f();
512 }
513 } //[Директ] - если чел кликал по ссылке в баннере последние 24 часа - убираем анкликед и прописываем визитед
514
515
516 if (!notForDirect && this.storage) checkDomain: {
517 var link = root.querySelector('.bannerTitle a');
518
519 if (!link) {
520 break checkDomain;
521 }
522
523 var domain = link.dataset.domain;
524
525 if (!domain) {
526 break checkDomain;
527 }
528
529 var result = this.storage.getItem('funfrogDirectDomainVisited_' + domain);
530
531 if (result) {
532 link.parentElement.classList.add('visited');
533 link.classList.remove('unclicked');
534 } else {
535 link.addEventListener('click', function () {
536 link.parentElement.classList.add('visited');
537 task.storage.setItem('funfrogDirectDomainVisited_' + domain, true); //Теперь надо перелопатить все ссылки на этот домен во всем сьюте и им тоже прописать visited и убрать анкликед
538
539 var _iterator7 = _createForOfIteratorHelper(root.parentElement.querySelectorAll('.bannerTitle a')),
540 _step7;
541
542 try {
543 for (_iterator7.s(); !(_step7 = _iterator7.n()).done;) {
544 var otherLink = _step7.value;
545
546 if (otherLink.dataset.domain !== domain) {
547 continue;
548 }
549
550 otherLink.parentElement.classList.add('visited');
551 otherLink.classList.remove('unclicked');
552 task.turnOffErrorClassOnParentElementTagBlock(otherLink);
553 }
554 } catch (err) {
555 _iterator7.e(err);
556 } finally {
557 _iterator7.f();
558 }
559 });
560 }
561 } //На все некликанные ссылки вешаем обработчик, чтобы они стали кликанными при кликании по ним
562
563 var _iterator8 = _createForOfIteratorHelper(root.querySelectorAll('a.unclicked')),
564 _step8;
565
566 try {
567 for (_iterator8.s(); !(_step8 = _iterator8.n()).done;) {
568 var _link = _step8.value;
569
570 _link.addEventListener('click', function () {
571 task.turnOffErrorClassOnParentElementTagBlock(this);
572 this.classList.remove('unclicked');
573 });
574 } //Контекстные менюшки
575
576 } catch (err) {
577 _iterator8.e(err);
578 } finally {
579 _iterator8.f();
580 }
581
582 if (contextMenu && customContextMenu) {
583 root.addEventListener('contextmenu', function (e) {
584 _this2.handleContextMenuOpen(e);
585 });
586 }
587
588 document.ready.then(function () {
589 //Если по чему-то надо кликать-кликаем
590 Array.from(root.querySelectorAll('.toBeClickedAtStart')).forEach(function (clickTarget) {
591 try {
592 task.getAssignment().getTaskSuite().focusElement(clickTarget.dataset.number);
593 } catch (e){
594 clickTarget.click();
595 }
596
597 clickTarget.classList.remove('toBeClickedAtStart');
598 }); //Если на старте есть солюшка - применяем её
599
600 if (!task.startingSolution && Object.keys(_this2.theOneSolution).length > 1) {
601 _this2.loadSolution(_this2.theOneSolution);
602 } //Если у нас режим просмотра выполенных заданий - удаляем все пустые бесконечные группы
603
604
605 if (_this2.isReadOnly) {
606 var emptyGroups = root.querySelectorAll('.groupContainer.infinite>.group:not(:first-child):not(.hasSomething)');
607
608 if (emptyGroups) {
609 for (var i = 0; i < emptyGroups.length; i++) {
610 emptyGroups[i].parentElement.removeChild(emptyGroups[i]);
611 }
612 }
613 }
614
615 task.rendered = true; //Кастомные штуки
616
617 if (window.onTaskReady) {
618 onTaskReady.apply(task, [root]);
619 }
620 }).catch(function (err) {
621 return console.error(err);
622 }); //Кастомные штуки
623
624 if (window.onTaskRender) {
625 onTaskRender.apply(this, [root]);
626 }
627 },
628 //Закрывает ШВшные контекстные меню
629 closeAllContextMenus: function closeAllContextMenus() {
630 var root = this.getDOMElement();
631
632 var _iterator9 = _createForOfIteratorHelper(root.querySelectorAll('.customContextMenu')),
633 _step9;
634
635 try {
636 for (_iterator9.s(); !(_step9 = _iterator9.n()).done;) {
637 var oldMenu = _step9.value;
638 oldMenu.parentElement.removeChild(oldMenu);
639 }
640 } catch (err) {
641 _iterator9.e(err);
642 } finally {
643 _iterator9.f();
644 }
645
646 this.customContextMenuOpened = false;
647 },
648 //Открывает контекстное меню
649 handleContextMenuOpen: function handleContextMenuOpen(event) {
650 var _this3 = this;
651
652 var menuScreenMargin = 20,
653 root = this.getDOMElement(); //Make sure ни одно контекстное меню не открыто
654
655 this.closeAllContextMenus();
656
657 if (event.target.closest('.customContextMenuItemsContainer')) {
658 return;
659 }
660
661 var menu = document.createElement('div');
662 menu.className = 'customContextMenu';
663 menu.innerHTML = '<div class="customContextMenuItemsContainer alien"><div class="customContextMenuRightClickField">Правый клик сюда<br>откроет меню браузера</div></div>';
664 var itemsContainer = menu.querySelector('.customContextMenuItemsContainer');
665 var itemsArray = [];
666
667 var addItem = function addItem(item) {
668 if (item.isVisible && !item.isVisible.apply(_this3, [])) {
669 return;
670 }
671
672 itemsArray.push(item);
673 };
674
675 var _iterator10 = _createForOfIteratorHelper(customContextMenu.items),
676 _step10;
677
678 try {
679 for (_iterator10.s(); !(_step10 = _iterator10.n()).done;) {
680 var itemName = _step10.value;
681
682 if (standardContextMenuItems[itemName]) {
683 addItem(standardContextMenuItems[itemName]);
684 }
685 }
686 } catch (err) {
687 _iterator10.e(err);
688 } finally {
689 _iterator10.f();
690 }
691
692 var _iterator11 = _createForOfIteratorHelper(customContextMenu.customItems),
693 _step11;
694
695 try {
696 for (_iterator11.s(); !(_step11 = _iterator11.n()).done;) {
697 var item = _step11.value;
698 addItem(item);
699 }
700 } catch (err) {
701 _iterator11.e(err);
702 } finally {
703 _iterator11.f();
704 }
705
706 if (itemsArray.length < 1) {
707 return;
708 }
709
710 event.preventDefault();
711 this.customContextMenuOpened = true;
712
713 var handleRootClick = function handleRootClick(e) {
714 if (!e.target.closest('.customContextMenu')) {
715 _this3.closeAllContextMenus();
716
717 root.removeEventListener('click', handleRootClick);
718 }
719 };
720
721 root.addEventListener('click', handleRootClick);
722 itemsContainer.innerHTML += getFieldsHTML(this.getJSON([{
723 type: "actionButtonGroup",
724 name: "customContextMenuActionButtons",
725 flags: ["block", "unfocusable"],
726 buttons: itemsArray
727 }]));
728 root.appendChild(menu);
729 this.initFieldsHere(itemsContainer); //console.log(event);
730
731 var task = fullScreenMode ? event.target.closest('.block.main') || event.target.closest('.windowContent') || event.target.closest('.task') : event.target.closest('.windowContent') || event.target.closest('.task');
732
733 if (!task) {
734 menu.style.left = event.clientX + 'px';
735 menu.style.top = event.clientY + 'px';
736 } else {
737 var menuRect = menu.getBoundingClientRect(),
738 taskRect = task.getBoundingClientRect();
739
740 if (menuRect.width + event.clientX + menuScreenMargin > taskRect.width) {
741 menu.style.left = event.clientX - menuRect.width + 'px';
742 } else {
743 menu.style.left = event.clientX + 'px';
744 }
745
746 if (menuRect.height + event.clientY + menuScreenMargin > taskRect.height) {
747 menu.style.top = event.clientY - menuRect.height + 'px';
748 } else {
749 menu.style.top = event.clientY + 'px';
750 }
751 }
752 },
753 //Инициализация всех используемых в шаблоне модальных окон
754 initModalWindows: function initModalWindows() {
755 var _this4 = this;
756
757 var root = this.getDOMElement(),
758 task = this,
759 _window = window;
760 this.windows = {};
761 this.activeWindowsChain = []; //Все открытые в данный момент окна, в порядке открытия
762
763 if (!window.modalWindows) {
764 window.modalWindows = {};
765 } //Добавляем ШВшные окна
766
767
768 modalWindows._fileWithSameNameAlreadyUploadedMessageWindow = {
769 title: 'Ой!',
770 content: "\u0424\u0430\u0439\u043B \u0441 \u0442\u0430\u043A\u0438\u043C \u0438\u043C\u0435\u043D\u0435\u043C \u0443\u0436\u0435 \u0431\u044B\u043B \u0437\u0430\u0433\u0440\u0443\u0436\u0435\u043D!",
771 buttons: [{
772 caption: 'Понятно.',
773 f: function f(window) {
774 window.close();
775 }
776 }]
777 };
778
779 var _loop = function _loop(windowName) {
780 var windowObj = modalWindows[windowName];
781 var window = root.querySelector('.window.' + windowName);
782
783 if (!window) {
784 //Окно не описали в Handlebars - значит сами его создадим
785 var div = document.createElement('div');
786 div.innerHTML = _this4.compileHandlebars("{{#window name=\"".concat(windowName, "\"}}{{/window}}"));
787 window = div.querySelector('.window.' + windowName);
788 root.appendChild(window);
789 }
790
791 if (windowObj.flags && windowObj.flags.includes('temporary')) {
792 window.isTemporary = true;
793 }
794
795 window.className += flagsToClassName(windowObj.flags);
796
797 window.open = function () {
798 window.classList.add('opened');
799 _window.activeWindow = window;
800 task.activeWindowsChain.push(window);
801 window.content.scrollTop = 0;
802
803 if (window.isTemporary) {
804 window.setContent(windowObj.content);
805 task.initFieldsHere(window.content);
806 } //Обновлянем все просмотрщики с канвасами
807
808
809 var _iterator12 = _createForOfIteratorHelper(window.querySelectorAll('.imageField .imageBlock canvas')),
810 _step12;
811
812 try {
813 for (_iterator12.s(); !(_step12 = _iterator12.n()).done;) {
814 var c = _step12.value;
815
816 if (c.initCanvas) {
817 c.initCanvas();
818 }
819 }
820 } catch (err) {
821 _iterator12.e(err);
822 } finally {
823 _iterator12.f();
824 }
825
826 clearTimeout(_window.focusTimeout);
827 _window.focusTimeout = setTimeout(function () {
828 task.getAssignment().getTaskSuite().focusNextElement(!!(windowObj.flags && windowObj.flags.includes('noScrollAfterOpening')));
829 }, 100);
830 if (window.onOpen) window.onOpen();
831 return new Promise(function (resolve, reject) {
832 window.resolve = function (obj) {
833 window.isResolved = true;
834 resolve(obj);
835 };
836
837 window.reject = function (obj) {
838 reject(obj);
839 };
840 });
841 };
842
843 window.close = function () {
844 window.classList.remove('opened');
845 task.activeWindowsChain.splice(task.activeWindowsChain.indexOf(window), 1);
846 _window.activeWindow = task.activeWindowsChain[task.activeWindowsChain.length - 1];
847 clearTimeout(_window.focusTimeout);
848 _window.focusTimeout = setTimeout(function () {
849 task.getAssignment().getTaskSuite().focusNextElement(!!(windowObj.flags && windowObj.flags.includes('noScrollAfterClosing')));
850 }, 100);
851 if (window.onClose) window.onClose();
852
853 if (window.isTemporary) {
854 window.setContent('');
855 }
856
857 if (!window.isResolved && window.reject) {
858 window.reject();
859 }
860
861 delete window.isResolved;
862 };
863
864 if (windowObj.onOpen) window.onOpen = function () {
865 windowObj.onOpen.apply(task, [window]);
866 };
867 if (windowObj.onRender) window.onRender = function () {
868 windowObj.onRender.apply(task, [window]);
869 };
870 if (windowObj.onClose) window.onClose = function () {
871 windowObj.onClose.apply(task, [window]);
872 };
873 var buttonsHTML = '';
874
875 if (windowObj.buttons) {
876 var buttonsJSON = {
877 type: "actionButtonGroup",
878 class: "noIcon windowButtons_" + windowName,
879 buttons: []
880 };
881 var i = 0;
882
883 var _iterator13 = _createForOfIteratorHelper(windowObj.buttons),
884 _step13;
885
886 try {
887 for (_iterator13.s(); !(_step13 = _iterator13.n()).done;) {
888 var butt = _step13.value;
889 butt.number = i++;
890 buttonsJSON.buttons.push({
891 iconClass: 'winButton' + butt.number,
892 caption: butt.caption,
893 name: butt.name
894 });
895 }
896 } catch (err) {
897 _iterator13.e(err);
898 } finally {
899 _iterator13.f();
900 }
901
902 buttonsHTML = handleFieldAndGetHTML.apply(_this4, [buttonsJSON]);
903 }
904
905 window.innerHTML = "<div class=\"windowBox block\"><div class='blockTitle'></div><div class=\"windowContent answerBlock\">".concat(window.innerHTML, "</div><div class=\"windowFooter\"><div class=\"windowButtons\">").concat(buttonsHTML, "</div></div></div>");
906
907 if (windowObj.buttons) {
908 var _iterator14 = _createForOfIteratorHelper(windowObj.buttons),
909 _step14;
910
911 try {
912 var _loop2 = function _loop2() {
913 var butt = _step14.value;
914
915 if (butt.f) {
916 window.querySelector('.windowButtons .winButton' + butt.number).addEventListener('click', function (e) {
917 butt.f.apply(task, [window]);
918 });
919 }
920 };
921
922 for (_iterator14.s(); !(_step14 = _iterator14.n()).done;) {
923 _loop2();
924 }
925 } catch (err) {
926 _iterator14.e(err);
927 } finally {
928 _iterator14.f();
929 }
930 } //Установка заголовка
931
932
933 window.titleBlock = window.querySelector('.blockTitle');
934
935 window.setTitle = function (title) {
936 window.titleBlock.innerHTML = title;
937 window.classList.add('hasTitle');
938 };
939
940 window.clearTitle = function () {
941 window.titleBlock.innerHTML = '';
942 window.classList.remove('hasTitle');
943 };
944
945 if (windowObj.title) {
946 window.setTitle(windowObj.title);
947 } //Установка содержимого
948
949
950 window.content = window.querySelector('.windowContent');
951
952 window.setContent = function (content) {
953 window.content.innerHTML = task.compileHandlebars(content);
954 };
955
956 window.clearContent = function () {
957 window.content.innerHTML = '';
958 };
959
960 if (windowObj.content && !window.isTemporary) {
961 window.setContent(windowObj.content);
962 }
963
964 _this4.windows[windowName] = window;
965 if (window.onRender) window.onRender();
966 };
967
968 for (var windowName in modalWindows) {
969 _loop(windowName);
970 }
971 },
972 //Обработчик, выполняющийся при любой смене ШВшной солюшки.
973 onSolutionChange: function onSolutionChange(solution) {
974 if (!this.rendered) {
975 return;
976 }
977
978 if (!solution) {
979 var eAndS = this.gatherErrorsAndSolution('Без вывода ошибок');
980 solution = eAndS.solution; //Проверки
981
982 if (window.solutionChecks) {
983 solutionChecks.apply(this, [solution]);
984 }
985 } //Если в солюшке есть кастомный объект - сохраняем его, чтобы не затёрся
986
987
988 if (this.theOneSolution && this.theOneSolution.__theOneCustomStorage) {
989 solution.__theOneCustomStorage = this.theOneSolution.__theOneCustomStorage;
990 }
991
992 this.theOneSolution = solution;
993 this.theOneSolutionUpdated = true;
994 },
995 //Очищает значения(солюшку) на всех контролах в указанном месте
996 clearSolutionHere: function clearSolutionHere(where) {
997 var root = this.getDOMElement(),
998 task = this;
999 Array.from(where.querySelectorAll('.btn.active')).forEach(function (btn) {
1000 if (btn.closest('.alien')) {
1001 return;
1002 }
1003
1004 task.buttonSmashed(btn, true);
1005 task.checksAfterValidation(btn);
1006 });
1007 Array.from(where.querySelectorAll('input, textarea')).slice().reverse().forEach(function (input) {
1008 if (input.closest('.alien')) {
1009 return;
1010 }
1011
1012 input.value = '';
1013 input.classList.remove('validationRejected', 'validationPassed');
1014 task.checksAfterValidation(input);
1015 });
1016 this.onSolutionChange();
1017 },
1018 //Загружает в таск переданную солюшку
1019 loadSolution: function loadSolution(solution, where) {
1020 var task = this,
1021 root = where ? where : this.getDOMElement();
1022
1023 if (!solution) {
1024 solution = this.theOneSolution;
1025 } //Обнуляем все контролы
1026
1027
1028 this.clearSolutionHere(root);
1029
1030 function recursiveApplying(solution, where) {
1031 var _loop3 = function _loop3(eName) {
1032 var e = void 0,
1033 ea = void 0;
1034
1035 if (where.classList.contains('groupContainer') && where.classList.contains('infinite')) {
1036 e = where.querySelector(':scope>.group:last-child .fieldName_' + eName + '');
1037 } else {
1038 ea = where.querySelectorAll('.fieldName_' + eName);
1039 e = ea[ea.length - 1];
1040 }
1041
1042 if (!e) {
1043 return "continue";
1044 } //Разбираемся, что это за элемент и в зависимости от этого применяем значение
1045
1046
1047 if (e.classList.contains('fieldType_textarea')) {
1048 var textarea = e.querySelector('textarea');
1049
1050 if (!textarea) {
1051 return "continue";
1052 }
1053
1054 textarea.value = solution[eName];
1055 task.validateInput(textarea);
1056 } else if (e.classList.contains('radioGroup') || e.classList.contains('select')) {
1057 var btn = e.querySelector('.fieldName_' + solution[eName] + ' .btn');
1058
1059 if (!btn) {
1060 return "continue";
1061 }
1062
1063 if (!isActive(btn)) task.buttonSmashed(btn);
1064 } else if (e.classList.contains('checkboxGroup')) {
1065 Array.from(solution[eName]).forEach(function (buttName) {
1066 var btn = e.querySelector('.fieldName_' + buttName + ' .btn');
1067
1068 if (!btn) {
1069 return;
1070 }
1071
1072 if (!isActive(btn)) task.buttonSmashed(btn);
1073 });
1074 } else if (e.classList.contains('buttDiv') && solution[eName]) {
1075 var _btn = e.querySelector('.btn');
1076
1077 if (!_btn) {
1078 return "continue";
1079 }
1080
1081 if (!isActive(_btn)) task.buttonSmashed(_btn);
1082 } else if (e.classList.contains('fieldType_date') || e.classList.contains('fieldType_text') || e.classList.contains('fieldType_email') || e.classList.contains('fieldType_link') || e.classList.contains('fieldType_integer') || e.classList.contains('fieldType_float')) {
1083 var input = e.querySelector('input');
1084
1085 if (!input) {
1086 return "continue";
1087 }
1088
1089 input.value = solution[eName];
1090 task.validateInput(input);
1091 } else if (e.classList.contains('fileField') && e.classList.contains('virtual') && solution[eName]) {
1092 var fuFiles = task.getDOMElement().querySelector('.__fileUploader .field_file__files'),
1093 fileDeleteButtons = fuFiles.querySelectorAll('.field_file__files__file .file__delete');
1094 e.clear();
1095
1096 if (fileDeleteButtons && fileDeleteButtons.length > 0) {
1097 task._fileUploader.uploadedFilesDivs = fuFiles.querySelectorAll('.field_file__files__file'); //Отрисовываем в контроле загруженные в нём файлы, которые есть в солюшке
1098
1099 var _iterator15 = _createForOfIteratorHelper(fileDeleteButtons),
1100 _step15;
1101
1102 try {
1103 for (_iterator15.s(); !(_step15 = _iterator15.n()).done;) {
1104 var fileDeleteButt = _step15.value;
1105 var attachmentId = fileDeleteButt.dataset.attachmentId;
1106 var item = task.getStorageItem('__uploadingFiles')[attachmentId];
1107
1108 if (item) {
1109 item.fileName = fileDeleteButt.parentElement.querySelector('.file__name').innerHTML;
1110
1111 if (item.fieldName === e.dataset.name && solution[e.dataset.name] && Array.isArray(solution[e.dataset.name]) && (solution[e.dataset.name].includes(item.fileName) || solution[e.dataset.name].includes(attachmentId))) {
1112 item.fileField = e;
1113 item.fileLine = e.addFileLine(item.fileName, attachmentId);
1114 }
1115 }
1116 }
1117 } catch (err) {
1118 _iterator15.e(err);
1119 } finally {
1120 _iterator15.f();
1121 }
1122 }
1123
1124 e.validate({}, true);
1125 } else if (e.classList.contains('groupContainer')) {
1126 Array.from(solution[eName]).forEach(function (s) {
1127 recursiveApplying(s, e);
1128 task.checksAfterValidation(e);
1129 });
1130 }
1131 };
1132
1133 for (var eName in solution) {
1134 var _ret = _loop3(eName);
1135
1136 if (_ret === "continue") continue;
1137 }
1138 } //Применяем солюшку
1139
1140
1141 recursiveApplying(solution, root);
1142 this.onSolutionChange(solution);
1143 },
1144 //Инициализирует неинициализированные поля. Вешает на отрисованные ШВшные элементы обработчики событий. Вам сюда лезть не надо, на свои элементы вешайте всё, что хотите, в onRender.
1145 initFieldsHere: function initFieldsHere(whereToInit) {
1146 var task = this,
1147 root = this.getDOMElement();
1148
1149 if (!whereToInit) {
1150 return;
1151 } //Пробегаемся по всем инпутам и вешаем на них валидэйшн
1152
1153
1154 Array.from(whereToInit.querySelectorAll('input:not(.initialized), textarea:not(.initialized)')).forEach(function (input) {
1155 if (input.parentElement.classList.contains('searchField') || input.classList.contains('comment')) {
1156 return;
1157 }
1158
1159 input.addEventListener('input', function () {
1160 if (this.closest('.alien')) {
1161 return;
1162 } //Если у нас ридонли и инпут попытались поменять - возвращаем как было
1163
1164
1165 if (task.rendered && (task.isReadOnly || this.interfaceBlocked) && !this.closest('.notReadOnly')) {
1166 if (this.dataset.name && task.theOneSolution[this.dataset.name]) {
1167 if (this.value !== task.theOneSolution[this.dataset.name]) {
1168 this.value = task.theOneSolution[this.dataset.name];
1169 }
1170 } else {
1171 if (this.value !== '') {
1172 this.value = '';
1173 }
1174 }
1175 }
1176
1177 if (task.validateInput(this)) {
1178 task.lastChangedElement = this;
1179 }
1180
1181 task.checksAfterValidation(this);
1182 task.onSolutionChange(); //Проверки для обработчиков событий
1183
1184 var eHandlers = task.elementEventHandlers[input.dataset.name];
1185
1186 if (eHandlers) {
1187 if (eHandlers.onInput) {
1188 eHandlers.onInput.apply(task, [this]);
1189 }
1190 }
1191 });
1192
1193 if (window.actionLogger) {
1194 input.addEventListener('change', function () {
1195 //Логируем нажатие
1196 var charLimit = window.actionLoggerInputAndTextareaCharLimit || 201;
1197 task.logAction('inputChange', {
1198 buttonName: this.dataset.name,
1199 value: this.value.length < charLimit ? this.value : "[>".concat(charLimit, " \u0441\u0438\u043C\u0432\u043E\u043B\u043E\u0432]")
1200 });
1201 });
1202 }
1203
1204 task.validateInput(input);
1205 input.classList.add('initialized');
1206 }); //Теперь кнопки
1207
1208 Array.from(whereToInit.querySelectorAll('.btn:not(.initialized)')).forEach(function (btn) {
1209 btn.addEventListener('click', function (event) {
1210 if (btn.dataset.select && isActive(btn) && btn.closest('.cantUnselect')) {
1211 return;
1212 }
1213
1214 if (btn.closest('.readOnly') && task.rendered === true) {
1215 return;
1216 }
1217
1218 task.buttonSmashed(this);
1219
1220 if (!this.closest('.actionButtonGroup') && !this.closest('.linkButtonGroup')) {
1221 task.checksAfterValidation(this);
1222 task.onSolutionChange();
1223 } //Проверки для обработчиков событий
1224
1225
1226 var eventHandlersName = btn.closest('.element') && btn.closest('.element').dataset.name ? "".concat(btn.closest('.element').dataset.name, "___").concat(btn.dataset.name) : btn.dataset.name;
1227 var buttHandlers = task.elementEventHandlers[eventHandlersName];
1228
1229 if (buttHandlers) {
1230 if (buttHandlers.onClick) {
1231 buttHandlers.onClick.apply(task, [btn]);
1232 }
1233 } //Логируем нажатие
1234
1235
1236 var additional = {};
1237 var action = 'buttonClick';
1238
1239 if (this.dataset.name && this.dataset.name !== "undefined") {
1240 additional.buttonName = this.dataset.name;
1241 }
1242
1243 if (!this.closest('.actionButtonGroup') && !this.closest('.linkButtonGroup')) {
1244 additional.isActive = isActive(this);
1245 }
1246
1247 var e = this.closest('.element');
1248
1249 if (e && e.dataset.name) {
1250 if (!e.classList.contains('spoiler')) {
1251 additional.element = e.dataset.name;
1252 } else {
1253 additional.spoiler = e.dataset.name;
1254
1255 if (isActive(this)) {
1256 action = 'spoilerOpen';
1257 } else {
1258 action = 'spoilerClose';
1259 }
1260 }
1261 }
1262
1263 if (this.closest('.groupContainer') && this.closest('.groupContainer').dataset.name) {
1264 additional.group = this.closest('.groupContainer').dataset.name;
1265 }
1266
1267 task.logAction(action, additional);
1268 });
1269 btn.addEventListener('mousedown', function () {
1270 btn.classList.add('underPressure');
1271 });
1272 btn.addEventListener('mousedown', function () {
1273 btn.classList.add('touchstart');
1274 });
1275 btn.addEventListener('mouseup', function () {
1276 btn.classList.remove('underPressure');
1277 });
1278 btn.addEventListener('mouseleave', function () {
1279 btn.classList.remove('underPressure');
1280 });
1281 btn.addEventListener('touchend', function () {
1282 btn.classList.remove('underPressure');
1283 });
1284 btn.classList.add('initialized');
1285 }); //Селекты
1286
1287 Array.from(whereToInit.querySelectorAll('.select.element:not(.initialized)')).forEach(function (select) {
1288 select.querySelector('.selectButtons .arrow').addEventListener('click', function (e) {
1289 select.classList.toggle('opened');
1290 });
1291 select.querySelector('.selectDisplay').addEventListener('click', function (e) {
1292 select.classList.toggle('opened');
1293 });
1294
1295 select.onKey = function (code, e) {
1296 if (code === "Space" && (!document.activeElement || document.activeElement && !document.activeElement.closest('.noHotkeys'))) {
1297 e.preventDefault();
1298 select.querySelector('.selectButtons .arrow').click();
1299 }
1300 };
1301
1302 select.classList.add('initialized');
1303 }); //Спойлеры
1304
1305 Array.from(whereToInit.querySelectorAll('.spoiler.element:not(.initialized)')).forEach(function (spoiler) {
1306 spoiler.querySelector('.spoilerHead .btn').addEventListener('click', function (e) {
1307 spoiler.classList.toggle('opened');
1308
1309 if (spoiler.classList.contains('opened')) {//task.getAssignment().getTaskSuite().maybeScrollToThisElement(spoiler, true);
1310 }
1311 });
1312
1313 spoiler.onKey = function (code, e) {
1314 if (code === "Space" && (!document.activeElement || document.activeElement && !document.activeElement.closest('.noHotkeys'))) {
1315 e.preventDefault();
1316 spoiler.querySelector('.spoilerHead .btn').click();
1317 }
1318 };
1319
1320 spoiler.classList.add('initialized');
1321 }); //Поля загрузки файлов
1322
1323 var initFilesFields = function initFilesFields() {
1324 Array.from(whereToInit.querySelectorAll('.fileField.element:not(.initialized)')).forEach(function (fileField) {
1325 task.initFileField(fileField);
1326 });
1327 };
1328
1329 if (this.rendered) {
1330 initFilesFields();
1331 } else {
1332 document.ready.then(function () {
1333 initFilesFields();
1334 }).catch(function (err) {
1335 return console.error(err);
1336 });
1337 } //Инициализируем все ШВшные просмотрщики изображений
1338
1339
1340 Array.from(whereToInit.querySelectorAll('.element.imageField:not(.initialized)')).forEach(function (field) {
1341 initImageField(field);
1342 field.classList.add('initialized');
1343 }); //Вставляем СВГ-иконки
1344
1345 this.insertIcons(); //Биндим действия на все helpContainer'ы
1346
1347 var _iterator16 = _createForOfIteratorHelper(root.querySelectorAll('.helpContainer:not(.initialized)')),
1348 _step16;
1349
1350 try {
1351 for (_iterator16.s(); !(_step16 = _iterator16.n()).done;) {
1352 var q = _step16.value;
1353 initHelpContainer(q);
1354 } //Перестраиваем все горячие клавиши
1355
1356 } catch (err) {
1357 _iterator16.e(err);
1358 } finally {
1359 _iterator16.f();
1360 }
1361
1362 if (this.rendered) {
1363 this.getAssignment().getTaskSuite().initTheOneHotkeys();
1364 } //Пробегаемся по всем groupContainer'ам и рисуем в них первый набор полей (может и последний, в зависимости от параметра infinite)
1365
1366
1367 Array.from(whereToInit.querySelectorAll('.groupContainer:not(.alien)')).forEach(function (gc) {
1368 if (gc.closest('.alien') || gc.parentElement.classList.contains('group') && gc.parentElement !== whereToInit) {
1369 return;
1370 }
1371
1372 task.addGroup(gc);
1373 });
1374 },
1375 //После валидации иногда нужно проводить разные проверки, все они находятся тут.
1376 //Если вы будете ковырять и модифицировать стандартные "бесконечные" группы полей - возможно, надо будет тут чёнить дописать.
1377 checksAfterValidation: function checksAfterValidation(validatedElement) {
1378 //Проверки для элементов внутри групп
1379 var gc = validatedElement.closest('.groupContainer'),
1380 props = null;
1381
1382 if (gc) {
1383 //Валидируем группы в ГруппКонтейнере
1384 Array.from(gc.querySelectorAll(':scope>.group')).forEach(function (group) {
1385 //.hasSomething
1386 if (group.querySelector('.validationPassed') || group.querySelector('.validationRejected') || group.querySelector('.active')) {
1387 group.classList.add('hasSomething');
1388 } else {
1389 group.classList.remove('hasSomething');
1390 } //.correct
1391
1392
1393 if (!group.querySelector('.validationRejected') && !group.querySelector(':scope>.element .clear:not(.skipMe)')) {
1394 group.classList.add('correct');
1395 } else {
1396 group.classList.remove('correct');
1397 }
1398 }); //Если валидированный элемент принадлежит к группе с бесконечными элементами - надо, мб, сгенерировать новых или, наоборот, удалить лишние
1399
1400 if (isGroupInfinite(gc)) {
1401 //Добавление новых, если все группы в контейнере корректны
1402 if (!gc.querySelector(':scope>.group:not(.correct)') && (!gc.dataset.limit || gc.dataset.limit && gc.querySelectorAll(':scope>.group').length < parseInt(gc.dataset.limit))) {
1403 this.addGroup(gc);
1404 } //Удаление пустых
1405
1406
1407 var emptyGroups = gc.querySelectorAll(':scope>.group:not(.hasSomething)');
1408 var startFrom = 1;
1409
1410 if (gc.querySelectorAll(':scope>.group').length > 1 && gc.querySelector(':scope>.group.hasSomething:not(.correct)')) {
1411 startFrom = 0;
1412 }
1413
1414 if (emptyGroups && emptyGroups.length > startFrom) {
1415 for (var i = startFrom; i < emptyGroups.length; i++) {
1416 gc.removeChild(emptyGroups[i]);
1417 }
1418 }
1419 }
1420 }
1421 },
1422 //Выполняется для каждой активированной ШВшной кнопки, будь то свитчБаттон, радиобаттон или чекбокс - без разницы.
1423 //Если вам нужно что-то делать после нажатия на кнопки (пусть даже некоторые) - внизу функции есть отличное место для кастомного кода. Хотя пишите куда хотите))))
1424 buttonSmashed: function buttonSmashed(btn, isTemporary) {
1425 if (this.rendered && (this.interfaceBlocked || this.isReadOnly && !btn.closest('.notReadOnly') || btn.closest('.disabled'))) {
1426 return;
1427 }
1428
1429 var root = this.getDOMElement(),
1430 task = this;
1431
1432 if (!btn.dataset.type || window.buttonsDisabled) {
1433 return;
1434 }
1435
1436 this.hideTaskError();
1437 this.turnOffErrorClassOnParentElementTagBlock(btn);
1438 var props = null;
1439
1440 if (btn.dataset.props) {
1441 props = fieldsPropsArray[btn.dataset.props];
1442 } //Производит всякие проверки в зависимости от свойств этот кнопки
1443
1444
1445 function propsChecks() {
1446 //Создадим массив выключенных в данный момент групп, чтобы не включить ничего лишнего
1447 var excludedArr = [];
1448
1449 if (props.includes || props.excludes) {
1450 Array.from(root.querySelectorAll('.btn.active.excludesSomething')).forEach(function (btn) {
1451 var props;
1452
1453 if (btn.dataset.props) {
1454 props = fieldsPropsArray[btn.dataset.props];
1455 } else {
1456 return;
1457 }
1458
1459 if (props.excludes) {
1460 props.excludes.forEach(function (groupName) {
1461 excludedArr.push(groupName);
1462 });
1463 }
1464 });
1465 } //Если эта кнопка должна при включении что-то исключать или включать: делаем это
1466
1467
1468 if (props.excludes) {
1469 Array.from(props.excludes).forEach(function (excludedClass) {
1470 var excludedDivs = root.querySelectorAll(':not(.includes).' + excludedClass);
1471 Array.from(excludedDivs).forEach(function (excludedDiv) {
1472 if (isActive(btn)) {
1473 excludedDiv.classList.add('excluded'); //И снимаем там все галки
1474
1475 Array.from(excludedDiv.querySelectorAll('.btn.active')).forEach(function (btn) {
1476 btn.click();
1477 });
1478 } else {
1479 if (!excludedArr.includes(excludedClass) && !(excludedDiv.dataset.excluded && excludedDiv.dataset.excluded === 'excluded')) {
1480 excludedDiv.classList.remove('excluded'); //Обновлянем все просмотрщики с канвасами
1481
1482 var _iterator17 = _createForOfIteratorHelper(excludedDiv.querySelectorAll('.imageField .imageBlock canvas')),
1483 _step17;
1484
1485 try {
1486 for (_iterator17.s(); !(_step17 = _iterator17.n()).done;) {
1487 var c = _step17.value;
1488
1489 if (c.initCanvas) {
1490 c.initCanvas();
1491 }
1492 }
1493 } catch (err) {
1494 _iterator17.e(err);
1495 } finally {
1496 _iterator17.f();
1497 }
1498 }
1499 }
1500 });
1501 });
1502 }
1503
1504 if (props.includes) {
1505 Array.from(props.includes).forEach(function (includedClass) {
1506 //Сначала проверим, может ничего и не надо делать
1507 if (!isActive(btn) && root.querySelector('.active.includes.' + includedClass)) {
1508 return;
1509 }
1510
1511 var includedDivs = root.querySelectorAll(':not(.includes).' + includedClass);
1512 Array.from(includedDivs).forEach(function (includedDiv) {
1513 if (!isActive(btn)) {
1514 includedDiv.classList.add('excluded'); //И снимаем там все галки
1515
1516 if (includedDiv.querySelector('.btn.active')) {
1517 Array.from(includedDiv.querySelectorAll('.btn.active')).forEach(function (btn) {
1518 btn.click();
1519 });
1520 }
1521 } else {
1522 if (!excludedArr.includes(includedClass)) {
1523 includedDiv.classList.remove('excluded'); //Обновлянем все просмотрщики с канвасами
1524
1525 var _iterator18 = _createForOfIteratorHelper(includedDiv.querySelectorAll('.imageField .imageBlock canvas')),
1526 _step18;
1527
1528 try {
1529 for (_iterator18.s(); !(_step18 = _iterator18.n()).done;) {
1530 var c = _step18.value;
1531
1532 if (c.initCanvas) {
1533 c.initCanvas();
1534 }
1535 }
1536 } catch (err) {
1537 _iterator18.e(err);
1538 } finally {
1539 _iterator18.f();
1540 }
1541 }
1542 }
1543 });
1544 });
1545 } //valuesToWrite - массив обьектов, описывающий какое значение записать в определённое свойство объекта с указанным селектором
1546
1547
1548 if (props.valuesToWrite) {
1549 if (!Array.isArray(props.valuesToWrite)) {
1550 ce('valuesToWrite_is_not_an_array', btn.parentElement.className);
1551 } else {
1552 props.valuesToWrite.forEach(function (valObj) {
1553 var ok = true;
1554
1555 if (!valObj.selector) {
1556 ce('valuesToWrite_has_no_selector', btn.parentElement.className);
1557 ok = false;
1558 }
1559
1560 if (!valObj.property) {
1561 ce('valuesToWrite_has_no_property', btn.parentElement.className);
1562 ok = false;
1563 }
1564
1565 if (!valObj.value && valObj.value !== '') {
1566 ce('valuesToWrite_has_no_value', btn.parentElement.className);
1567 ok = false;
1568 }
1569
1570 if (ok === false) {
1571 return;
1572 }
1573
1574 var target = root.querySelector(valObj.selector);
1575
1576 if (!target) {
1577 ce('valuesToWrite_target_not_found', 'Класс кнопки: ' + btn.parentElement.className + ', селектор: ' + valObj.selector);
1578 ok = false;
1579 } else {
1580 target[valObj.property] = valObj.value;
1581 }
1582 });
1583 }
1584 }
1585 }
1586
1587 switch (btn.dataset.type) {
1588 case 'switchButton':
1589 //Если эта кнопка - часть группы радиобаттонов и её включили, то снимаем остальные радиобаттоны в этой группе
1590 if ((btn.dataset.radiogroup || btn.dataset.select) && !isActive(btn)) {
1591 Array.from(btn.parentElement.parentElement.querySelectorAll('.btn.active')).forEach(function (b) {
1592 task.buttonSmashed(b);
1593 });
1594 } //Перекрашиваем кнопку
1595
1596
1597 invertButton(btn);
1598
1599 if (props) {
1600 propsChecks();
1601 }
1602
1603 break;
1604
1605 case 'linkButton':
1606 if (props) {
1607 propsChecks();
1608 }
1609
1610 btn.classList.add('clicked');
1611 break;
1612 } //Если это кнопка селекта
1613
1614
1615 if (btn.dataset.select) {
1616 var select = btn.closest('.select'),
1617 selectText = select.querySelector('.selectText');
1618
1619 if (isActive(btn)) {
1620 selectText.innerHTML = btn.querySelector('a').innerHTML;
1621 select.dataset.selection = btn.dataset.name;
1622 } else {
1623 selectText.innerHTML = '';
1624 select.dataset.selection = '';
1625 }
1626
1627 select.classList.remove('opened');
1628 } //Проверки для обработчиков событий
1629
1630
1631 var eventHandlersName = btn.closest('.element') && btn.closest('.element').dataset.name ? "".concat(btn.closest('.element').dataset.name, "___").concat(btn.dataset.name) : btn.dataset.name;
1632 var buttHandlers = this.elementEventHandlers[eventHandlersName];
1633
1634 if (!isTemporary && buttHandlers && btn.lastActivationState !== isActive(btn)) {
1635 if (buttHandlers.onActivation && isActive(btn)) {
1636 buttHandlers.onActivation.apply(this, [btn]);
1637 }
1638
1639 if (buttHandlers.onDeactivation && !isActive(btn)) {
1640 buttHandlers.onDeactivation.apply(this, [btn]);
1641 }
1642 }
1643
1644 if (!isTemporary) btn.lastActivationState = isActive(btn);
1645 this.lastChangedElement = btn;
1646 },
1647 //Служебная функция, добавляет группу полей в группКонтейнер. Здесь реально не на что смотреть, проходите.
1648 addGroup: function addGroup(gc
1649 /* groupContainer */
1650 ) {
1651 var group = document.createElement('div'),
1652 task = this;
1653 group.className = isGroupInfinite(gc) ? 'group infiniteChild' : 'group';
1654 group.innerHTML = fieldsPropsArray[gc.dataset.codepropindex];
1655 gc.appendChild(group);
1656 this.initFieldsHere(group); //На случай вложенных групп
1657
1658 Array.from(gc.querySelectorAll('.groupContainer')).forEach(function (gcInner) {
1659 var c = gc.closest('.groupContainer');
1660
1661 if (c && c === gc) {
1662 return;
1663 }
1664
1665 task.addGroup(gcInner);
1666 });
1667 },
1668 //Функция валидации всех ШВшных инпутов и текстарей. Если вам нужна какая-то непредусмотренная валидация - смело дополняйте в отведённом месте.
1669 validateInput: function validateInput(input) {
1670 var checksPassed;
1671
1672 if (input.closest('.alien')) {
1673 input.classList.add('skipMe');
1674 return;
1675 }
1676
1677 var task = this,
1678 root = this.getDOMElement(),
1679 parent = input.closest('.group') || input.closest('.answerBlock');
1680 var isValidationPassed = false,
1681 //validationPassed - поле прошло валидацию, validationRejected - не прошло.
1682 props = null;
1683
1684 if (input.dataset.props) {
1685 props = fieldsPropsArray[input.dataset.props];
1686 }
1687
1688 if (!input.value) {
1689 input.classList.add('clear');
1690 input.classList.remove('validationPassed');
1691 input.classList.remove('validationRejected');
1692
1693 if (isOptional(props)) {
1694 input.classList.add('skipMe');
1695 } //this.turnOffErrorClassOnParentElementTagBlock(input);
1696
1697
1698 return false;
1699 } else {
1700 input.classList.remove('skipMe');
1701 input.classList.remove('clear');
1702 } //Определяемся с валидацией - либо используем описанную у самого поля (если есть), либо по общим правилам
1703
1704
1705 var eHandlers = task.elementEventHandlers[input.dataset.name];
1706
1707 if (eHandlers && eHandlers.validation) {
1708 isValidationPassed = eHandlers.validation.apply(this, [input]);
1709 } else {
1710 switch (input.dataset.type) {
1711 case 'date':
1712 if (isDate(input.value, props && props.format ? props.format.toUpperCase() : 'DD-MM-YYYY')) {
1713 isValidationPassed = true;
1714 }
1715
1716 if (props) {
1717 if (isValidationPassed && (props.aheadOf || props.behindOf)) {
1718 checksPassed = true;
1719 var aheadOfWhat = null,
1720 behindOfWhat = null;
1721
1722 if (props.aheadOf) {
1723 aheadOfWhat = parent.querySelector('.fieldName_' + props.aheadOf + ' .validationPassed');
1724 }
1725
1726 if (props.behindOf) {
1727 behindOfWhat = parent.querySelector('.fieldName_' + props.behindOf + ' .validationPassed');
1728 }
1729
1730 if (props.aheadOf && aheadOfWhat) {
1731 //Надо сравнить обе даты, приведя каждую из них к удобному виду
1732 checksPassed = isFirstDateAheadOfSecond(input.value, props.format ? props.format.toUpperCase() : 'DD-MM-YYYY', aheadOfWhat.value, aheadOfWhat.dataset.props ? fieldsPropsArray[aheadOfWhat.dataset.props].format ? fieldsPropsArray[aheadOfWhat.dataset.props].format.toUpperCase() : 'DD-MM-YYYY' : 'DD-MM-YYYY');
1733 }
1734
1735 if (checksPassed && props.behindOf && behindOfWhat) {
1736 //Надо сравнить обе даты, приведя каждую из них к удобному виду
1737 checksPassed = isFirstDateAheadOfSecond(behindOfWhat.value, behindOfWhat.dataset.props ? fieldsPropsArray[behindOfWhat.dataset.props].format ? fieldsPropsArray[behindOfWhat.dataset.props].format.toUpperCase() : 'DD-MM-YYYY' : 'DD-MM-YYYY', input.value, props.format ? props.format.toUpperCase() : 'DD-MM-YYYY');
1738 }
1739
1740 isValidationPassed = checksPassed;
1741 }
1742 }
1743
1744 break;
1745
1746 case 'text':
1747 case 'textarea':
1748 checksPassed = true;
1749
1750 if (props) {
1751 if (props.flags) {
1752 if (props.flags.includes('cyrillic') && !/^[\u0430-\u044f\s-"]*$/i.test(input.value)) {
1753 checksPassed = false;
1754 }
1755
1756 if (props.flags.includes('firstCapital') && !/^([\u0410-\u042F][\u0430-\u044f]+\s?)*$/.test(input.value)) {
1757 checksPassed = false;
1758 }
1759 }
1760
1761 if (props.min && input.value.length < props.min) {
1762 checksPassed = false;
1763 }
1764
1765 if (props.max && input.value.length > props.max) {
1766 checksPassed = false;
1767 }
1768 }
1769
1770 isValidationPassed = checksPassed;
1771 break;
1772
1773 case 'link':
1774 if (isLink(input.value)) {
1775 isValidationPassed = true;
1776 }
1777
1778 if (props) {
1779 //В свойствах поля задан конкретный список сервисов, поэтому валидируем ссылку на принадлежность к каждому из них.
1780 if (isValidationPassed && props.services) {
1781 var pass;
1782
1783 if (typeof props.services === 'string') {
1784 props.services = [props.services];
1785 }
1786
1787 var _iterator19 = _createForOfIteratorHelper(props.services),
1788 _step19;
1789
1790 try {
1791 for (_iterator19.s(); !(_step19 = _iterator19.n()).done;) {
1792 var service = _step19.value;
1793
1794 if (isServiceLink(input.value, service.toLowerCase())) {
1795 pass = true;
1796 break;
1797 }
1798 }
1799 } catch (err) {
1800 _iterator19.e(err);
1801 } finally {
1802 _iterator19.f();
1803 }
1804
1805 if (!pass) {
1806 isValidationPassed = false;
1807 }
1808 }
1809
1810 if (isValidationPassed && props.domains) {
1811 var _pass;
1812
1813 if (typeof props.domains === 'string') {
1814 props.domains = [props.domains];
1815 }
1816
1817 for (var _i2 = 0, _Array$from = Array.from(props.domains); _i2 < _Array$from.length; _i2++) {
1818 var domain = _Array$from[_i2];
1819
1820 if (new URL(input.value).host.includes(domain)) {
1821 _pass = true;
1822 break;
1823 }
1824 }
1825
1826 if (!_pass) {
1827 isValidationPassed = false;
1828 }
1829 }
1830 }
1831
1832 break;
1833
1834 case 'email':
1835 if (isEmail(input.value)) {
1836 isValidationPassed = true;
1837 }
1838
1839 break;
1840
1841 case 'integer':
1842 checksPassed = isInteger(input.value);
1843
1844 if (props) {
1845 if (checksPassed && props.min && parseInt(input.value) < props.min) {
1846 checksPassed = false;
1847 }
1848
1849 if (checksPassed && props.max && parseInt(input.value) > props.max) {
1850 checksPassed = false;
1851 }
1852 }
1853
1854 isValidationPassed = checksPassed;
1855 break;
1856
1857 case 'float':
1858 checksPassed = false;
1859 checksPassed = isInteger(input.value) || isFloat(input.value);
1860
1861 if (props) {
1862 if (checksPassed && props.min && parseFloat(input.value) < props.min) {
1863 checksPassed = false;
1864 }
1865
1866 if (checksPassed && props.max && parseFloat(input.value) > props.max) {
1867 checksPassed = false;
1868 }
1869 }
1870
1871 isValidationPassed = checksPassed;
1872 break;
1873 } //Проверка на соответствие глобальным лимитам
1874
1875
1876 if (isValidationPassed) {
1877 if (input.dataset.type === 'textarea' && window.textareaCharLimit && input.value.length > window.textareaCharLimit || input.dataset.type !== 'textarea' && window.inputCharLimit && input.value.length > window.inputCharLimit) {
1878 isValidationPassed = false;
1879 }
1880 }
1881 } //Проверка на уникальность при наличии нужного флага
1882
1883
1884 if (input.value && isValidationPassed && props && props.flags && props.flags.includes('unique')) {
1885 var allInputsVals = [];
1886 Array.from(root.querySelectorAll('input')).forEach(function (i) {
1887 if (!i.value || i === input) {
1888 return;
1889 }
1890
1891 var iValue;
1892
1893 if (i.dataset.type === 'link') {
1894 iValue = skipProtocolParametersAndOtherStuff(i.value);
1895 } else {
1896 iValue = i.value;
1897 }
1898
1899 allInputsVals.push(iValue);
1900 });
1901 var iValue;
1902
1903 if (input.dataset.type === 'link') {
1904 iValue = skipProtocolParametersAndOtherStuff(input.value);
1905 } else {
1906 iValue = input.value;
1907 }
1908
1909 if (allInputsVals.includes(iValue) || props && props.uniqueAdditional && Array.isArray(props.uniqueAdditional) && props.uniqueAdditional.includes(iValue)) {
1910 isValidationPassed = false;
1911 }
1912 } //Место для кастомного кода
1913
1914
1915 if (!isValidationPassed) {
1916 input.classList.remove('validationPassed');
1917 input.classList.add('validationRejected');
1918 } else {
1919 input.classList.add('validationPassed');
1920 input.classList.remove('validationRejected');
1921 this.turnOffErrorClassOnParentElementTagBlock(input);
1922 }
1923
1924 return true;
1925 },
1926 //Я не знаю, кто написал эту функцию. Когда я стал лид-асессором, она уже была написана кем-то (Денисом Раскостовым?) и все её использовали в своих шаблонах. А мы чё, хуже?)))
1927 addError: function addError(message, field, errors) {
1928 errors || (errors = {
1929 task_id: this.getOptions().task.id,
1930 errors: {}
1931 });
1932 errors.errors[field] = {
1933 message: message
1934 };
1935 return errors;
1936 },
1937 //Аналог вышеописанной функции, но вешает на конкретные DOM-элементы ШВшную плашку с ошибкой (а не на Хэндлбарсный контрол). Используйте на здоровье при кастомной валидации!
1938 addErrorToField: function addErrorToField(field, msg, errors) {
1939 var root = this.getDOMElement(),
1940 task = this;
1941 var wrappedMSG = "<div class=\"errorMsgBlockInner\">".concat(msg, "</div>");
1942
1943 if (!field.classList.contains('element')) {
1944 field = field.closest('.element');
1945 }
1946
1947 if (!field.dataset.errorMsgNumber) {
1948 field.dataset.errorMsgNumber = this.errorMSGs.length;
1949 var newMSG = document.createElement('div');
1950 newMSG.className = 'errorMsgBlock';
1951 newMSG.innerHTML = wrappedMSG;
1952 this.errorMSGs.push(field.insertBefore(newMSG, field.firstElementChild));
1953 } else {
1954 this.errorMSGs[field.dataset.errorMsgNumber].innerHTML = wrappedMSG;
1955 }
1956
1957 this.turnOnErrorClassOnParentElementTagBlock(field);
1958 return this.addError(msg, 'Fallout 76', errors);
1959 },
1960 //Всё понятно из названия, а если нет - то из содержания уж точно. Вам, наверное, не понадобится.
1961 turnOnErrorClassOnParentElementTagBlock: function turnOnErrorClassOnParentElementTagBlock(someElement) {
1962 var parentContainer = someElement.closest('.element');
1963
1964 if (parentContainer) {
1965 parentContainer.classList.add('withSomeError');
1966 }
1967
1968 makeSureThisDivFitsOnScreen(parentContainer.querySelector('.errorMsgBlock'));
1969 },
1970 //Всё понятно из названия, а если нет - то из содержания уж точно. Может вам понадобиться, если где-то надо спрятать плашку с ошибкой, выведенной addErrorToField().
1971 turnOffErrorClassOnParentElementTagBlock: function turnOffErrorClassOnParentElementTagBlock(someElement) {
1972 var parentContainer = someElement.closest('.element');
1973
1974 if (parentContainer) {
1975 parentContainer.classList.remove('withSomeError');
1976 }
1977 },
1978 //Функция отвечает за обработку параметра convert в спекМаппере
1979 convertSpecField: function convertSpecField(res, convert, func) {
1980 var task = this;
1981
1982 if (typeof convert == "string") {
1983 convert = [convert];
1984 }
1985
1986 convert.forEach(function (conv) {
1987 switch (conv) {
1988 case "strToBool":
1989 res = !!(res === true || res && ['true', 'yes', 'yep', 'sure', 'да'].includes(res.toLowerCase()));
1990 break;
1991
1992 case "invert":
1993 res = !res;
1994 break;
1995
1996 case "infiniteToStringArray":
1997 if (res) {
1998 var result = [];
1999
2000 var _iterator20 = _createForOfIteratorHelper(res),
2001 _step20;
2002
2003 try {
2004 for (_iterator20.s(); !(_step20 = _iterator20.n()).done;) {
2005 var line = _step20.value;
2006
2007 for (var key in line) {
2008 result.push(line[key]);
2009 }
2010 }
2011 } catch (err) {
2012 _iterator20.e(err);
2013 } finally {
2014 _iterator20.f();
2015 }
2016
2017 res = result;
2018 } else {
2019 res = null;
2020 }
2021
2022 break;
2023
2024 case "function":
2025 if (!func) {
2026 ce('specmapper_conversion_no_function', 'type: ' + conv);
2027 } else {
2028 res = func.apply(task, [res]);
2029 }
2030
2031 break;
2032
2033 default:
2034 ce('specmapper_conversion_type_not_found', 'type: ' + conv);
2035 }
2036 });
2037 return res;
2038 },
2039 //Тот самый спекМаппер, о котором вам рассказывали на экскурсии по Звёздному Крейсеру "Галактика". И нет, я не сайлон. Наверное...
2040 mapSpecField: function mapSpecField(mapperObj, solution) {
2041 var root = this.getDOMElement(),
2042 task = this,
2043 res;
2044
2045 if (!mapperObj.function && mapperObj.f) {
2046 mapperObj.function = mapperObj.f;
2047 }
2048
2049 if (!mapperObj.convert && mapperObj.function) {
2050 mapperObj.convert = ['function'];
2051 }
2052
2053 gettingRes: {
2054 if (mapperObj.from) {
2055 if (Array.isArray(mapperObj.from)) {
2056 var _iterator21 = _createForOfIteratorHelper(mapperObj.from),
2057 _step21;
2058
2059 try {
2060 for (_iterator21.s(); !(_step21 = _iterator21.n()).done;) {
2061 var name = _step21.value;
2062
2063 if (this.output[name] || this.output[name] === false) {
2064 res = this.output[name];
2065 break gettingRes;
2066 }
2067 }
2068 } catch (err) {
2069 _iterator21.e(err);
2070 } finally {
2071 _iterator21.f();
2072 }
2073 } else {
2074 if (this.output[mapperObj.from] || this.output[mapperObj.from] === false) {
2075 res = this.output[mapperObj.from];
2076 break gettingRes;
2077 }
2078 }
2079 }
2080
2081 if (mapperObj.fromT) {
2082 if (Array.isArray(mapperObj.fromT)) {
2083 var _iterator22 = _createForOfIteratorHelper(mapperObj.fromT),
2084 _step22;
2085
2086 try {
2087 for (_iterator22.s(); !(_step22 = _iterator22.n()).done;) {
2088 var _name = _step22.value;
2089
2090 if (solution[_name] || solution[_name] === false) {
2091 res = solution[_name];
2092 break gettingRes;
2093 }
2094 }
2095 } catch (err) {
2096 _iterator22.e(err);
2097 } finally {
2098 _iterator22.f();
2099 }
2100 } else {
2101 if (solution[mapperObj.fromT] || solution[mapperObj.fromT] === false) {
2102 res = solution[mapperObj.fromT];
2103 break gettingRes;
2104 }
2105 }
2106 }
2107
2108 if (!mapperObj.from && !mapperObj.fromT) {
2109 res = this.output;
2110 }
2111 }
2112
2113 if (mapperObj.convert) {
2114 res = this.convertSpecField(res, mapperObj.convert, mapperObj.function);
2115 }
2116
2117 return res;
2118 },
2119 //Серьёзно, вам нужен комментарий для этой функции?
2120 validate: function validate(solution) {
2121 var root = this.getDOMElement(),
2122 task = this,
2123 input = this.getTask().input_values;
2124
2125 if (printSolution) {
2126 console.log('Таск:', root.dataset.number);
2127 console.log('Солюшка до валидации:', Object.assign({}, solution.output_values));
2128 }
2129
2130 var errors, output; //Валидация и сбор солюшки по ШВшным элементам
2131
2132 if (!this.dontGatherSolution) {
2133 var eAndS = this.gatherErrorsAndSolution();
2134 errors = eAndS.errors;
2135 output = eAndS.solution;
2136 } else {
2137 output = this.theOneSolution;
2138 }
2139
2140 this.output = output; //СпекМаппер
2141
2142 var specFields = 0,
2143 res = false;
2144
2145 if (useSpecMapper) {
2146 res = {}; //Если в HTML есть скрытое поле для загрузки файлов - его надо обязательно смаппить в выходное поле files
2147
2148 if (root.querySelector('.element.__fileUploader')) {
2149 specMapper.files = {
2150 fromT: "files"
2151 };
2152 }
2153
2154 for (var outField in specMapper) {
2155 specFields++;
2156 var value = this.mapSpecField(specMapper[outField], solution.output_values);
2157
2158 if (value !== undefined) {
2159 res[outField] = value;
2160 }
2161 }
2162
2163 if (usingOneRawData) {
2164 var tord = $.extend(true, {}, this.theOneSolution);
2165 tord.__theOneCustomStorage.__actionLog = []; //Экономим трафик и место
2166
2167 tord.__theOneCustomStorage.__timingsStorage = {}; //Оно там не нужно
2168
2169 delete tord.undefined; //Были прецеденты)))
2170
2171 res.__theOneRawData = JSON.stringify(tord);
2172
2173 if (printSolution) {
2174 console.log('__theOneRawData:', JSON.parse(res['__theOneRawData']));
2175 }
2176 }
2177 } //Валидация
2178
2179
2180 if (window.validation) {
2181 errors = validation.apply(this, [res, output, solution, errors, root]);
2182 } //Завершения валидэйта
2183
2184
2185 solution.output_values = res ? res : {
2186 "result": output
2187 };
2188 errors = errors || TolokaHandlebarsTask.prototype.validate.apply(this, solution);
2189
2190 if (errors && window.noValidationErrors) {
2191 window.noValidationErrors = false;
2192 }
2193
2194 if (printSolution) {
2195 console.log('Солюшка ШВ:', $.extend(true, {}, this.output), ' Полная: ', $.extend(true, {}, this.theOneSolution));
2196 console.log('Итоговая солюшка:', solution.output_values);
2197 console.log('Ошибки:', errors);
2198 }
2199
2200 return errors;
2201 },
2202 //Функция для ШВшной валидации и сбора солюшки во всём таске. Если beQuiet - не ругается на ошибки и не собирает их, а просто собирает валидную солюшку.
2203 //whereToGather - либо селектор, в котором всё это делать, либо элемент с уникальным в рамках таска className
2204 gatherErrorsAndSolution: function gatherErrorsAndSolution(beQuiet, whereToGather) {
2205 var root = this.getDOMElement(),
2206 task = this,
2207 selectorPrefix,
2208 output = {},
2209 errors = null,
2210 validationGroupIndex = -1;
2211 selectorPrefix = whereToGather ? typeof whereToGather == 'string' ? whereToGather : function () {
2212 var s = '';
2213
2214 var _iterator23 = _createForOfIteratorHelper(whereToGather.classList),
2215 _step23;
2216
2217 try {
2218 for (_iterator23.s(); !(_step23 = _iterator23.n()).done;) {
2219 var item = _step23.value;
2220 s += '.' + item;
2221 }
2222 } catch (err) {
2223 _iterator23.e(err);
2224 } finally {
2225 _iterator23.f();
2226 }
2227
2228 return s + ' ';
2229 }() : '.answerBlock '; //Добавляем и валидируем все обычные поля
2230
2231 var valResult = this.validateAndGrabFieldsInHere(selectorPrefix, errors, beQuiet, true);
2232 output = valResult.value;
2233 errors = valResult.errors;
2234 /* Теперь у нам предстоит 2 прохода по бесконечным группам - сначала мы все нумеруем, а потом собираем солюшку. */
2235 //Индексируем группы для оптимизации дальнейшего алгоритма и отладки
2236
2237 Array.from(root.querySelectorAll(selectorPrefix + '.groupContainer:not(.excluded)')).forEach(function (gc) {
2238 var groups = gc.querySelectorAll(':scope>.group');
2239
2240 for (var i = 0; i < groups.length; i++) {
2241 validationGroupIndex++;
2242
2243 if (groups[i].dataset.validationGroupIndex) {
2244 groups[i].classList.remove('validationGroupIndex' + groups[i].dataset.validationGroupIndex);
2245 }
2246
2247 groups[i].dataset.validationGroupIndex = validationGroupIndex;
2248 groups[i].classList.add('validationGroupIndex' + validationGroupIndex);
2249 groups[i].dataset.numberInContainer = i;
2250 }
2251 }); //Добавляем и валидируем группКонтейнеры погруппно каждый
2252
2253 Array.from(root.querySelectorAll(selectorPrefix + '.groupContainer:not(.excluded)')).forEach(function (gc) {
2254 var groups = gc.querySelectorAll(':scope>.group');
2255
2256 for (var i = 0; i < groups.length; i++) {
2257 //Если это какая-то "левая", неШВшная группа
2258 if (groups[i].parentElement !== gc) {
2259 continue;
2260 } //Проверки для "бесконечных" групп
2261
2262
2263 if (isGroupInfinite(groups[i].parentElement)) {
2264 //Если группа пустая, но при этом есть другие - пропускаем (для "бесконечных" групп)
2265 groups[i].classList.remove('infiniteSkipped');
2266
2267 if (i > 0 && !groups[i].classList.contains('hasSomething') && !groups[i].querySelector('.group.hasSomething')) {
2268 groups[i].classList.add('infiniteSkipped');
2269 continue;
2270 } else if (i === 0 && !groups[i].classList.contains('hasSomething') && groups[i].closest('.group.infiniteSkipped')) {
2271 continue;
2272 }
2273 }
2274
2275 var _valResult = task.validateAndGrabFieldsInHere(selectorPrefix + ".groupContainer .group.validationGroupIndex".concat(groups[i].dataset.validationGroupIndex, ">div:not(.groupContainer)"), errors, beQuiet); //Если valResult непустой - записываем его в выходную спеку
2276
2277
2278 if (_valResult.isNotEmpty) {
2279 if (isGroupInfinite(gc)) {
2280 var gcChain = [{
2281 name: gc.dataset.name,
2282 number: i
2283 }];
2284 var parentInfiniteChildGroup = gc.closest('.group.infiniteChild');
2285
2286 if (parentInfiniteChildGroup) {
2287 //Если эта бесконечная группа вложена как минимум в какую-то другую
2288 //Идём вверх по родителям и создаём последовательность имён и номеров групп, чтобы получить как-бы адрес текущей группы в солюшке
2289 do {
2290 gcChain.push({
2291 name: parentInfiniteChildGroup.parentElement.dataset.name,
2292 number: parseInt(parentInfiniteChildGroup.dataset.numberInContainer)
2293 });
2294 parentInfiniteChildGroup = parentInfiniteChildGroup.parentElement.closest('.group.infiniteChild');
2295 } while (parentInfiniteChildGroup); //Теперь перемещаем курсор снаружи вглубь солюшки по составленной цепочке попутно создавая нужные ключи, если их нет
2296
2297
2298 var cursor = output;
2299
2300 for (var n = gcChain.length - 1; n >= 0; n--) {
2301 var gcName = gcChain[n].name,
2302 gcNumber = gcChain[n].number;
2303
2304 if (!cursor[gcName]) {
2305 cursor[gcName] = [];
2306 }
2307
2308 if (!cursor[gcName][gcNumber]) {
2309 cursor[gcName][gcNumber] = {};
2310 }
2311
2312 cursor = cursor[gcName][gcNumber];
2313 } //На данный момент наш курсор находится в финальной точке и указывает на конечный объект в солюшке для текущей группы.
2314 //Остаётся лишь заполнить его собранной солюшкой
2315
2316
2317 for (var val in _valResult.value) {
2318 cursor[val] = _valResult.value[val];
2319 }
2320 } else {
2321 //Если текущая бесконечная группа у нас не содержится внутри другой бесконечной группы - всё намного проще
2322 if (!output[gc.dataset.name]) {
2323 output[gc.dataset.name] = [];
2324 }
2325
2326 if (!output[gc.dataset.name][i]) {
2327 output[gc.dataset.name][i] = {};
2328 }
2329
2330 for (var _val in _valResult.value) {
2331 output[gc.dataset.name][i][_val] = _valResult.value[_val];
2332 }
2333 }
2334 } else {
2335 for (var _val2 in _valResult.value) {
2336 output[_val2 + (i > 0 ? i : '')] = _valResult.value[_val2];
2337 }
2338 }
2339 }
2340
2341 errors = _valResult.errors;
2342 }
2343 }); //Проверяем заполненность всех radioGroup
2344
2345 Array.from(root.querySelectorAll(selectorPrefix + '.radioGroup:not(.optional)')).forEach(function (radioGroup) {
2346 if (!beQuiet && !radioGroup.querySelector('.btn.active') && !radioGroup.closest('.excluded') && !radioGroup.closest('.window:not(.opened)')) {
2347 var group = radioGroup.closest('.group');
2348
2349 if (group) {
2350 var gc = group.parentElement;
2351
2352 if (gc && group && group.closest('.infiniteSkipped')) {
2353 return;
2354 }
2355
2356 if (gc && group && isGroupInfinite(gc) && !group.classList.contains('hasSomething') && gc.querySelector('.group.hasSomething')) {
2357 return;
2358 }
2359 }
2360
2361 errors = task.addErrorToField(radioGroup, 'Пожалуйста, выберите соответствующую кнопку!', errors);
2362 }
2363 }); //Проверяем заполненность всех checkboxGroup
2364
2365 Array.from(root.querySelectorAll(selectorPrefix + '.checkboxGroup:not(.optional)')).forEach(function (checkboxGroup) {
2366 if (!beQuiet && !checkboxGroup.querySelector('.btn.active') && !checkboxGroup.closest('.excluded') && !checkboxGroup.closest('.window:not(.opened)')) {
2367 var group = checkboxGroup.closest('.group');
2368
2369 if (group) {
2370 var gc = group.parentElement;
2371
2372 if (gc && group && group.closest('.infiniteSkipped')) {
2373 return;
2374 }
2375
2376 if (gc && group && isGroupInfinite(gc) && !group.classList.contains('hasSomething') && gc.querySelector('.group.hasSomething')) {
2377 return;
2378 }
2379 }
2380
2381 errors = task.addErrorToField(checkboxGroup, 'Пожалуйста, выберите соответствующую кнопку!', errors);
2382 }
2383 }); //Проверяем заполненность всех селектов
2384
2385 Array.from(root.querySelectorAll(selectorPrefix + '.select:not(.optional)')).forEach(function (select) {
2386 if (!beQuiet && !select.dataset.selection && !select.closest('.excluded') && !select.closest('.window:not(.opened)')) {
2387 var group = select.closest('.group');
2388
2389 if (group) {
2390 var gc = group.parentElement;
2391
2392 if (gc && group && group.closest('.infiniteSkipped')) {
2393 return;
2394 }
2395
2396 if (gc && group && isGroupInfinite(gc) && !group.classList.contains('hasSomething') && gc.querySelector('.group.hasSomething')) {
2397 return;
2398 }
2399 }
2400
2401 errors = task.addErrorToField(select, 'Пожалуйста, выберите элемент выпадающего списка!', errors);
2402 }
2403 });
2404 return {
2405 "errors": errors,
2406 "solution": output
2407 };
2408 },
2409 //Производит ШВшную валидацию и сбор ШВшной солюшки (далее по тексту - просто "Валидация") в указанном элементе.
2410 validateAndGrabFieldsInHere: function validateAndGrabFieldsInHere(selector, errors, beQuiet, notInGroup) {
2411 var task = this,
2412 root = this.getDOMElement(),
2413 result = {
2414 "value": {},
2415 "errors": errors,
2416 "isNotEmpty": false
2417 }; //Добавляем все инпуты
2418
2419 Array.from(root.querySelectorAll(selector + ' input, ' + selector + ' textarea')).forEach(function (field) {
2420 if (notInGroup && field.closest('.groupContainer')) {
2421 return result;
2422 }
2423
2424 if (field.closest('.excluded') || field.closest('.alien') || field.closest('.window:not(.opened)')) {
2425 return result;
2426 } //Дополнительная проверка для инпутов вложенных полей
2427
2428
2429 task.validateInput(field);
2430
2431 if (field.classList.contains('skipMe')) {
2432 return;
2433 }
2434
2435 if (!field.classList.contains('validationPassed')) {
2436 if (!beQuiet) {
2437 result.errors = task.addErrorToField(field, 'Проверьте, пожалуйста, корректность заполнения этого поля.', result.errors);
2438 }
2439 } else {
2440 result.value[field.dataset.solutionname || field.dataset.name] = field.value;
2441 result.isNotEmpty = true;
2442 }
2443 }); //Добавляем все нажатые кнопки
2444
2445 Array.from(root.querySelectorAll(selector + ' .btn.active')).forEach(function (btn) {
2446 if (notInGroup && btn.closest('.groupContainer')) {
2447 return result;
2448 }
2449
2450 if (btn.closest('.excluded') || btn.closest('.alien') || btn.closest('.window:not(.opened)')) {
2451 return result;
2452 }
2453
2454 var groupSolutionName = btn.dataset.groupsolutionname || btn.dataset.radiogroup || btn.dataset.select || btn.dataset.checkboxgroup;
2455
2456 if (btn.dataset.radiogroup) {
2457 //Эта кнопка - часть массива радиобаттонов
2458 result.value[groupSolutionName] = btn.dataset.name;
2459 } else if (btn.dataset.select) {
2460 //Эта кнопка - часть селекта
2461 result.value[groupSolutionName] = btn.dataset.name;
2462 } else if (btn.dataset.checkboxgroup) {
2463 //Эта кнопка - часть массива чекбоксов (от обычных чекбоксов они отличаются сбором данных в массив)
2464 if (!result.value[groupSolutionName]) {
2465 result.value[groupSolutionName] = [];
2466 }
2467
2468 result.value[groupSolutionName].push(btn.dataset.name);
2469 } else {
2470 result.value[btn.dataset.name] = true;
2471 }
2472
2473 result.isNotEmpty = true;
2474 }); //Добавляем выключенные кнопки
2475
2476 if (typeof buttonsCanBeFalse === "undefined" || buttonsCanBeFalse) {
2477 Array.from(root.querySelectorAll(selector + '.buttonGroup .btn:not(.active)')).forEach(function (btn) {
2478 if (notInGroup && btn.closest('.groupContainer')) {
2479 return result;
2480 }
2481
2482 if (btn.closest('.excluded') || btn.closest('.alien') || btn.closest('.window:not(.opened)')) {
2483 return result;
2484 }
2485
2486 var groupSolutionName = btn.dataset.groupsolutionname || btn.dataset.radiogroup || btn.dataset.select || btn.dataset.checkboxgroup;
2487 result.value[btn.dataset.name] = false;
2488 result.isNotEmpty = true;
2489 });
2490 } //Валидируем ШВшные загрузчики файлов
2491
2492
2493 var _iterator24 = _createForOfIteratorHelper(root.querySelectorAll(selector + ' .element.fileField')),
2494 _step24;
2495
2496 try {
2497 for (_iterator24.s(); !(_step24 = _iterator24.n()).done;) {
2498 var fileField = _step24.value;
2499
2500 if (notInGroup && fileField.closest('.groupContainer')) {
2501 continue;
2502 }
2503
2504 if (fileField.closest('.excluded') || fileField.closest('.alien') || fileField.closest('.window:not(.opened)')) {
2505 continue;
2506 } //Валидация
2507
2508
2509 result.errors = fileField.validate(result.errors, beQuiet); //Сбор солюшки
2510
2511 if (fileField.classList.contains('validationPassed')) {
2512 var files = [];
2513 var fileLines = fileField.querySelectorAll('.fcFileLine');
2514
2515 var _iterator25 = _createForOfIteratorHelper(fileLines),
2516 _step25;
2517
2518 try {
2519 for (_iterator25.s(); !(_step25 = _iterator25.n()).done;) {
2520 var line = _step25.value;
2521
2522 if (line.dataset.attachmentId) {
2523 files.push(line.dataset.attachmentId);
2524 }
2525 }
2526 } catch (err) {
2527 _iterator25.e(err);
2528 } finally {
2529 _iterator25.f();
2530 }
2531
2532 if (files.length) {
2533 result.value[fileField.dataset.name] = files;
2534 }
2535 }
2536 }
2537 } catch (err) {
2538 _iterator24.e(err);
2539 } finally {
2540 _iterator24.f();
2541 }
2542
2543 return result;
2544 },
2545 //Инициализирует поле для загрузки файла
2546 initFileField: function initFileField(field) {
2547 var _this5 = this;
2548
2549 var task = this,
2550 root = this.getDOMElement(),
2551 fContainer = field.querySelector('.fContainer'),
2552 fTitle = field.querySelector('.stepTitle'),
2553 fileUploader = root.querySelector('.__fileUploader'),
2554 fieldName = field.dataset.name;
2555
2556 if (!fileUploader) {
2557 ce('Вы добавили в спеку поле типа file, оно отрендерилось, но вы не добавили для него стандартный Толоковский загручик файлов. Читайте доку.', field);
2558 return;
2559 }
2560
2561 var fuFiles = fileUploader.querySelector('.field_file__files');
2562
2563 if (!fuFiles) {
2564 ce('Очень странно, у загрузчика файлов не найден div .field_file__files...', fileUploader);
2565 return;
2566 }
2567
2568 var props;
2569
2570 if (field.dataset.props) {
2571 props = fieldsPropsArray[field.dataset.props];
2572 } //Раз у нас в шаблоне есть скрытый загрузчик файлов - надо повесить на него обсервер, если ещё не повесили
2573
2574
2575 if (!this._fileUploader) {
2576 this._fileUploader = {};
2577 }
2578
2579 if (!this._fileUploader.observer) {
2580 this._fileUploader.uploadedFilesDivs = [];
2581 this._fileUploader.observer = new MutationObserver(function (mutations) {
2582 var filesDivs = fuFiles.querySelectorAll('.field_file__files__file');
2583 var uploadingFiles = {}; //Пробегаемся по всем дивам с файлами и загоняем имена в объект
2584
2585 var nameOfLastFile;
2586
2587 var _iterator26 = _createForOfIteratorHelper(filesDivs),
2588 _step26;
2589
2590 try {
2591 for (_iterator26.s(); !(_step26 = _iterator26.n()).done;) {
2592 var fDiv = _step26.value;
2593 var fileName = fDiv.querySelector('.file__name').innerHTML;
2594 var fileDeleteButt = fDiv.querySelector('.file__delete');
2595 var attachmentId = fileDeleteButt.dataset.attachmentId;
2596
2597 if (attachmentId) {
2598 uploadingFiles[attachmentId] = {
2599 fileName: fileName
2600 };
2601
2602 if (_this5.getStorageItem('__uploadingFiles')[attachmentId]) {
2603 if (_this5.getStorageItem('__uploadingFiles')[attachmentId].fileLine) {
2604 uploadingFiles[attachmentId].fileLine = _this5.getStorageItem('__uploadingFiles')[attachmentId].fileLine;
2605 }
2606
2607 if (_this5.getStorageItem('__uploadingFiles')[attachmentId].fileField) {
2608 uploadingFiles[attachmentId].fileField = _this5.getStorageItem('__uploadingFiles')[attachmentId].fileField;
2609 }
2610
2611 if (_this5.getStorageItem('__uploadingFiles')[attachmentId].fieldName) {
2612 uploadingFiles[attachmentId].fieldName = _this5.getStorageItem('__uploadingFiles')[attachmentId].fieldName;
2613 }
2614 }
2615 }
2616
2617 nameOfLastFile = fileName;
2618 } //Если добавили файл
2619
2620 } catch (err) {
2621 _iterator26.e(err);
2622 } finally {
2623 _iterator26.f();
2624 }
2625
2626 if (filesDivs.length > _this5._fileUploader.uploadedFilesDivs.length) {
2627 if (_this5._fileUploader.someFileUploading) {
2628 _this5._fileUploader.someFileUploading(nameOfLastFile);
2629 }
2630 } else //Если удалили файл
2631 if (filesDivs.length < _this5._fileUploader.uploadedFilesDivs.length) {
2632 //Сначала надо определить, какой именно
2633 var deletedFileName;
2634
2635 for (var f in uploadingFiles) {
2636 var weGotIt = void 0;
2637
2638 for (var _f in _this5.getStorageItem('__uploadingFiles')) {
2639 if (f === _f) {
2640 weGotIt = true;
2641 }
2642 }
2643
2644 if (!weGotIt) {
2645 if (_this5._fileUploader.someFileRemoved) {
2646 _this5._fileUploader.someFileRemoved(f);
2647 }
2648
2649 break;
2650 }
2651 }
2652 } else //Если количество не изменилось - значит какой-то файл закончил загружаться
2653 {
2654 if (_this5._fileUploader.uploadComplete) {
2655 _this5._fileUploader.uploadComplete(filesDivs);
2656 }
2657 }
2658
2659 _this5.setStorageItem('__uploadingFiles', uploadingFiles);
2660
2661 _this5._fileUploader.uploadedFilesDivs = filesDivs;
2662 });
2663
2664 this._fileUploader.observer.observe(fuFiles, {
2665 attributes: false,
2666 childList: true,
2667 characterData: false
2668 });
2669 } //Добавляет к полю загрузчик файла
2670
2671
2672 field.addUploader = function () {
2673 function openFileDialog() {
2674 fileUploader.querySelector('.field_file__upload').click();
2675 }
2676
2677 var waitTillTheFileFinishUploading = function waitTillTheFileFinishUploading() {
2678 return new Promise(function (resolve, reject) {
2679 _this5._fileUploader.uploadComplete = function (filesDivs) {
2680 resolve(filesDivs);
2681 };
2682 });
2683 };
2684
2685 var fuButtons = {
2686 type: "actionButtonGroup",
2687 flags: ["unfocusable"],
2688 buttons: [{
2689 iconClass: "uploadButtIcon",
2690 caption: "Загрузить",
2691 onClick: function onClick() {
2692 var _this6 = this;
2693
2694 if (this.isReadOnly || this.interfaceBlocked) {
2695 return;
2696 }
2697
2698 if (this._fileUploader.someFileUploadingNow) {
2699 return;
2700 }
2701
2702 openFileDialog(); //Переопределяем функцию, которая будет вызвана после появления нового файла в загрузчике
2703
2704 this._fileUploader.someFileUploading = function (fileName) {
2705 if ( //Проверка расширения файла
2706 props && props.extensions && !new RegExp("\\.(".concat(function () {
2707 var s = '';
2708
2709 var _iterator27 = _createForOfIteratorHelper(props.extensions),
2710 _step27;
2711
2712 try {
2713 for (_iterator27.s(); !(_step27 = _iterator27.n()).done;) {
2714 var e = _step27.value;
2715 s += '|' + e;
2716 }
2717 } catch (err) {
2718 _iterator27.e(err);
2719 } finally {
2720 _iterator27.f();
2721 }
2722
2723 return s.substr(1);
2724 }(), ")$"), 'ig').test(fileName) || //Проверка имени файла
2725 function () {
2726 var uf = _this6.getStorageItem('__uploadingFiles');
2727
2728 for (var attachmentId in uf) {
2729 if (uf[attachmentId].fileName === fileName) {
2730 _this6.windows._fileWithSameNameAlreadyUploadedMessageWindow.open();
2731
2732 return true;
2733 }
2734 }
2735 }()) {
2736 //Отменяем загрузку файла
2737 var thisFileDeleteButt = fileUploader.querySelector('.field_file__files__file_processing .file__delete');
2738 thisFileDeleteButt.click();
2739 return;
2740 }
2741
2742 _this6._fileUploader.someFileUploadingNow = true;
2743 var fileLine = _this6._fileUploader.uploadingFileLine = field.addFileLine(fileName);
2744 waitTillTheFileFinishUploading().then(function (filesDivs) {
2745 _this6._fileUploader.uploadingFileLine.uploadingComplete(filesDivs);
2746 });
2747 };
2748 }
2749 }]
2750 };
2751 task.getJSON([fuButtons]);
2752 var fUploader = document.createElement('div');
2753 fUploader.className = "fUploader";
2754 fUploader.innerHTML = "\n <div class=\"fuButtons\">".concat(handleFieldAndGetHTML(fuButtons), "</div>\n ");
2755 fTitle.appendChild(fUploader);
2756 task.initFieldsHere(fUploader);
2757 };
2758
2759 field.addUploader();
2760
2761 field.addFileLine = function (fileName, attachmentId) {
2762 var flButtons = {
2763 type: "actionButtonGroup",
2764 flags: ["unfocusable"],
2765 buttons: [{
2766 iconClass: "thrashButtIcon",
2767 caption: "Удалить",
2768 onClick: function onClick() {
2769 if (this.isReadOnly || this.interfaceBlocked) {
2770 return;
2771 }
2772
2773 fileLine.remove();
2774 }
2775 }]
2776 };
2777 task.getJSON([flButtons]);
2778 var fileLine = document.createElement('div');
2779
2780 fileLine.remove = function () {
2781 //Перебираем все файлы в загрузчке и удаляем нужный
2782 var _iterator28 = _createForOfIteratorHelper(_this5._fileUploader.uploadedFilesDivs),
2783 _step28;
2784
2785 try {
2786 for (_iterator28.s(); !(_step28 = _iterator28.n()).done;) {
2787 var fDiv = _step28.value;
2788 var thisFileName = fDiv.querySelector('.file__name').innerHTML;
2789 var thisFileDeleteButt = fDiv.querySelector('.file__delete');
2790 var thisFileAttachmentId = thisFileDeleteButt.dataset.attachmentId;
2791
2792 if (thisFileName === fileName && (!thisFileAttachmentId || attachmentId === thisFileAttachmentId)) {
2793 thisFileDeleteButt.click();
2794 break;
2795 }
2796 }
2797 } catch (err) {
2798 _iterator28.e(err);
2799 } finally {
2800 _iterator28.f();
2801 }
2802
2803 fileObj.fileLine.classList.remove('uploading');
2804 fContainer.classList.remove('somethingIsUploading');
2805 _this5._fileUploader.someFileUploadingNow = false; //Удаляем строчку ШВшного загрузчика с данным файлом
2806
2807 fContainer.removeChild(fileObj.fileLine);
2808
2809 _this5.turnOffErrorClassOnParentElementTagBlock(field);
2810
2811 field.validate({}, true);
2812 };
2813
2814 fileLine.className = "fcFileLine";
2815
2816 if (!attachmentId) {
2817 fileLine.className += " uploading";
2818 }
2819
2820 fileLine.innerHTML = "\n <div class=\"fcFileButtons\">".concat(handleFieldAndGetHTML(flButtons), "</div>\n <div class=\"fcFileName\">").concat(fileName, "</div>\n ");
2821 var fileObj = {
2822 fileName: fileName,
2823 fileLine: fileLine,
2824 fileField: field,
2825 fieldName: fieldName
2826 };
2827 fContainer.appendChild(fileLine);
2828 task.initFieldsHere(fileLine);
2829
2830 if (!attachmentId) {
2831 fileLine.uploadingComplete = function (filesDivs) {
2832 fileLine.classList.remove('uploading');
2833 fContainer.classList.remove('somethingIsUploading');
2834 _this5._fileUploader.someFileUploadingNow = false; //Получаем и прописываем attachmentId загруженного файла
2835
2836 if (filesDivs) {
2837 var _iterator29 = _createForOfIteratorHelper(filesDivs),
2838 _step29;
2839
2840 try {
2841 for (_iterator29.s(); !(_step29 = _iterator29.n()).done;) {
2842 var fDiv = _step29.value;
2843 var fName = fDiv.querySelector('.file__name').innerHTML;
2844
2845 if (fName !== fileName) {
2846 continue;
2847 }
2848
2849 var fileDeleteButt = fDiv.querySelector('.file__delete');
2850 attachmentId = fileDeleteButt.dataset.attachmentId;
2851 break;
2852 }
2853 } catch (err) {
2854 _iterator29.e(err);
2855 } finally {
2856 _iterator29.f();
2857 }
2858
2859 fileLine.dataset.attachmentId = attachmentId;
2860 _this5.getStorageItem('__uploadingFiles')[attachmentId] = fileObj;
2861
2862 _this5.turnOffErrorClassOnParentElementTagBlock(field);
2863
2864 field.validate({}, true);
2865
2866 _this5.checksAfterValidation(field);
2867
2868 _this5.onSolutionChange();
2869 }
2870 };
2871 } else {
2872 fileLine.dataset.attachmentId = attachmentId;
2873 }
2874
2875 fContainer.classList.add('somethingIsUploading');
2876 return fileLine;
2877 };
2878
2879 field.clear = function () {
2880 fContainer.innerHTML = '';
2881 };
2882
2883 field.validate = function (errors, beQuiet) {
2884 var fileLines = field.querySelectorAll('.fcFileLine');
2885 var minFiles = props && props.min ? parseInt(props.min) : false;
2886 var maxFiles = props && props.max ? parseInt(props.max) : false; //Валидация
2887
2888 var validationPassed = true;
2889
2890 if (!beQuiet && !fileLines.length && !field.classList.contains('optional')) {
2891 if (!minFiles && !maxFiles) {
2892 validationPassed = false;
2893 errors = task.addErrorToField(field, 'Загрузите, пожалуйста, хотя бы один файл!', errors);
2894 } else {
2895 validationPassed = false;
2896 errors = task.addErrorToField(field, "\u0417\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u0435, \u043F\u043E\u0436\u0430\u043B\u0443\u0439\u0441\u0442\u0430, \u0444\u0430\u0439\u043B\u044B! (".concat(minFiles ? 'от ' + minFiles : '').concat(maxFiles ? (minFiles ? ' ' : '') + 'до ' + maxFiles : '', ")"), errors);
2897 }
2898 } else if (minFiles && !beQuiet && fileLines.length < minFiles) {
2899 validationPassed = false;
2900 errors = task.addErrorToField(field, "\u041C\u0438\u043D\u0438\u043C\u0430\u043B\u044C\u043D\u043E\u0435 \u043A\u043E\u043B\u0438\u0447\u0435\u0441\u0442\u0432\u043E \u0444\u0430\u0439\u043B\u043E\u0432: ".concat(minFiles, "!"), errors);
2901 } else if (maxFiles && !beQuiet && fileLines.length > maxFiles) {
2902 validationPassed = false;
2903 errors = task.addErrorToField(field, "\u041C\u0430\u043A\u0441\u0438\u043C\u0430\u043B\u044C\u043D\u043E\u0435 \u043A\u043E\u043B\u0438\u0447\u0435\u0441\u0442\u0432\u043E \u0444\u0430\u0439\u043B\u043E\u0432: ".concat(maxFiles, "!"), errors);
2904 }
2905
2906 field.classList.remove('validationRejected');
2907 field.classList.remove('validationPassed');
2908 field.classList.remove('clear');
2909 field.classList.remove('skipMe');
2910
2911 if (fileLines.length > 0) {
2912 if (validationPassed) {
2913 field.classList.add('validationPassed');
2914 } else {
2915 field.classList.add('validationRejected');
2916 }
2917 } else {
2918 field.classList.add('clear');
2919
2920 if (field.classList.contains('optional')) {
2921 field.classList.add('skipMe');
2922 }
2923 }
2924
2925 return errors;
2926 }; //Если поле не виртуальное - отображаем все его загруженные файлы
2927
2928
2929 if (!field.classList.contains('virtual') && (!field.closest('.groupContainer ') || !isGroupInfinite(field.closest('.groupContainer')))) {
2930 var fileDeleteButtons = fuFiles.querySelectorAll('.field_file__files__file .file__delete');
2931
2932 if (fileDeleteButtons && fileDeleteButtons.length > 0) {
2933 this._fileUploader.uploadedFilesDivs = fuFiles.querySelectorAll('.field_file__files__file'); //Отрисовываем в контроле загруженные в нём файлы, которые есть в скрытом загрузчике
2934
2935 var _iterator30 = _createForOfIteratorHelper(fileDeleteButtons),
2936 _step30;
2937
2938 try {
2939 for (_iterator30.s(); !(_step30 = _iterator30.n()).done;) {
2940 var fileDeleteButt = _step30.value;
2941 var attachmentId = fileDeleteButt.dataset.attachmentId;
2942 var item = this.getStorageItem('__uploadingFiles')[attachmentId];
2943
2944 if (item) {
2945 item.fileName = fileDeleteButt.parentElement.querySelector('.file__name').innerHTML;
2946
2947 if (item.fieldName === fieldName) {
2948 item.fileField = field;
2949 item.fileLine = field.addFileLine(item.fileName, attachmentId);
2950 }
2951 }
2952 }
2953 } catch (err) {
2954 _iterator30.e(err);
2955 } finally {
2956 _iterator30.f();
2957 }
2958 }
2959
2960 field.validate({}, true);
2961 }
2962
2963 field.validate({}, true);
2964 field.classList.add('initialized');
2965 },
2966 //Очищает список загруженных файлов (не удаляя их) у ШВшного поля загрузки файлов
2967 clearFileField: function clearFileField(name) {
2968 var field = this.getDOMElement().querySelector('.element.fileField.fieldName_' + name);
2969
2970 if (!field) {
2971 ce('Не найдено поле загрузки файлов для его очистки', name);
2972 return;
2973 }
2974
2975 field.clear();
2976 },
2977 //Открывает окно жалобщика
2978 submitComplaint: function submitComplaint() {
2979 if (!useComplainter) {
2980 return;
2981 }
2982
2983 var assignmentInfo = this.getAssignment().getOptions().assignment;
2984 window.Complaints.default.open({
2985 inputValues: this.getTask().input_values,
2986 assignment: {
2987 assignmentId: assignmentInfo.id,
2988 poolId: assignmentInfo.poolId,
2989 taskSuiteId: assignmentInfo.taskSuiteId
2990 }
2991 }, this.getAssignment().getId());
2992 },
2993 //Заново рендерит указанный контрол с новой спекой и сохранением солюшки
2994 replaceField: function replaceField(name, json) {
2995 var root = this.getDOMElement(),
2996 field = root.querySelector('.element.' + name);
2997
2998 if (!field) {
2999 return;
3000 }
3001
3002 this.loadSolution(this.theOneSolution);
3003 },
3004 //Записывает в солюшку все поля переданного объекта
3005 writeObjectToSolution: function writeObjectToSolution(object, postfix, solution) {
3006 if (!solution) {
3007 solution = this.theOneSolution || {};
3008 }
3009
3010 if (!object) {
3011 return solution;
3012 }
3013
3014 for (var item in object) {
3015 solution[item + postfix] = object[item];
3016 }
3017
3018 return solution;
3019 },
3020 //Заново отрисовывает указанный контрол с сохранением солюшки
3021 redrawField: function redrawField(name, group) {
3022 var root = this.getDOMElement(),
3023 field = root.querySelector('.element.' + name);
3024
3025 if (!field) {
3026 return;
3027 }
3028
3029 var fieldParent = field.parentElement;
3030 var fieldWasFocused = field.classList.contains('focused');
3031 var specField = this.getSpecField(name, group);
3032
3033 if (!specField) {
3034 return;
3035 }
3036
3037 field.outerHTML = handleFieldAndGetHTML.apply(this, [specField]);
3038 this.initFieldsHere(fieldParent);
3039 this.loadSolution(this.theOneSolution, fieldParent);
3040
3041 if (fieldWasFocused) {
3042 var newField = root.querySelector('.element.' + name);
3043 newField.click();
3044 }
3045 },
3046 redrawFields: function redrawFields(fieldsName) {
3047 var root = this.getDOMElement(),
3048 container = root.querySelector('.theOneFieldsContainer.fieldsName_' + fieldsName),
3049 spec = this.theOneSpec.spec[fieldsName];
3050
3051 if (!container) {
3052 ce("В redrawFields передано несуществующее имя theOneFieldsContainer'a", fieldsName);
3053 return;
3054 }
3055
3056 if (!spec) {
3057 ce("В redrawFields передано несуществующее имя спеки theOneSpec", fieldsName);
3058 return;
3059 }
3060
3061 var _iterator31 = _createForOfIteratorHelper(root.querySelectorAll('.theOneFieldsContainer.fieldsName_' + fieldsName)),
3062 _step31;
3063
3064 try {
3065 for (_iterator31.s(); !(_step31 = _iterator31.n()).done;) {
3066 var _container = _step31.value;
3067 var p = _container.parentElement;
3068 _container.outerHTML = getFieldsHTML.apply(this, [fieldsName]);
3069 _container = p.querySelector('.theOneFieldsContainer');
3070 this.initFieldsHere(_container);
3071 this.loadSolution(this.theOneSolution, _container);
3072 }
3073 } catch (err) {
3074 _iterator31.e(err);
3075 } finally {
3076 _iterator31.f();
3077 }
3078 },
3079 //Возвращает ссылку на конкретное поле в спеке Всевластия
3080 getSpecField: function getSpecField(name, group) {
3081 if (!this.theOneSpec.specByName[name]) {
3082 return;
3083 }
3084
3085 var field = group ? this.theOneSpec.specByName[name].getElementByName(group).groupFields.getElementByName(name) : this.theOneSpec.specByName[name].getElementByName(name);
3086 return field;
3087 },
3088 //Устанавливает/заменяет массив полей в спеке Всевластия
3089 setSpecFields: function setSpecFields(fieldsName, value) {
3090 this.theOneSpec.spec[fieldsName] = this.getJSON(value);
3091 this.getTask().input_values[fieldsName] = this.theOneSpec.spec[fieldsName];
3092 },
3093 includeGroup: function includeGroup(name) {
3094 var root = this.getDOMElement(),
3095 groupDiv = root.querySelector('.groupContainer.fieldName_' + name);
3096
3097 if (!groupDiv) {
3098 return;
3099 }
3100
3101 groupDiv.classList.remove('excluded');
3102 },
3103 excludeGroup: function excludeGroup(name) {
3104 var root = this.getDOMElement(),
3105 groupDiv = root.querySelector('.groupContainer.fieldName_' + name);
3106
3107 if (!groupDiv) {
3108 return;
3109 }
3110
3111 groupDiv.classList.add('excluded');
3112 },
3113 focusNextTask: function focusNextTask() {
3114 this.getAssignment().getTaskSuite().focusNextTask();
3115 },
3116 focusPreviousTask: function focusPreviousTask() {
3117 this.getAssignment().getTaskSuite().focusPreviousTask();
3118 },
3119
3120 /* Работа с видами (view) */
3121 //Прячет указанный view
3122 hideView: function hideView(name) {
3123 var root = this.getDOMElement(),
3124 view = root.querySelector('.view.' + name);
3125
3126 if (!view) {
3127 return;
3128 }
3129
3130 view.classList.add('hidden');
3131 },
3132 //Показывает указанный view
3133 showView: function showView(name) {
3134 var root = this.getDOMElement(),
3135 view = root.querySelector('.view.' + name);
3136
3137 if (!view) {
3138 return;
3139 }
3140
3141 view.classList.remove('hidden'); //Обновлянем все просмотрщики с канвасами
3142
3143 var _iterator32 = _createForOfIteratorHelper(view.querySelectorAll('.imageField .imageBlock canvas')),
3144 _step32;
3145
3146 try {
3147 for (_iterator32.s(); !(_step32 = _iterator32.n()).done;) {
3148 var c = _step32.value;
3149
3150 if (c.initCanvas) {
3151 c.initCanvas();
3152 }
3153 }
3154 } catch (err) {
3155 _iterator32.e(err);
3156 } finally {
3157 _iterator32.f();
3158 }
3159 },
3160 //Исключает из сбора солюшки указанный view
3161 excludeView: function excludeView(name) {
3162 var root = this.getDOMElement(),
3163 view = root.querySelector('.view.' + name);
3164
3165 if (!view) {
3166 return;
3167 }
3168
3169 view.classList.add('excluded');
3170 },
3171 //Включает в сбор солюшки указанный view
3172 includeView: function includeView(name) {
3173 var root = this.getDOMElement(),
3174 view = root.querySelector('.view.' + name);
3175
3176 if (!view) {
3177 return;
3178 }
3179
3180 view.classList.remove('excluded');
3181 },
3182 //Прячет все view
3183 hideAllViews: function hideAllViews(className) {
3184 var root = this.getDOMElement();
3185
3186 if (!className) {
3187 className = 'view';
3188 }
3189
3190 var _iterator33 = _createForOfIteratorHelper(root.querySelectorAll('.view.' + className)),
3191 _step33;
3192
3193 try {
3194 for (_iterator33.s(); !(_step33 = _iterator33.n()).done;) {
3195 var view = _step33.value;
3196 view.classList.add('hidden');
3197 }
3198 } catch (err) {
3199 _iterator33.e(err);
3200 } finally {
3201 _iterator33.f();
3202 }
3203 },
3204 //Выключает сбор солюшки во всех view
3205 excludeAllViews: function excludeAllViews(className) {
3206 var root = this.getDOMElement();
3207
3208 if (!className) {
3209 className = 'view';
3210 }
3211
3212 var _iterator34 = _createForOfIteratorHelper(root.querySelectorAll('.view.' + className)),
3213 _step34;
3214
3215 try {
3216 for (_iterator34.s(); !(_step34 = _iterator34.n()).done;) {
3217 var view = _step34.value;
3218 view.classList.add('excluded');
3219 }
3220 } catch (err) {
3221 _iterator34.e(err);
3222 } finally {
3223 _iterator34.f();
3224 }
3225 },
3226 //Показывает все view
3227 showAllViews: function showAllViews(className) {
3228 var root = this.getDOMElement();
3229
3230 if (!className) {
3231 className = 'view';
3232 }
3233
3234 var _iterator35 = _createForOfIteratorHelper(root.querySelectorAll('.view.' + className)),
3235 _step35;
3236
3237 try {
3238 for (_iterator35.s(); !(_step35 = _iterator35.n()).done;) {
3239 var view = _step35.value;
3240 view.classList.remove('hidden'); //Обновлянем все просмотрщики с канвасами
3241
3242 var _iterator36 = _createForOfIteratorHelper(view.querySelectorAll('.imageField .imageBlock canvas')),
3243 _step36;
3244
3245 try {
3246 for (_iterator36.s(); !(_step36 = _iterator36.n()).done;) {
3247 var c = _step36.value;
3248
3249 if (c.initCanvas) {
3250 c.initCanvas();
3251 }
3252 }
3253 } catch (err) {
3254 _iterator36.e(err);
3255 } finally {
3256 _iterator36.f();
3257 }
3258 }
3259 } catch (err) {
3260 _iterator35.e(err);
3261 } finally {
3262 _iterator35.f();
3263 }
3264 },
3265 //Включает сбор солюшки во всех view
3266 includeAllViews: function includeAllViews(className) {
3267 var root = this.getDOMElement();
3268
3269 if (!className) {
3270 className = 'view';
3271 }
3272
3273 var _iterator37 = _createForOfIteratorHelper(root.querySelectorAll('.view.' + className)),
3274 _step37;
3275
3276 try {
3277 for (_iterator37.s(); !(_step37 = _iterator37.n()).done;) {
3278 var view = _step37.value;
3279 view.classList.remove('excluded');
3280 }
3281 } catch (err) {
3282 _iterator37.e(err);
3283 } finally {
3284 _iterator37.f();
3285 }
3286 },
3287 //Показывает только указыннй view (остальные прячет)
3288 showOnlyThisView: function showOnlyThisView(name, className) {
3289 var root = this.getDOMElement();
3290 this.hideAllViews(className);
3291 this.showView(name);
3292 },
3293 //Включает сбор солюшки только в указанном view
3294 includeOnlyThisView: function includeOnlyThisView(name, className) {
3295 var root = this.getDOMElement();
3296 this.excludeAllViews(className);
3297 this.includeView(name);
3298 },
3299 //Компилирует HTML+Handlebars в контексте текущего таска
3300 compileHandlebars: function compileHandlebars(code, optionalContext) {
3301 var context = optionalContext || this.handlebarsContext || this.getTask().input_values;
3302 return Handlebars.compile(code)(context);
3303 },
3304 //Сохраняет данные в ШВ-шное локальное хранилище
3305 setStorageItem: function setStorageItem(key, value) {
3306 this.theOneSolution.__theOneCustomStorage[key] = value;
3307 this.theOneSolutionUpdated = true;
3308 },
3309 //Получает данные из ШВ-шного локального хранилища
3310 getStorageItem: function getStorageItem(key) {
3311 if (this.theOneSolution.__theOneCustomStorage && this.theOneSolution.__theOneCustomStorage[key]) {
3312 return this.theOneSolution.__theOneCustomStorage[key];
3313 }
3314 },
3315 //Раньше в шаблоне использовался иконочный шрифт material icons. Функция ищет все старые и новые иконки в коде и меняет их на свгшки.
3316 insertIcons: function insertIcons(where) {
3317 var root = where ? where : this.getDOMElement(),
3318 c = 0;
3319 Array.from(root.querySelectorAll('i.material-icons:not(.processed)')).forEach(function (i) {
3320 c++;
3321 i.dataset.icon = i.innerHTML;
3322 i.innerHTML = '';
3323 i.classList.add('ffIcon');
3324 });
3325 Array.from(root.querySelectorAll('.ffIcon:not(.processed)')).forEach(function (i) {
3326 var icon = svgIcons[i.dataset.icon];
3327
3328 if (!icon) {
3329 return;
3330 }
3331
3332 var html = '<svg focusable="false" xmlns="http://www.w3.org/2000/svg" ' + icon.attributes + '>';
3333
3334 if (icon.translucentPath) {
3335 html += '<path fill="currentColor" d="' + icon.translucentPath + '" opacity="0.4"></path>';
3336 }
3337
3338 if (icon.path) {
3339 html += '<path fill="currentColor" d="' + icon.path + '"></path>';
3340 }
3341
3342 i.innerHTML = html + '</svg>';
3343 i.classList.add('processed');
3344 });
3345 },
3346
3347 /* Логирование действий */
3348 getTimeStamp: function getTimeStamp() {
3349 var date = new Date(),
3350 curTime = date.getTime(),
3351 ts = this.getStorageItem('__timingsStorage');
3352 var timestamp = ts.total + (curTime - ts.focused);
3353 return timestamp;
3354 },
3355 //Обеспечивает задержку в логировании, потому что если кликнуть по элементу несфокусированного таска, то сначала сработает события клика по элементу, а только потом сфокусируется таск.
3356 logAction: function logAction(action, additional) {
3357 var _this7 = this;
3358
3359 if (!actionLogger || !this.rendered) {
3360 return;
3361 }
3362
3363 setTimeout(function () {
3364 _this7.logActionNow(action, additional);
3365 }, 60);
3366 },
3367 logActionNow: function logActionNow(action, additional) {
3368 var logObj = {
3369 time: this.getTimeStamp(),
3370 action: action
3371 },
3372 ts = this.getStorageItem('__timingsStorage');
3373
3374 if (additional) {
3375 logObj.additional = additional;
3376 }
3377
3378 this.getStorageItem('__actionLog').push(logObj);
3379 },
3380 onFocus: function onFocus() {
3381 if (!actionLogger || !this.rendered) {
3382 return;
3383 }
3384
3385 var date = new Date(),
3386 curTime = date.getTime(),
3387 ts = this.getStorageItem('__timingsStorage');
3388
3389 if (!ts.started) {
3390 ts.started = curTime;
3391 }
3392
3393 ts.focused = curTime;
3394 this.logActionNow('focus');
3395 },
3396 onBlur: function onBlur() {
3397 if (!actionLogger || !this.rendered) {
3398 return;
3399 }
3400
3401 var date = new Date(),
3402 curTime = date.getTime(),
3403 ts = this.getStorageItem('__timingsStorage');
3404
3405 if (!ts.focused) {
3406 return;
3407 }
3408
3409 ts.blurred = curTime;
3410 this.logActionNow('blur');
3411 ts.total += curTime - ts.focused;
3412 }
3413};
3414/*
3415*
3416* Методы класса TaskSuite
3417*
3418*/
3419
3420window.theOneTaskSuitePrototype = {
3421 onRender: function onRender() {
3422 var root = this.getDOMElement(),
3423 _this = this;
3424
3425 this.initSuite();
3426 document.ready.then(function () {//Кастомные штуки
3427 }).catch(function (err) {
3428 return console.error(err);
3429 }); //Кастомные штуки
3430 },
3431 //Инициализирует интерфейс Шаблона Всевластия
3432 initSuite: function initSuite() {
3433 var root = this.getDOMElement(),
3434 _this = this,
3435 tasks = root.querySelectorAll('.task'),
3436 isMobile = window.thisIsMobileDevice || window.screen.width <= 768;
3437
3438 this.tasks = tasks;
3439 window.readyToKill = false;
3440 window.curTask = 4815162342;
3441 cssNoClassSettings.forEach(function (settingName) {
3442 if (!eval(settingName)) {
3443 root.classList.add('no_' + settingName);
3444 }
3445 });
3446 cssClassSettings.forEach(function (settingName) {
3447 if (eval(settingName)) {
3448 root.classList.add(settingName);
3449 }
3450 }); //Установка флагов режима только для чтения и просмотра выполненных заданий
3451
3452 if (this.getWorkspaceOptions().isReadOnly) {
3453 this.isReadOnly = true;
3454 }
3455
3456 if (this.getWorkspaceOptions().isReviewMode) {
3457 this.isReviewMode = true;
3458 } //Инициализация поиска по глоссарию
3459
3460
3461 if (useGlossarySearch) {
3462 $.when($.getScript(jQueryUILibSrc), $.getScript(glossarySearchLib), $.Deferred(function (deferred) {
3463 $(deferred.resolve);
3464 })).done(function () {
3465 $('<link/>', {
3466 rel: 'stylesheet',
3467 type: 'text/css',
3468 href: jQueryUICssSrc
3469 }).appendTo('head');
3470
3471 var _iterator38 = _createForOfIteratorHelper(tasks),
3472 _step38;
3473
3474 try {
3475 for (_iterator38.s(); !(_step38 = _iterator38.n()).done;) {
3476 var task = _step38.value;
3477 $(task.querySelector('.btnGlossarySearch .glossarySearchForm')).glossSearch('add', {
3478 source: 'ajax_to_S3',
3479 url: glossarySearchSource
3480 });
3481 task.querySelector('.btnGlossarySearch a').addEventListener('click', function () {
3482 this.parentElement.classList.toggle('active');
3483 });
3484 }
3485 } catch (err) {
3486 _iterator38.e(err);
3487 } finally {
3488 _iterator38.f();
3489 }
3490 });
3491 } //Инициализация Жалобщика
3492
3493
3494 if (useComplainter) {
3495 var _iterator39 = _createForOfIteratorHelper(tasks),
3496 _step39;
3497
3498 try {
3499 var _loop4 = function _loop4() {
3500 var task = _step39.value;
3501 task.querySelector('.btnComplainter a').addEventListener('click', function () {
3502 _this.getTasks()[task.dataset.number].submitComplaint();
3503 });
3504 };
3505
3506 for (_iterator39.s(); !(_step39 = _iterator39.n()).done;) {
3507 _loop4();
3508 }
3509 } catch (err) {
3510 _iterator39.e(err);
3511 } finally {
3512 _iterator39.f();
3513 }
3514 } //Формируем tabList у переключалки тасков
3515 //Формируем HTML для выпадашек - таскопереключалок
3516
3517
3518 var tabListHTML = '',
3519 i = 0;
3520 Array.from(tasks).forEach(function (task) {
3521 if (!task.querySelector('.tabList')) {
3522 return;
3523 } //Это чтобы таски не перепутались, если где-то отсутствует таблист
3524
3525
3526 i++;
3527 var name = task.querySelector('.taskName');
3528 name = name ? name.innerHTML : 'Задание ' + i;
3529 tabListHTML += ' <div class="tabListItem" data-number="' + (i - 1) + '">' + '<div class="tabListItemNumber">' + i + '.</div>' + '<div class="tabListItemName">' + name + '</div>' + '</div>';
3530 }); //И прописываем его в переключалки тасков
3531
3532 i = 0;
3533 Array.from(tasks).forEach(function (task) {
3534 var tabList = task.querySelector('.tabList');
3535
3536 if (tabList) {
3537 tabList.innerHTML = '<div>' + tabListHTML + '</div>'; //Обрабатываем элементы выпадашки
3538
3539 var j = 0;
3540 Array.from(tabList.querySelectorAll('.tabListItem')).forEach(function (tlItem) {
3541 var curItemNumber = j;
3542
3543 if (i === j) {
3544 tlItem.classList.add('selected');
3545 } else tlItem.addEventListener('click', function (e) {
3546 e.preventDefault();
3547 e.stopPropagation();
3548 var n = parseInt(this.dataset.number);
3549
3550 if (!verticalSuite) {
3551 window.scrollingNow = true;
3552 root.style.left = -100 * n + 'vw';
3553 setTimeout(function () {
3554 _this.focusTask(n, false);
3555 }, 50);
3556 } else {
3557 window.scrollingNow = true;
3558 setTimeout(function () {
3559 _this.focusTask(n, true);
3560
3561 $(root).animate({
3562 scrollTop: tasks[n].offsetTop
3563 }, 200);
3564 }, 50);
3565 }
3566 });
3567
3568 j++;
3569 }); //После отрисовки страницы прописываем таблисту правильную высоту при ховере, чтобы анимация работала максимально красиво
3570
3571 document.ready.then(function () {
3572 setTimeout(function () {
3573 document.styleSheets[0].insertRule('.tabScroller:hover .tabList {max-height: ' + tabList.offsetHeight + 'px;}');
3574 document.styleSheets[0].insertRule('.tabList {max-height: 0;}');
3575 }, 0);
3576 }).catch(function (err) {
3577 return console.error(err);
3578 });
3579 i++;
3580 }
3581 }); //Переключение между тасками свайпом
3582
3583 if (isMobile) {
3584 Array.from(tasks).forEach(function (task) {
3585 var swipe = {};
3586
3587 function mbTouchStart(e) {
3588 swipe.startX = e.touches[0].clientX;
3589 swipe.startY = e.touches[0].clientY;
3590 }
3591
3592 function mbTouchEnd(e) {
3593 swipe.endX = e.changedTouches[0].clientX;
3594 swipe.endY = e.changedTouches[0].clientY;
3595
3596 if (!swipe.startX || !swipe.startY || !swipe.endX || !swipe.endY) {
3597 return;
3598 }
3599
3600 swipe.diffX = Math.abs(swipe.startX - swipe.endX);
3601 swipe.diffY = Math.abs(swipe.startY - swipe.endY);
3602
3603 if (swipe.diffX > window.screen.width / 2.5 && swipe.diffX > swipe.diffY * 2.5) {
3604 //Надо бы свайпнуть
3605 if (swipe.startX < swipe.endX) {
3606 task.querySelector('.leftScroll').click();
3607 } else {
3608 task.querySelector('.rightScroll').click();
3609 }
3610 }
3611 }
3612
3613 task.addEventListener('touchstart', mbTouchStart, false);
3614 task.addEventListener('touchend', mbTouchEnd, false);
3615 });
3616 }
3617
3618 this.initTheOneHotkeys();
3619 document.addEventListener('keydown', function (event) {
3620 if (document.activeElement && document.activeElement.closest('.noHotkeys')) {
3621 return;
3622 }
3623
3624 if (window.onSuiteKeydown) {
3625 if (onSuiteKeydown.apply(_this, [event.code, root, event])) {
3626 return;
3627 }
3628 }
3629
3630 if (_this.focusedElement && _this.focusedElement.onKey) {
3631 _this.focusedElement.onKey(event.code, event);
3632 } //Удерживание кнопки не ставит вердикт, а показывает подсказку (совместно с onKey и onkeyup)
3633
3634
3635 if (!event.repeat) {
3636 _this.keydownTime = Date.now();
3637 var key = event.key.toUpperCase();
3638
3639 if (elementButtonsHotkeysOrderString.includes(key)) {
3640 _this.keyHoldTimeout = setTimeout(function () {
3641 if (_this.focusedElement && _this.focusedElement.buttons && _this.focusedElement.closest('.task_focused')) {
3642 var btn = _this.focusedElement.buttons[elementButtonsHotkeysOrderString.indexOf(key)];
3643
3644 if (!btn) {
3645 return;
3646 }
3647
3648 var popup = btn.parentElement.querySelector('.popUpTitle');
3649
3650 if (!popup) {
3651 return;
3652 }
3653
3654 _this.openedPopup = popup;
3655 popup.parentElement.classList.add('openedByKey');
3656 }
3657 }, timeToOpenHintByKey);
3658 }
3659 }
3660
3661 if (event.code === "Tab") {
3662 event.preventDefault();
3663 }
3664
3665 if (!hotkeysEnabled) {
3666 return;
3667 }
3668
3669 if (event.code === "Escape" && document.activeElement && document.activeElement.closest('.element') && (document.activeElement.tagName === 'INPUT' || document.activeElement.tagName === 'TEXTAREA')) {
3670 document.activeElement.blur();
3671 }
3672 /*if (event.keyCode!=9 && document.activeElement && document.activeElement.closest('.element') && document.activeElement.tagName=='TEXTAREA' && document.activeElement.value.length>0) {
3673 _this.focusElement(parseInt(document.activeElement.closest('.element').dataset.number));
3674 return;
3675 }*/
3676
3677
3678 var tf = root.querySelector('.task_focused');
3679
3680 if (!tf) {
3681 return false;
3682 }
3683
3684 if (event.code === "Tab") {
3685 //TAB
3686 _this.focusNextElement();
3687 }
3688 });
3689 document.addEventListener('keyup', function (event) {
3690 //Удерживание кнопки не ставит вердикт, а показывает подсказку (совместно с onKey и onkeydown)
3691 clearTimeout(_this.keyHoldTimeout);
3692
3693 if (_this.openedPopup) {
3694 _this.openedPopup.parentElement.classList.remove('openedByKey');
3695
3696 _this.openedPopup = false;
3697 }
3698 }); //Проставляем номера и количество тасков в листалках и биндим действия на кнопки
3699
3700 root.style.left = '0';
3701
3702 var _loop5 = function _loop5(_i3) {
3703 tasks[_i3].querySelector('.curTab').innerHTML = _i3 + 1;
3704 tasks[_i3].querySelector('.tabsCount').innerHTML = tasks.length;
3705 tasks[_i3].dataset.number = _i3.toString();
3706 tasks[_i3].querySelector('.leftScroll').i = _i3;
3707 tasks[_i3].querySelector('.rightScroll').i = _i3; //Переключение между тасками
3708
3709 if (!verticalSuite) {
3710 //Горизонтальное расположение тасков в сьюте (с листалкой)
3711 tasks[_i3].querySelector('.leftScroll').addEventListener('click', function () {
3712 if (this.i > 0 && !window.scrollingNow) {
3713 window.scrollingNow = true;
3714 root.style.left = parseInt(root.style.left) + 100 + 'vw';
3715 setTimeout(function () {
3716 _this.focusTask(_i3 - 1, false);
3717 }, 50);
3718 }
3719 });
3720
3721 tasks[_i3].querySelector('.rightScroll').addEventListener('click', function () {
3722 if (this.i < tasks.length - 1 && !window.scrollingNow) {
3723 window.scrollingNow = true;
3724 root.style.left = parseInt(root.style.left) - 100 + 'vw';
3725 setTimeout(function () {
3726 _this.focusTask(_i3 + 1, false);
3727 }, 50);
3728 }
3729 });
3730 } else {
3731 //Традиционное, вертикальное расположение тасков в сьюте
3732 tasks[_i3].querySelector('.leftScroll').addEventListener('click', function () {
3733 if (window.curTask > 0) {
3734 window.scrollingNow = true;
3735 setTimeout(function () {
3736 _this.focusTask(window.curTask - 1, true);
3737 }, 50);
3738 }
3739 });
3740
3741 tasks[_i3].querySelector('.rightScroll').addEventListener('click', function () {
3742 if (window.curTask < tasks.length - 1) {
3743 window.scrollingNow = true;
3744 setTimeout(function () {
3745 _this.focusTask(window.curTask + 1, true);
3746 }, 50);
3747 }
3748 });
3749 } //Сворачивалка/разворачивалка блоков
3750
3751
3752 if (taskMinimizer) {
3753 Array.from(tasks[_i3].querySelectorAll('.block.minimizable .blockTitle:not(.tabScroller):not(.searchField):not(.darkButt)')).forEach(function (btn) {
3754 if (!isMobile) {
3755 btn.addEventListener('click', function () {
3756 if (this.parentElement.classList.contains('minimized')) {
3757 this.parentElement.classList.remove('minimized');
3758 } else {
3759 this.parentElement.classList.add('minimized');
3760 }
3761 });
3762 } else {
3763 btn.closest('.block').classList.remove('minimizable');
3764 }
3765 });
3766 }
3767 };
3768
3769 for (var _i3 = 0; _i3 < tasks.length; _i3++) {
3770 _loop5(_i3);
3771 } //Включаем светлую тему для джедаев
3772
3773
3774 if (this.storage && this.storage.getItem('funfrogLightSide') === false) {
3775 var _iterator40 = _createForOfIteratorHelper(root.querySelectorAll('.task .btnDarkSide a')),
3776 _step40;
3777
3778 try {
3779 for (_iterator40.s(); !(_step40 = _iterator40.n()).done;) {
3780 var switchButton = _step40.value;
3781 switchButton.setAttribute('title', localize('lightSide'));
3782 root.classList.add('darkSide');
3783 }
3784 } catch (err) {
3785 _iterator40.e(err);
3786 } finally {
3787 _iterator40.f();
3788 }
3789 } //Going full Anakin and back
3790
3791
3792 var _iterator41 = _createForOfIteratorHelper(root.querySelectorAll('.task .btnDarkSide a')),
3793 _step41;
3794
3795 try {
3796 for (_iterator41.s(); !(_step41 = _iterator41.n()).done;) {
3797 var _switchButton = _step41.value;
3798
3799 _switchButton.addEventListener("click", function () {
3800 if (root.classList.contains('darkSide')) {
3801 var _iterator46 = _createForOfIteratorHelper(root.querySelectorAll('.task .btnDarkSide a')),
3802 _step46;
3803
3804 try {
3805 for (_iterator46.s(); !(_step46 = _iterator46.n()).done;) {
3806 var _switchButton2 = _step46.value;
3807
3808 _switchButton2.setAttribute('title', localize('darkSide'));
3809 }
3810 } catch (err) {
3811 _iterator46.e(err);
3812 } finally {
3813 _iterator46.f();
3814 }
3815
3816 root.classList.remove('darkSide');
3817
3818 if (_this.storage) {
3819 _this.storage.setItem('funfrogLightSide', 'true', new Date().getTime() + 30000000);
3820 }
3821 } else {
3822 var _iterator47 = _createForOfIteratorHelper(root.querySelectorAll('.task .btnDarkSide a')),
3823 _step47;
3824
3825 try {
3826 for (_iterator47.s(); !(_step47 = _iterator47.n()).done;) {
3827 var _switchButton3 = _step47.value;
3828
3829 _switchButton3.setAttribute('title', localize('lightSide'));
3830 }
3831 } catch (err) {
3832 _iterator47.e(err);
3833 } finally {
3834 _iterator47.f();
3835 }
3836
3837 root.classList.add('darkSide');
3838
3839 if (_this.storage) {
3840 _this.storage.setItem('funfrogLightSide', 'false', new Date().getTime() + 30000000);
3841 }
3842 }
3843 });
3844 } //Пытаемся выпытать у браузера сохранённую солюшку, если она была
3845 //this.getSolutionsFromStorage(); Перенесено в loadStuffFromTheOneLibrary()
3846 //Периодически сохраняем всю солюшку в локальное хранилище, чтобы потом, если что, можно было восстановить
3847
3848 } catch (err) {
3849 _iterator41.e(err);
3850 } finally {
3851 _iterator41.f();
3852 }
3853
3854 if (_this.storage) {
3855 var options = _this.getOptions(),
3856 assId = options.assignment._options.assignment.id,
3857 wOptions = options.workspaceOptions;
3858
3859 if (assId && !wOptions.isReadOnly && !wOptions.isReviewMode) {
3860 setInterval(function () {
3861 var solutions = [];
3862 var solutionsUpdated = false;
3863
3864 _this.getTasks().forEach(function (task) {
3865 if (task.theOneSolutionUpdated) {
3866 solutionsUpdated = true;
3867 task.theOneSolutionUpdated = false;
3868 }
3869
3870 solutions.push(task.theOneSolution);
3871 });
3872
3873 if (!solutionsUpdated) {
3874 return;
3875 }
3876
3877 if (printSolution) {
3878 console.log('Сохранили солюшку:', JSON.parse(JSON.stringify(solutions)));
3879 }
3880
3881 _this.storage.setItem('funfrogSavedSolutions_' + assId, JSON.stringify(solutions));
3882 }, 10000);
3883 }
3884 } //Биндим действия на все helpContainer'ы
3885
3886
3887 var _iterator42 = _createForOfIteratorHelper(root.querySelectorAll('.helpContainer:not(.initialized)')),
3888 _step42;
3889
3890 try {
3891 for (_iterator42.s(); !(_step42 = _iterator42.n()).done;) {
3892 var q = _step42.value;
3893 initHelpContainer(q);
3894 }
3895 } catch (err) {
3896 _iterator42.e(err);
3897 } finally {
3898 _iterator42.f();
3899 }
3900
3901 document.ready.then(function () {
3902 //Обработка миспринтов во всех тасках (если они там есть)
3903 if (misprintsContainers && Array.isArray(misprintsContainers)) {
3904 var _iterator43 = _createForOfIteratorHelper(tasks),
3905 _step43;
3906
3907 try {
3908 for (_iterator43.s(); !(_step43 = _iterator43.n()).done;) {
3909 var task = _step43.value;
3910 var misprintsDiv = task.querySelector('.stringifiedMisprints');
3911
3912 if (!misprintsDiv) {
3913 continue;
3914 }
3915
3916 var misprints = JSON.parse(misprintsDiv.innerHTML);
3917
3918 if (!misprints || !Array.isArray(misprints)) {
3919 continue;
3920 }
3921
3922 var mString = '';
3923
3924 var _iterator44 = _createForOfIteratorHelper(misprints),
3925 _step44;
3926
3927 try {
3928 for (_iterator44.s(); !(_step44 = _iterator44.n()).done;) {
3929 var misprint = _step44.value;
3930 mString += '|(\\s+|^)\\S?' + misprint.replace(/(ёшь|умя|ами|ому|емя|ого|ими|ишь|ёте|ите|еми|ёт|ий|ам|ые|ый|ие|am|ая|ас|ax|ит|ем|её|ей|их|ию|ею|ex|ух|ут|ёх|ум|cm|им|ую|шь|ою|ми|оё|ое|ой|ов|мя|ом|ы|и|й|о|е|ъ|у|э|а|я|м|ю|ь)$/i, '') + '\\S*';
3931 }
3932 } catch (err) {
3933 _iterator44.e(err);
3934 } finally {
3935 _iterator44.f();
3936 }
3937
3938 var regExp = new RegExp('(' + mString.substr(1) + ')', 'gi');
3939
3940 var _iterator45 = _createForOfIteratorHelper(misprintsContainers),
3941 _step45;
3942
3943 try {
3944 for (_iterator45.s(); !(_step45 = _iterator45.n()).done;) {
3945 var selector = _step45.value;
3946 var div = task.querySelector(selector);
3947
3948 if (!div) {
3949 continue;
3950 }
3951
3952 var text = div.innerText;
3953 div.innerHTML = text.replace(regExp, '<span class="misprinted">$1</span>');
3954 }
3955 } catch (err) {
3956 _iterator45.e(err);
3957 } finally {
3958 _iterator45.f();
3959 }
3960 }
3961 } catch (err) {
3962 _iterator43.e(err);
3963 } finally {
3964 _iterator43.f();
3965 }
3966 } //Завершение инициализации
3967
3968
3969 window.readyToKill = true;
3970
3971 _this.focusTask(0);
3972
3973 if (!_this.getTasks()[0].getStorageItem('__timingsStorage').started) {
3974 _this.getTasks()[0].onFocus();
3975 } //Устанавливаем фокус на первый элемент
3976
3977 /*if (hotkeysEnabled) {
3978 const elementToFocus = root.querySelector('.focusMeOnStart');
3979 if (elementToFocus) {
3980 _this.focusElement(elementToFocus.dataset.number);
3981 } else {
3982 _this.focusNextElement();
3983 }
3984 }*/
3985
3986
3987 if (window.onSuiteReady) {
3988 onSuiteReady.apply(_this, [root]);
3989 }
3990 }).catch(function (err) {
3991 return console.error(err);
3992 });
3993 },
3994 //Инициализация ШВшных горячих клавиш (ТуДу: перенести в таск)
3995 initTheOneHotkeys: function initTheOneHotkeys() {
3996 var _this = this,
3997 root = this.getDOMElement();
3998
3999 if (hotkeysEnabled) {
4000 this.elements = root.querySelectorAll('.element:not(.unfocusable)');
4001
4002 for (var i = 0; i < _this.elements.length; i++) {
4003 this.elements[i].dataset.number = i.toString();
4004
4005 if (!this.elements[i].classList.contains('hotKeysBinded')) {
4006 this.elements[i].addEventListener('click', function (e) {
4007 if (!readyToKill || !e.isTrusted) {
4008 return;
4009 }
4010
4011 _this.focusElement(parseInt(this.dataset.number));
4012 });
4013 this.elements[i].classList.add('hotKeysBinded');
4014 } //Прорисовываем кнопкам соответствующие горячие клавиши
4015
4016
4017 var buttons = this.elements[i].querySelectorAll('.btn');
4018
4019 for (var j = 0; j < buttons.length; j++) {
4020 var div = buttons[j].querySelector('.hotkeyHint');
4021
4022 if (!div) {
4023 div = document.createElement('div');
4024 div.className = 'hotkeyHint';
4025 div.innerHTML = elementButtonsHotkeysOrderString[j];
4026 buttons[j].appendChild(div);
4027 } else {
4028 div.innerHTML = elementButtonsHotkeysOrderString[j];
4029 }
4030 }
4031 }
4032 }
4033 },
4034 getSolutionsFromStorage: function getSolutionsFromStorage() {
4035 var options = this.getOptions(),
4036 assId = options.assignment._options.assignment.id,
4037 tasks = this.getTasks();
4038
4039 if (assId) {
4040 var sols = this.storage.getItem('funfrogSavedSolutions_' + assId);
4041
4042 if (sols) {
4043 var _tasks = this.getTasks();
4044
4045 for (var i in _tasks) {
4046 _tasks[i].theOneSolution = sols[i];
4047 }
4048
4049 if (printSolution) {
4050 console.log('Солюшка из локального хранилища:', JSON.parse(JSON.stringify(sols)));
4051 }
4052
4053 return true;
4054 }
4055 }
4056 },
4057 maybeScrollToThisElement: function maybeScrollToThisElement(element, forceTop) {
4058 //Прокручивает страницу к элементу, если он находится за пределами экрана
4059 var root = this.getDOMElement(),
4060 what2scroll = verticalSuite ? root : fullScreenMode ? root.querySelector('.task_focused .block.main>.blockContent') : root.querySelector('.task_focused'),
4061 topMargin = fullScreenMode ? 40 : 10;
4062
4063 if (!forceTop && element.getBoundingClientRect().bottom + 10 > document.documentElement.clientHeight) {
4064 //Надо подкрутить страницу
4065 $(what2scroll).animate({
4066 scrollTop: what2scroll.scrollTop + element.getBoundingClientRect().bottom - document.documentElement.clientHeight + 10
4067 }, 200);
4068 } else if (forceTop || element.getBoundingClientRect().top - topMargin < 0) {
4069 //Надо подкрутить страницу
4070 $(what2scroll).animate({
4071 scrollTop: what2scroll.scrollTop + element.getBoundingClientRect().top - topMargin
4072 }, 200);
4073 }
4074 },
4075 focusNextElement: function focusNextElement(dontScroll) {
4076 //Пытается перевести фокус на следующий элемент
4077 if (window.thisIsMobileDevice) {
4078 return;
4079 }
4080
4081 var root = this.getDOMElement();
4082
4083 var _this = this;
4084
4085 var n = _this.focusedElement && _this.focusedElement.closest('.task_focused') ? //Если есть элемент с фокусом в этом таске
4086 parseInt(_this.focusedElement.dataset.number) + 1 : //Берём следующий
4087 parseInt(root.querySelector('.task_focused .element:not(.unfocusable)') ? root.querySelector('.task_focused .element:not(.unfocusable)').dataset.number : null); //Или берём первый в этом таске
4088
4089 if (!n && n !== 0) {
4090 return;
4091 }
4092
4093 var nothing = false,
4094 nStart = n; //Запоминаем текущий номер элемента, а то вдруг по кругу пройдём: чтоб не зациклиться.
4095
4096 while (!_this.elements[n] || _this.elements[n].closest('.excluded') || !_this.elements[n].closest('.task_focused') || _this.elements[n].closest('.view.hidden') || window.activeWindow && !isDescendant(window.activeWindow, _this.elements[n]) || !window.activeWindow && _this.elements[n].closest('.window')) {
4097 //Если текущий элемент спрятан или не в этом таске
4098 n++;
4099
4100 if (n >= _this.elements.length) {
4101 n = root.querySelector('.task_focused .element:not(.unfocusable)').dataset.number;
4102 }
4103
4104 if (n === nStart) {
4105 nothing = true;
4106 break;
4107 }
4108 }
4109
4110 if (!nothing) {
4111 _this.focusElement(n, dontScroll);
4112 }
4113 },
4114 focusPreviousElement: function focusPreviousElement() {
4115 //Пытается перевести фокус на предыдущий элемент
4116 if (window.thisIsMobileDevice) {
4117 return;
4118 }
4119
4120 var root = this.getDOMElement();
4121
4122 var _this = this;
4123
4124 var n = _this.focusedElement && _this.focusedElement.closest('.task_focused') ? //Если есть элемент с фокусом в этом таске
4125 parseInt(_this.focusedElement.dataset.number) - 1 : //Берём предыдущий
4126 _this.elements.length - 1; //Или берём тупо самый последний
4127
4128 var nothing = false,
4129 nStart = n; //Запоминаем текущий номер элемента, а то вдруг по кругу пройдём: чтоб не зациклиться.
4130
4131 while (!_this.elements[n] || _this.elements[n].closest('.excluded') || !_this.elements[n].closest('.task_focused') || _this.elements[n].closest('.view.hidden') || window.activeWindow && !isDescendant(window.activeWindow, _this.elements[n]) || !window.activeWindow && _this.elements[n].closest('.window')) {
4132 //Если текущий элемент спрятан или не в этом таске
4133 n--;
4134
4135 if (n < 0) {
4136 n = _this.elements.length - 1;
4137 }
4138
4139 if (n === nStart) {
4140 nothing = true;
4141 break;
4142 }
4143 }
4144
4145 if (!nothing) {
4146 _this.focusElement(n);
4147 }
4148 },
4149 focusElement: function focusElement(n, dontScroll) {
4150 if (window.thisIsMobileDevice) {
4151 return;
4152 }
4153
4154 var root = this.getDOMElement();
4155
4156 var _this = this;
4157
4158 n = parseInt(n);
4159 document.activeElement.blur();
4160 Array.from(root.querySelectorAll('.element.focused')).forEach(function (ef) {
4161 ef.classList.remove('focused');
4162 });
4163 this.focusedElement = _this.elements[n];
4164 this.focusedElement.classList.add('focused'); //На потестить
4165
4166 if (!dontScroll) {
4167 this.maybeScrollToThisElement(this.focusedElement);
4168 }
4169
4170 if (this.isReadOnly || this.interfaceBlocked) {
4171 return;
4172 }
4173
4174 if (_this.focusedElement.querySelector('input')) {
4175 setTimeout(function () {
4176 var input = _this.focusedElement.querySelector('input');
4177
4178 if (input) {
4179 input.focus();
4180 }
4181 }, 100);
4182 } else if (_this.focusedElement.querySelector('textarea')) {
4183 setTimeout(function () {
4184 _this.focusedElement.querySelector('textarea').focus();
4185 }, 100);
4186 } else if (!_this.focusedElement.buttons) {
4187 _this.focusedElement.buttons = _this.focusedElement.querySelectorAll('.btn');
4188 }
4189 },
4190 focusTask: function focusTask(i) {
4191 var _this = this;
4192
4193 var n = parseInt(i);
4194
4195 if (!verticalSuite) {
4196 if (!this.tasks) {
4197 return;
4198 }
4199
4200 Array.from(this.getDOMElement().querySelectorAll('.task_focused')).forEach(function (task) {
4201 task.classList.remove('task_focused');
4202 });
4203 this.tasks[n].classList.add('task_focused');
4204 } else {
4205 TolokaHandlebarsTaskSuite.prototype.focusTask.apply(this, arguments);
4206 }
4207
4208 if (window.curTask === n) {
4209 return;
4210 }
4211
4212 window.curTask = n; //Обновлянем все просмотрщики с канвасами
4213
4214 var _iterator48 = _createForOfIteratorHelper(_this.tasks[window.curTask].querySelectorAll('.imageField .imageBlock canvas')),
4215 _step48;
4216
4217 try {
4218 for (_iterator48.s(); !(_step48 = _iterator48.n()).done;) {
4219 var c = _step48.value;
4220
4221 if (c.initCanvas) {
4222 c.initCanvas();
4223 }
4224 }
4225 } catch (err) {
4226 _iterator48.e(err);
4227 } finally {
4228 _iterator48.f();
4229 }
4230
4231 setTimeout(function () {
4232 window.scrollingNow = false;
4233
4234 var elementToFocus = _this.tasks[window.curTask].querySelector('.focusMeOnStart');
4235
4236 if (!elementToFocus && _this.focusedElement && _this.focusedElement.closest('.task_focused')) {
4237 elementToFocus = _this.focusedElement;
4238 }
4239
4240 if (elementToFocus) {
4241 _this.focusElement(elementToFocus.dataset.number, true);
4242 } else {
4243 _this.focusNextElement(true);
4244 }
4245
4246 _this.maybeScrollToThisElement(_this.tasks[window.curTask], true);
4247 }, 250);
4248 },
4249 focusNextTask: function focusNextTask() {
4250 if (this.getDOMElement().querySelector('.window.opened')) {
4251 return;
4252 }
4253
4254 this.getDOMElement().querySelector('.task_focused .rightScroll').click();
4255 },
4256 focusPreviousTask: function focusPreviousTask() {
4257 if (this.getDOMElement().querySelector('.window.opened')) {
4258 return;
4259 }
4260
4261 this.getDOMElement().querySelector('.task_focused .leftScroll').click();
4262 },
4263 initHotkeys: function initHotkeys() {
4264 this.hotkey.reset();
4265 this.hotkey.on('arrow-left', this.focusPreviousTask, this);
4266 this.hotkey.on('arrow-down', this.focusNextTask, this); //Почему-то это arrow-right
4267
4268 if (!hotkeysEnabled) {
4269 this.hotkey.on('arrow-up', this.focusPreviousTask, this);
4270 this.hotkey.on('arrow-right', this.focusNextTask, this); //Почему-то это arrow-down
4271 } else {
4272 this.hotkey.on('arrow-up', this.focusPreviousElement, this);
4273 this.hotkey.on('arrow-right', this.focusNextElement, this); //Почему-то это arrow-down
4274
4275 this.hotkey.on('key', this.onKey, this);
4276 }
4277 },
4278 onKey: function onKey(key) {
4279 if (Date.now() - this.keydownTime > timeToOpenHintByKey) {
4280 return;
4281 }
4282
4283 if (this.focusedElement && this.focusedElement.buttons && this.focusedElement.closest('.task_focused') && elementButtonsHotkeysOrderString.includes(key)) {
4284 var btn = this.focusedElement.buttons[elementButtonsHotkeysOrderString.indexOf(key)];
4285
4286 if (btn) {
4287 if (btn.classList.contains('linkButton')) {
4288 btn.querySelector('a').click();
4289 } else {
4290 btn.click();
4291 }
4292 }
4293 }
4294 },
4295 validate: function validate() {
4296 var root = this.getDOMElement(),
4297 _this = this;
4298
4299 if (typeof beforeTaskSuiteValidation !== "undefined") {
4300 var _result = beforeTaskSuiteValidation.apply(this, [root]);
4301
4302 if (_result) {
4303 return true;
4304 }
4305 }
4306
4307 window.noValidationErrors = true; //Перевод фокуса на активный таск (он может быть не первым, если это задание - обучение, и были указаны неправильные ответы)
4308
4309 window.hintScrollerCounter = 0;
4310 var tf = root.querySelector('.task_focused');
4311
4312 if (!tf) {
4313 this.focusTask(0);
4314 tf = root.querySelector('.task_focused');
4315 }
4316
4317 this.currentFocusedTaskNumber = tf.dataset.number || 0;
4318 clearInterval(window.hintScrollTimer);
4319 window.hintScrollTimer = setInterval(function () {
4320 var curTask = root.querySelector('.task_focused');
4321
4322 if (curTask) {
4323 if (!verticalSuite) {
4324 if (curTask.dataset.number !== _this.currentFocusedTaskNumber) {
4325 root.style.left = curTask.dataset.number * -100 + 'vw';
4326 }
4327 } else {
4328 window.scrollingNow = true;
4329 window.curTask = parseInt(curTask.dataset.number);
4330 setTimeout(function () {
4331 $(root).animate({
4332 scrollTop: curTask.offsetTop
4333 }, 200);
4334 setTimeout(function () {
4335 window.scrollingNow = false;
4336 }, 250);
4337 }, 50);
4338 }
4339 }
4340
4341 window.hintScrollerCounter++;
4342
4343 if (window.hintScrollerCounter > 1 || curTask && _this.currentFocusedTaskNumber !== curTask.dataset.number) {
4344 clearInterval(window.hintScrollTimer);
4345 }
4346 }, 1000);
4347 var result = TolokaHandlebarsTaskSuite.prototype.validate.apply(this, arguments);
4348
4349 if (printSolution) {
4350 console.log('Итоговая солюшка тасксьюта: ', arguments[0]);
4351 }
4352
4353 return result;
4354 }
4355};
4356/*
4357*
4358* Хелперы Handlebars
4359*
4360*/
4361
4362window.theOneHandlebarsHelpers = {
4363 //Рисует поля для выбора ответов
4364 drawFields: function drawFields(fields) {
4365 var context = this; //Логика такая - перебираем все поля во входном JSON и отрисовываем их. Вешать на них валидацию будем в onRender каждого таска
4366
4367 return getFieldsHTML(fields, context);
4368 },
4369 urlDecode: function urlDecode(url) {
4370 return decodeURIComponent(url.replace(/\+/g, ' '));
4371 },
4372 urlEncode: function urlEncode(url) {
4373 return encodeURIComponent(url);
4374 },
4375 urlRemoveSpacesAndEncode: function urlRemoveSpacesAndEncode(url) {
4376 return encodeURIComponent(url.replace(/\s+/g, ''));
4377 },
4378 //Выводит строки для описания входного задания - возможно, стоит удалить?
4379 drawInputStrings: function drawInputStrings(inputStrings, placeholders) {
4380 var out = '';
4381 Array.from(inputStrings).forEach(function (inputString) {
4382 //У нас могут быть либо обычные плейсхолдеры - %%имя%%, либо со ссылкой - %%имя||ссылка%%. Меняем их тут на соответствующий код.
4383 var s = inputString.text.replace(/\$\$.*?\$\$/g, function (str) {
4384 var placeholder = str.match(/[$|][^$|]+[$|]/g);
4385
4386 if (placeholder[1]) {
4387 return '<a href="' + placeholders[placeholder[1].match(/[$|]([^$|]+)[$|]/)[1]] + '" target="_blank">' + placeholders[placeholder[0].match(/[$|]([^$|]+)[$|]/)[1]] + '</a>';
4388 } else if (placeholder[0]) {
4389 return placeholders[placeholder[0].match(/[$|]([^$|]+)[$|]/)[1]];
4390 }
4391 });
4392 out += '<div class="stepTitle">' + s + '</div>';
4393 });
4394 return out;
4395 },
4396 //Помогает передавать объекты как аргументы хелперов
4397 o: function o(_ref) {
4398 var hash = _ref.hash;
4399 return hash;
4400 },
4401 //Устанавливает словарь локализации window.l10n
4402 setLocalization: function setLocalization(l10n) {
4403 window.handlebarsContext = this;
4404 window.currentRenderingTask.handlebarsContext = this;
4405 window.l10n = l10n;
4406 },
4407 //Локализует строку в соответствии с window.l10n
4408 l: function l(t) {
4409 return localize(t);
4410 },
4411 setTaskName: function setTaskName(name) {
4412 window.handlebarsContext = this;
4413 window.currentRenderingTask.handlebarsContext = this;
4414 name = name ? name.length < 60 ? name : name.substr(0, 57) + '...' : 'Задание';
4415 var taskNameHTML = "<div class='taskName'>".concat(name, "</div>");
4416 return taskNameHTML;
4417 },
4418 ploosAdeen: function ploosAdeen(someNumb) {
4419 return someNumb + 1;
4420 },
4421 shortName: function shortName(name) {
4422 return name ? name.length < 60 ? name : name.substr(0, 57) + '...' : 'Задание';
4423 },
4424 e: function e(options) {
4425 return "<div class=\"element ".concat(options.hash.name ? options.hash.name : 'alien unfocusable', " ").concat(options.hash.class, "\" ").concat(options.hash.HTMLTitle ? "title=\"".concat(options.hash.HTMLTitle, "\"") : '', ">").concat(options.fn(this), "</div>");
4426 },
4427 a: function a(options) {
4428 if (!options.hash.title) {
4429 options.hash.title = options.hash.href;
4430 }
4431
4432 if (!options.hash.name) {
4433 options.hash.name = '';
4434 }
4435
4436 if (!options.hash.class) {
4437 options.hash.class = '';
4438 }
4439
4440 return "<span class=\"link ".concat(options.hash.name, " ").concat(options.hash.class, "\"><a data-name=\"").concat(options.hash.name, "\" target=\"_blank\" href=\"").concat(options.hash.href, "\" rel=\"noreferrer\"><i class='ffIcon' data-icon=\"open_in_new\"></i>").concat(options.hash.title, "</a></span>");
4441 },
4442 window: function window(options) {
4443 return "<div class=\"window ".concat(options.hash.name, " ").concat(options.hash.class, "\">").concat(options.fn(this), "</div>");
4444 },
4445 v: function v(options) {
4446 return "<div class=\"view ".concat(options.hash.name, " ").concat(options.hash.class, "\" data-name=\"").concat(options.hash.name, "\">").concat(options.fn(this), "</div>");
4447 },
4448 flexBox: function flexBox(options) {
4449 return "<div class=\"flexBox ".concat(options.hash.name, " ").concat(options.hash.class, "\" ").concat(options.hash.name ? "data-name=\"".concat(options.hash.name, "\"") : '', ">").concat(options.fn(this), "</div>");
4450 },
4451 spoiler: function spoiler(options) {
4452 return "<div class=\"element spoiler ".concat(options.hash.name ? options.hash.name : 'alien', " ").concat(options.hash.class, "\" data-name=\"").concat(options.hash.name, "\">\n <div class='spoilerHead'>\n ").concat(getSwitchButtonHTML({
4453 icon: 'angle_down',
4454 caption: options.hash.title
4455 }), "\n </div>\n <div class='spoilerContainer'>\n <div class='spoilerContent'>\n ").concat(options.fn(this), "\n </div>\n </div>\n </div>");
4456 },
4457 iLine: function iLine(options) {
4458 if (!options.hash.name) {
4459 options.hash.name = '';
4460 }
4461
4462 if (!options.hash.title) {
4463 options.hash.title = '';
4464 }
4465
4466 return "<div class=\"infoLine ".concat(options.hash.name, " ").concat(options.hash.class, "\">") + "<span class='ilTitle'>".concat(options.hash.title, ":</span>") + "<span class='ilContent'>".concat(options.fn(this), "</span>") + "</div>";
4467 },
4468 iLines: function iLines(options) {
4469 if (!options.hash.name) {
4470 options.hash.name = '';
4471 }
4472
4473 if (!options.hash.title) {
4474 options.hash.title = '';
4475 }
4476
4477 return "<div class=\"infoLine infoLines ".concat(options.hash.name, " ").concat(options.hash.class, "\">") + "<span class='ilTitle'>".concat(options.hash.title, ":</span>") + "<span class='ilContent'>".concat(options.fn(this), "</span>") + "</div>";
4478 },
4479 baloon: function baloon(options) {
4480 var opacity = options.hash.opacity ? options.hash.opacity : 20;
4481 var bdOpacity = Math.min(255, parseInt(opacity, 16) + 40).toString(16);
4482 options.hash.color = '#' + options.hash.color;
4483 return "<div class=\"baloon ".concat(options.hash.name, " ").concat(options.hash.class, "\" style=\"border-color: ").concat(options.hash.color).concat(bdOpacity, "; background-color: ").concat(options.hash.color).concat(opacity, ";\">") + "".concat(options.hash.title ? "<div class=\"baloonTitle\">".concat(options.hash.title, "</div>") : '') + "".concat(options.fn(this)) + "</div>";
4484 },
4485 block: function block(options) {
4486 var blockButtons = '';
4487
4488 if (!options.hash.class) {
4489 options.hash.class = '';
4490 }
4491
4492 if (taskMinimizer && !options.hash.class.includes('nonMinimizable') && (!fullScreenMode || fullScreenMode && !options.hash.class.includes('main'))) {
4493 options.hash.class += ' minimizable';
4494 }
4495
4496 if (options.hash.class.includes('main')) {
4497 if (useComplainter) {
4498 blockButtons += "\n <div class='blockTitle btnComplainter'>\n <a title=\"".concat(localize('complaint'), "\">\n <i class=\"ffIcon\" data-icon=\"comment_smile\"></i>\n </a>\n </div>");
4499 }
4500
4501 if (useGlossarySearch) {
4502 blockButtons += "\n <div class='blockTitle btnGlossarySearch noHotkeys'>\n <a title=\"".concat(localize('glossarySearch'), "\">\n <i class=\"ffIcon\" data-icon=\"helpRound\"></i>\n </a>\n <div class='glossarySearchForm alien'></div>\n </div>");
4503 }
4504
4505 blockButtons += "\n <div class='blockTitle darkButt btnDarkSide'>\n <a title=\"".concat(localize('darkSide'), "\">\n <i class=\"ffIcon\" data-icon=\"invert_colors\"></i>\n </a>\n </div>");
4506
4507 if (window.customTitleElements) {
4508 var _iterator49 = _createForOfIteratorHelper(customTitleElements),
4509 _step49;
4510
4511 try {
4512 for (_iterator49.s(); !(_step49 = _iterator49.n()).done;) {
4513 var e = _step49.value;
4514 blockButtons += "\n <div class='blockTitle custom ".concat(e.name, " ").concat(e.class ? e.class : '', "'>\n ").concat(Handlebars.compile(e.content)(this), "\n </div>");
4515 }
4516 } catch (err) {
4517 _iterator49.e(err);
4518 } finally {
4519 _iterator49.f();
4520 }
4521 }
4522
4523 blockButtons += "\n <div class='blockTitle tabScroller'>\n <span class='leftScroll'>\n <i class=\"ffIcon\" data-icon=\"keyboard_backspace\"></i>\n <i class=\"ffIcon\" data-icon=\"keyboard_tab\"></i>\n </span>\n <span class=\"tabNumbers\">\n <span class='curTab'></span>\n /\n <span class='tabsCount'></span>\n </span>\n <span class='rightScroll'>\n <i class=\"ffIcon\" data-icon=\"keyboard_backspace\"></i>\n <i class=\"ffIcon\" data-icon=\"keyboard_tab\"></i>\n </span>\n <div class='tabList'></div>\n </div>\n ";
4524 }
4525
4526 var title = '';
4527
4528 if (options.hash.title) {
4529 title = "<div class='blockTitle'>".concat(options.hash.title, "<span class='miniMaxi' title='\u0421\u0432\u0435\u0440\u043D\u0443\u0442\u044C/\u0440\u0430\u0437\u0432\u0435\u0440\u043D\u0443\u0442\u044C \u0431\u043B\u043E\u043A'><i class=\"material-icons\">keyboard_arrow_up</i></span></div>");
4530 } // свернуть блок, если блоку назначен класс minimized
4531
4532
4533 var minimized = '';
4534
4535 if (options.hash.class.includes('minimized')) {
4536 minimized = 'minimized';
4537 options.hash.class = options.hash.class.replace('minimized', '');
4538 } //Подсказка
4539
4540
4541 var help = '';
4542
4543 if (options.hash.help) {
4544 help = "<div class=\"helpContainer\"><i class=\"ffIcon\" data-icon=\"help\"></i><div class=\"popUpTitle\" style=\"height: 0;\"><div class=\"buttPath\">\u041F\u043E\u0434\u0441\u043A\u0430\u0437\u043A\u0430:</div>".concat(options.hash.help, "</div></div>");
4545 }
4546
4547 return "<div class='block ".concat(options.hash.name, " ").concat(options.hash.class, "' ").concat(options.hash.name ? "data-name=\"".concat(options.hash.name, "\"") : '', "><div class='blockContent ").concat(minimized, "'>").concat(help, "<div class='blockButtons'>").concat(blockButtons, "</div>").concat(title).concat(options.fn(this), "</div></div>");
4548 },
4549 //Рисует баннер для шаблонов Директа
4550 drawBanner: function drawBanner(banner) {
4551 if (!banner) {
4552 return;
4553 }
4554
4555 var b = banner.data[0];
4556
4557 if (!b) {
4558 return;
4559 }
4560
4561 var title = b.title + (b.title_extension ? ' - ' + b.title_extension : '');
4562 var domain = /https?:\/\/([^/]+)/.exec(b.parametrized_href)[1];
4563 var out = '<div class="element alien unfocusable bannerElement">' + '<div class="bannerContainer">' + '<div class="banner">' + '<div class="bannerTitle">' + '<i class="ffIcon" data-icon="eye"></i><a target="_blank" class="unclicked" href="' + b.parametrized_href + '" rel="noreferrer" data-domain="' + (domain ? domain : b.parametrized_href) + '">' + title + '</a>' + '</div>' + '<div class="bannerText">' + b.body + '</div>' + '<div class="bannerDomain">' + b.domain + '</div>' + '</div>' + '</div>' + '</div>';
4564 return out;
4565 },
4566 //Рисует дополнительные данные баннера для шаблонов Директа
4567 drawBannerInfo: function drawBannerInfo(banner) {
4568 if (!banner) {
4569 return;
4570 }
4571
4572 var b = banner.data[0];
4573 var m = banner.meta;
4574
4575 if (!b || !m) {
4576 return;
4577 }
4578
4579 var infoLines = '';
4580
4581 function addInfoLine(title, lineClass, value, valueType, additional) {
4582 var v;
4583
4584 if (!valueType) {
4585 valueType = 'text';
4586 }
4587
4588 switch (valueType) {
4589 case 'link':
4590 v = '<a target="_blank" href="' + additional + '" rel="noreferrer">' + value + '</a>';
4591 break;
4592
4593 case 'somethingOther':
4594 break;
4595
4596 default:
4597 v = value;
4598 }
4599
4600 var line = '<div class="bannerInfoLine ' + lineClass + '">' + '<div class="bilTitle">' + title + '</div>' + '<div class="bilValue ' + valueType + '">' + v + '</div>' + '</div>';
4601 infoLines += line;
4602 }
4603
4604 function arrayOrStringToString(something) {
4605 if (Array.isArray(something)) {
4606 var list = '';
4607
4608 var _iterator50 = _createForOfIteratorHelper(something),
4609 _step50;
4610
4611 try {
4612 for (_iterator50.s(); !(_step50 = _iterator50.n()).done;) {
4613 var item = _step50.value;
4614 list += ', ' + item;
4615 }
4616 } catch (err) {
4617 _iterator50.e(err);
4618 } finally {
4619 _iterator50.f();
4620 }
4621
4622 list = list.substr(2);
4623 return list;
4624 } else if (something) {
4625 return something;
4626 }
4627 } //Регионы показа
4628
4629
4630 if (b.geo_country) {
4631 addInfoLine('Регионы показа:', 'geoCountry', arrayOrStringToString(b.geo_country));
4632 } //ГеоТекст
4633 //if (b.geo_text) {addInfoLine('Регион подробно:', 'geoText', b.geo_text);}
4634 //Языки
4635
4636
4637 if (b.lang) {
4638 addInfoLine('Языки:', 'lang', arrayOrStringToString(b.lang));
4639 } //Логин
4640
4641
4642 if (b.login) {
4643 addInfoLine('Логин:', 'login', b.login);
4644 } //Просмотр баннера
4645
4646
4647 if (m.banner_id) {
4648 addInfoLine('Просмотр баннера:', 'bannerLink', m.banner_id, 'link', 'https://direct-mod.yandex-team.ru/history/banner/?bid=' + m.banner_id);
4649 } //Просмотр кампании
4650
4651
4652 if (m.campaign_id) {
4653 addInfoLine('Просмотр кампании:', 'campaignLink', m.campaign_id, 'link', 'https://direct-mod.yandex-team.ru/campaign/show/?cid=' + m.campaign_id);
4654 }
4655
4656 var out = '<div class="element alien unfocusable bannerInfoElement">' + '<div class="bannerInfoContainer">' + '<div class="bannerInfo">' + infoLines + '</div>' + '</div>' + '</div>';
4657 return out;
4658 },
4659 misprints: function misprints(_misprints) {
4660 if (!_misprints) {
4661 return;
4662 }
4663
4664 var stringifiedMisprints = '<div class="stringifiedMisprints" style="display: none;">' + JSON.stringify(_misprints) + '</div>';
4665 return stringifiedMisprints;
4666 }
4667};
4668/*
4669*
4670* Всякие функции
4671*
4672*/
4673
4674window.dumpObjectKeysToDocumentBody = function (obj) {
4675 document.querySelector('body').innerHTML += '<br><br>';
4676
4677 for (var k in obj) {
4678 document.querySelector('body').innerHTML += k + '<br>';
4679 }
4680};
4681
4682window.loadStuffFromTheOneLibrary = function (assignment) {
4683 //Вытаскиваем настройки шаблона из объекта
4684 for (var s in window.theOneSettings) {
4685 window[s] = window.theOneSettings[s];
4686 } //Обогащаем таск и тасксьют методами из библиотеки и накидываем поверх кастомных методов, ежели таковые имеются
4687
4688
4689 var taskSuite = assignment.getTaskSuite();
4690 injectMethods(theOneAssignmentMethods, assignment, window.customAssignmentMethods);
4691 injectMethods(theOneTaskSuitePrototype, taskSuite, window.customTaskSuiteMethods);
4692
4693 var _iterator51 = _createForOfIteratorHelper(taskSuite.getTasks()),
4694 _step51;
4695
4696 try {
4697 for (_iterator51.s(); !(_step51 = _iterator51.n()).done;) {
4698 var task = _step51.value;
4699 injectMethods(theOneTaskPrototype, task, window.customTaskMethods);
4700 }
4701 } catch (err) {
4702 _iterator51.e(err);
4703 } finally {
4704 _iterator51.f();
4705 }
4706
4707 if (!window.helpersRegisteredAndCssApplied) {
4708 //Регистрируем библиотечные хелперы Handlebars
4709 for (var name in theOneHandlebarsHelpers) {
4710 Handlebars.registerHelper(name, theOneHandlebarsHelpers[name]);
4711 } //Вставляем CSS
4712
4713
4714 applyTheOneCSS();
4715 window.helpersRegisteredAndCssApplied = true;
4716 } //То, что раньше было в onResume()
4717
4718
4719 window.lang = assignment.getWorkspaceOptions().language || 'ru'; //Инициализация жалобщика
4720
4721 if (useComplainter) {
4722 $.getScript(complainterLibSrc, function (data, textStatus, jqxhr) {
4723 var options = assignment.getWorkspaceOptions();
4724 var namespace = complainterNamespace;
4725 window.Complaints.default.initialize({
4726 api: {
4727 url: '',
4728 origin: options.origin,
4729 proxyName: complainterProxyName,
4730 assignmentId: assignment.getId()
4731 },
4732 language: options.language,
4733 namespace: namespace,
4734 ajax: function ajax(ajaxOptions) {
4735 !ajaxOptions.type && (ajaxOptions.type = ajaxOptions.method);
4736 return assignment.getSandboxChannel().request('proxy', ajaxOptions);
4737 },
4738 custom_causes: complainterCauses,
4739 custom_translations: complainterTranslations
4740 });
4741 });
4742 } //Пытаемся выпытать у браузера сохранённую солюшку, если она была
4743
4744
4745 assignment.getTaskSuite().getSolutionsFromStorage();
4746};
4747
4748window.injectMethods = function (from, to, customMethods) {
4749 for (var m in from) {
4750 to[m] = from[m];
4751 }
4752
4753 if (customMethods) {
4754 for (var _m in customMethods) {
4755 if (to[_m]) {
4756 to['_' + _m] = to[_m];
4757 }
4758
4759 to[_m] = customMethods[_m];
4760 }
4761 }
4762};
4763
4764window.applyTheOneCSS = function () {
4765 var style = document.createElement('style');
4766 style.setAttribute('type', 'text/css');
4767 style.innerHTML = theOneCSS;
4768 document.querySelector('head').appendChild(style);
4769};
4770
4771window.initHelpContainer = function (q) {
4772 var popUpTitle = q.querySelector('.popUpTitle');
4773 popUpTitle.innerHTML = "<div class=\"popUpTitleInner\">".concat(popUpTitle.innerHTML, "</div>");
4774 q.addEventListener('click', function () {
4775 if (this.classList.contains('triggered')) {
4776 this.classList.remove('triggered');
4777 } else {
4778 this.classList.add('triggered');
4779 makeSureThisDivFitsOnScreen(q.querySelector('.popUpTitle'));
4780 }
4781 });
4782 q.classList.add('initialized');
4783}; //Проверяет, помещается ли указанный контейнер на экране, и если нет - сдвигает его и масштабирует
4784
4785
4786window.makeSureThisDivFitsOnScreen = function (div) {
4787 div.setAttribute('style', '');
4788 var style = getComputedStyle(div),
4789 margin = 20,
4790 //Ровно на столько пикселей от края экрана будут отодвигаться подсказки
4791 task = fullScreenMode ? div.closest('.block.main') || div.closest('.windowContent') || div.closest('.task') : div.closest('.windowContent') || div.closest('.task');
4792 var verticalProperty,
4793 n = 0;
4794
4795 do {
4796 if (n++ > 20) {
4797 break;
4798 }
4799
4800 var divRect = div.getBoundingClientRect(),
4801 taskRect = task.getBoundingClientRect(),
4802 divEnd = void 0; //console.log(divRect);
4803 //console.log(taskRect);
4804 //Ширина блока должна быть меньше ширины таска
4805
4806 if (divRect.width > taskRect.width - 2 * margin) {
4807 div.style.maxWidth = taskRect.width - 2 * margin + "px";
4808 divRect = div.getBoundingClientRect();
4809 taskRect = task.getBoundingClientRect();
4810 } //Высота блока должна быть меньше высоты таска
4811
4812
4813 if (divRect.height > taskRect.height - 2 * margin) {
4814 div.style.maxHeight = taskRect.height - 2 * margin + "px";
4815 divRect = div.getBoundingClientRect();
4816 taskRect = task.getBoundingClientRect();
4817 } //Выход за правую границу
4818
4819
4820 divEnd = divRect.right + margin;
4821
4822 if (divEnd > taskRect.right - 20) {
4823 div.style.left = "-" + (divEnd - taskRect.right).toString() + "px";
4824 divRect = div.getBoundingClientRect();
4825 taskRect = task.getBoundingClientRect();
4826 } //Выход за нижнюю границу
4827
4828
4829 divEnd = divRect.bottom + margin;
4830
4831 if (!div.classList.contains('errorMsgBlock') && divEnd > taskRect.bottom + 10) {
4832 div.style.top = "-" + (divEnd - taskRect.bottom - parseInt(style.top)).toString() + "px";
4833 divRect = div.getBoundingClientRect();
4834 taskRect = task.getBoundingClientRect();
4835 } //Выход за левую границу
4836
4837
4838 divEnd = divRect.left - margin;
4839
4840 if (divEnd < -10) {
4841 div.style.left = margin + "px";
4842 divRect = div.getBoundingClientRect();
4843 taskRect = task.getBoundingClientRect();
4844 } //Выход за верхнюю границу
4845
4846
4847 divEnd = divRect.top - margin;
4848
4849 if (!div.classList.contains('errorMsgBlock') && divEnd < taskRect.top - 10) {
4850 if (div.classList.contains('errorMsgBlock')) {
4851 div.style.maxHeight = divRect.height + divEnd - taskRect.top + "px";
4852 } else {
4853 div.style.top = "-" + (divEnd - taskRect.top - parseInt(style.top)).toString() + "px";
4854 }
4855
4856 divRect = div.getBoundingClientRect();
4857 taskRect = task.getBoundingClientRect();
4858 }
4859 } while (n < 3);
4860};
4861
4862window.initImageField = function (imageField) {
4863 /*
4864 Тут 2 канваса, внешний и внутренний.
4865 img - внутренний канвас с картинкой pic
4866 canvas - внешний канвас.
4867 */
4868 var imageSource = imageField.querySelector('.imageSource'),
4869 imageOrigLink = imageField.querySelector('.imageOriginalLink'),
4870 imageBlock = imageField.querySelector('.imageBlock'),
4871 imageConsole = imageField.querySelector('.imageConsole'),
4872 noCanvasMode = imageField.classList.contains('noCanvas'),
4873 imgRatio,
4874 canvasRatio,
4875 imgD,
4876 leftButtsJSON;
4877 var leftButts = imageField.querySelector('.imageLeftButts'),
4878 rightButts = imageField.querySelector('.imageRightButts');
4879
4880 if (!imageSource) {
4881 ce('imageField_no_imageSource_div', imageField.className);
4882 return;
4883 }
4884
4885 imageSource = imageSource.innerHTML;
4886
4887 if (!imageSource) {
4888 ce('imageField_no_imageSource', imageField.className);
4889 return;
4890 }
4891
4892 if (imageOrigLink) {
4893 imageOrigLink = imageOrigLink.innerHTML;
4894 }
4895
4896 if (noCanvasMode) {
4897 var img = document.createElement('img');
4898 img.className = 'justAnImage';
4899 img.setAttribute('src', imageSource);
4900 imageBlock.appendChild(img);
4901 leftButtsJSON = {
4902 type: "actionButtonGroup",
4903 class: "imageFieldRightControls",
4904 flags: ["unfocusable"],
4905 buttons: [{
4906 iconClass: "expandButtIcon",
4907 hint: "Развернуть на весь экран.\nДвойной клик по изображению делает то же самое."
4908 }]
4909 };
4910 leftButts.innerHTML = handleFieldAndGetHTML(leftButtsJSON);
4911 var btnA;
4912 btnA = leftButts.querySelector('.expandButtIcon a');
4913
4914 if (btnA) {
4915 var expandStuff = function expandStuff(e) {
4916 e.preventDefault();
4917 imageField.classList.toggle('expanded');
4918
4919 var btn = _btnA.closest('.btn');
4920
4921 if (imageField.classList.contains('expanded')) {
4922 btn.classList.add('selected');
4923 } else {
4924 btn.classList.remove('selected');
4925 }
4926 };
4927
4928 var _btnA = btnA;
4929 btnA.addEventListener('click', expandStuff);
4930 imageBlock.addEventListener('dblclick', expandStuff);
4931 }
4932 } else {
4933 var initCanvas = function initCanvas() {
4934 ctx.canvas.width = canvas.offsetWidth;
4935 ctx.canvas.height = canvas.offsetHeight;
4936 ctx.canvas.halfWidth = canvas.offsetWidth / 2;
4937 ctx.canvas.halfHeight = canvas.offsetHeight / 2;
4938 canvas.scale =
4939 /* canvas.scale ? canvas.scale :*/
4940 Math.min(canvas.width / _img.width, canvas.height / _img.height);
4941 canvas.minScale =
4942 /*canvas.minScale ? canvas.minScale : */
4943 canvas.scale;
4944 _img.rotation = _img.rotation ? _img.rotation : 0;
4945 _img.left = _img.width / -2;
4946 _img.top = _img.height / -2;
4947 onScaleChange();
4948 ctx.setTransform(canvas.scale, 0, 0, canvas.scale, canvas.halfWidth, canvas.halfHeight);
4949 ctx.drawImage(_img, _img.left, _img.top);
4950 imgRatio = _img.width / _img.height;
4951 canvasRatio = canvas.width / canvas.height;
4952
4953 if (imgRatio < canvasRatio) {
4954 imgD = 1;
4955 } else {
4956 imgD = -1;
4957 }
4958
4959 if (imgRatio < 1) {
4960 imgD *= -1;
4961 }
4962 };
4963
4964 var disableButts = function disableButts() {
4965 Array.from(leftButts.querySelectorAll('.btn.selected:not(.adjustSolidButtIcon):not(.expandButtIcon)')).forEach(function (btn) {
4966 return btn.classList.remove('selected');
4967 });
4968 ['freeRotationMode', 'freeHueMode', 'freeContrastMode', 'freeBrightnessMode', 'freeSaturationMode'].forEach(function (name) {
4969 return _img[name] = false;
4970 });
4971 };
4972
4973 var clrscr = function clrscr() {
4974 ctx.clearRect(canvas.left, canvas.top, canvas.scaledWidth, canvas.scaledHeight);
4975 };
4976
4977 var imgClrscr = function imgClrscr() {
4978 imgCtx.clearRect(-_img.halfWidth - 10, -_img.halfHeight - 10, _img.width + 20, _img.height + 20);
4979 };
4980
4981 var imgLog = function imgLog(text) {
4982 imageConsole.style.opacity = "0.8";
4983 imageConsole.innerHTML = text;
4984 clearTimeout(imageConsole.timeout);
4985 imageConsole.timeout = setTimeout(function () {
4986 imageConsole.style.opacity = "0";
4987 }, 2000);
4988 };
4989
4990 var applyFilters = function applyFilters(f) {
4991 if (!f) {
4992 f = _img.filters;
4993 }
4994
4995 imgCtx.filter = "invert(".concat(f.invert, "%) hue-rotate(").concat(f.hue, "deg) contrast(").concat(f.contrast, "%) brightness(").concat(f.brightness, "%) saturate(").concat(f.saturation, "%)");
4996 imgCtx.drawImage(pic, pic.left, pic.top);
4997 ctx.drawImage(_img, _img.left, _img.top);
4998 };
4999
5000 var rotatePic = function rotatePic() {
5001 clrscr();
5002 imgClrscr();
5003 var angle = _img.rotation * 180 % 180;
5004 angle = angle > 90 ? 90 - (angle - 90) : angle;
5005 var alpha = Math.atan2(_img.height, _img.width); // Угол диагонали
5006
5007 var alpha_1 = alpha + angle * Math.PI / 180; // Новый угол первой диагонали
5008
5009 var alpha_2 = alpha_1 + Math.PI - 2 * alpha; // Новый угол второй диагонали
5010
5011 var H_new = Math.max(Math.abs(Math.sin(alpha_1)), Math.abs(Math.sin(alpha_2))) * Math.sqrt(_img.width * _img.width + _img.height * _img.height); // Длина проекции на ось Y
5012
5013 var W_new = Math.max(Math.abs(Math.cos(alpha_1)), Math.abs(Math.cos(alpha_2))) * Math.sqrt(_img.width * _img.width + _img.height * _img.height); // Длина проекции на ось X
5014
5015 _img.scale = 1 / Math.max(W_new / _img.width, H_new / _img.height);
5016
5017 if (canvas.scale > canvas.minScale || imgD < 0) {
5018 canvas.scale = canvas.savedScale / _img.scale;
5019 }
5020
5021 imgCtx.setTransform(_img.scale, 0, 0, _img.scale, _img.halfWidth, _img.halfHeight);
5022 imgCtx.rotate(_img.rotation * Math.PI);
5023 imgCtx.drawImage(pic, pic.left, pic.top);
5024 onScaleChange();
5025 positionCorrector();
5026 ctx.setTransform(canvas.scale, 0, 0, canvas.scale, canvas.halfWidth, canvas.halfHeight);
5027 clrscr();
5028 ctx.drawImage(_img, _img.left, _img.top);
5029 imgLog("\u0423\u0433\u043E\u043B \u043F\u043E\u0432\u043E\u0440\u043E\u0442\u0430 \u0438\u0437\u043E\u0431\u0440\u0430\u0436\u0435\u043D\u0438\u044F: ".concat(Math.round(_img.rotation * 180), "\xB0"));
5030 };
5031
5032 var imgDrag = function imgDrag(e) {
5033 canvas.midnightMover = true;
5034 imageField.classList.add('dragging');
5035 };
5036
5037 var imgMove = function imgMove(e) {
5038 if (!canvas.midnightMover) {
5039 return;
5040 }
5041
5042 _img.left += e.movementX / canvas.scale;
5043 _img.top += e.movementY / canvas.scale;
5044 positionCorrector();
5045 clrscr();
5046 ctx.drawImage(_img, _img.left, _img.top);
5047 };
5048
5049 var imgDrop = function imgDrop() {
5050 canvas.midnightMover = false;
5051 imageField.classList.remove('dragging');
5052 };
5053
5054 var onScaleChange = function onScaleChange() {
5055 canvas.left = -canvas.halfWidth / canvas.scale;
5056 canvas.top = -canvas.halfHeight / canvas.scale;
5057 canvas.scaledWidth = canvas.width / canvas.scale;
5058 canvas.scaledHeight = canvas.height / canvas.scale;
5059 canvas.right = -canvas.left;
5060 canvas.bottom = -canvas.top;
5061 onPositionChange();
5062 };
5063
5064 var onPositionChange = function onPositionChange() {
5065 _img.right = _img.left + _img.width;
5066 _img.bottom = _img.top + _img.height;
5067 };
5068
5069 var positionCorrector = function positionCorrector() {
5070 onPositionChange();
5071
5072 if (_img.width > canvas.scaledWidth) {
5073 if (_img.right > canvas.right && _img.left > canvas.left) {
5074 _img.left = canvas.left;
5075 }
5076
5077 if (_img.left < canvas.left && _img.right < canvas.right) {
5078 _img.left = canvas.right - _img.width;
5079 }
5080 } else {
5081 if (_img.left < canvas.left) {
5082 _img.left = canvas.left;
5083 }
5084
5085 if (_img.right > canvas.right) {
5086 _img.left = canvas.right - _img.width;
5087 }
5088 }
5089
5090 if (_img.height > canvas.scaledHeight) {
5091 if (_img.bottom > canvas.bottom && _img.top > canvas.top) {
5092 _img.top = canvas.top;
5093 }
5094
5095 if (_img.top < canvas.top && _img.bottom < canvas.bottom) {
5096 _img.top = canvas.bottom - _img.height;
5097 }
5098 } else {
5099 if (_img.top < canvas.top) {
5100 _img.top = canvas.top;
5101 }
5102
5103 if (_img.bottom > canvas.bottom) {
5104 _img.top = canvas.bottom - _img.height;
5105 }
5106 }
5107
5108 onPositionChange();
5109 clrscr();
5110 ctx.drawImage(_img, _img.left, _img.top);
5111 };
5112
5113 var imgWheel = function imgWheel(e) {
5114 if (!imageField.classList.contains('focused')) {
5115 return;
5116 }
5117
5118 e.preventDefault();
5119
5120 if (_img.freeRotationMode) {
5121 //Свободное вращение
5122 if (e.deltaY > 0) {
5123 _img.rotation < 2 ? _img.rotation += imageViewerRotationStep : _img.rotation = imageViewerRotationStep;
5124 } else {
5125 _img.rotation > 0 ? _img.rotation -= imageViewerRotationStep : _img.rotation = 2 - imageViewerRotationStep;
5126 }
5127
5128 rotatePic();
5129 } else if (_img.freeHueMode) {
5130 //Свободный оттенок
5131 if (e.deltaY < 0) {
5132 _img.filters.hue <= 358 ? _img.filters.hue += 2 : _img.filters.hue = 2;
5133 } else {
5134 _img.filters.hue >= 2 ? _img.filters.hue -= 2 : _img.filters.hue = 358;
5135 }
5136
5137 imgLog("\u041E\u0442\u0442\u0435\u043D\u043E\u043A \u043F\u043E\u0432\u0451\u0440\u043D\u0443\u0442 \u043D\u0430 ".concat(_img.filters.hue, "\xB0"));
5138 applyFilters();
5139 } else if (_img.freeContrastMode) {
5140 //Свободная контрастность
5141 if (e.deltaY < 0) {
5142 _img.filters.contrast <= 199 ? _img.filters.contrast += 1 : _img.filters.contrast = 200;
5143 } else {
5144 _img.filters.contrast >= 1 ? _img.filters.contrast -= 1 : _img.filters.contrast = 0;
5145 }
5146
5147 imgLog("\u041A\u043E\u043D\u0442\u0440\u0430\u0441\u0442\u043D\u043E\u0441\u0442\u044C ".concat(_img.filters.contrast, "%"));
5148 applyFilters();
5149 } else if (_img.freeBrightnessMode) {
5150 //Свободная яркость
5151 if (e.deltaY < 0) {
5152 _img.filters.brightness <= 199 ? _img.filters.brightness += 1 : _img.filters.brightness = 200;
5153 } else {
5154 _img.filters.brightness >= 1 ? _img.filters.brightness -= 1 : _img.filters.brightness = 0;
5155 }
5156
5157 imgLog("\u042F\u0440\u043A\u043E\u0441\u0442\u044C ".concat(_img.filters.brightness, "%"));
5158 applyFilters();
5159 } else if (_img.freeSaturationMode) {
5160 //Свободная насыщенность
5161 if (e.deltaY < 0) {
5162 _img.filters.saturation <= 199 ? _img.filters.saturation += 1 : _img.filters.saturation = 200;
5163 } else {
5164 _img.filters.saturation >= 1 ? _img.filters.saturation -= 1 : _img.filters.saturation = 0;
5165 }
5166
5167 imgLog("\u041D\u0430\u0441\u044B\u0449\u0435\u043D\u043D\u043E\u0441\u0442\u044C ".concat(_img.filters.saturation, "%"));
5168 applyFilters();
5169 } else {
5170 var oldScale = canvas.scale;
5171
5172 if (e.deltaY > 0) {
5173 canvas.scale -= imageViewerScalingStep + canvas.scale / 10;
5174
5175 if (canvas.scale < canvas.minScale) {
5176 canvas.scale = canvas.minScale;
5177 }
5178 } else {
5179 canvas.scale += imageViewerScalingStep + canvas.scale / 10;
5180
5181 if (canvas.scale > 4) {
5182 canvas.scale = 4;
5183 }
5184 }
5185
5186 onScaleChange();
5187 positionCorrector();
5188 ctx.setTransform(canvas.scale, 0, 0, canvas.scale, canvas.halfWidth, canvas.halfHeight);
5189 clrscr();
5190 ctx.drawImage(_img, _img.left, _img.top);
5191 }
5192 };
5193
5194 var canvas = imageBlock.querySelector('canvas');
5195 var ctx = canvas.getContext("2d"); //Внутренний канвас с картинкой
5196
5197 var _img = document.createElement('canvas');
5198
5199 var imgCtx = _img.getContext("2d");
5200
5201 ctx.imageSmoothingEnabled = false;
5202 ctx.imageSmoothingEnabled = false;
5203 var pic = new Image();
5204 pic.src = imageSource;
5205 imgLog('Загружаю изображение...');
5206
5207 pic.onload = function () {
5208 imgLog('Изображение загружено!');
5209 var picMaxD = Math.max(pic.width, pic.height);
5210 var picDiagonal = Math.ceil(Math.sqrt(Math.pow(pic.width, 2) + Math.pow(pic.height, 2)));
5211 _img.width = pic.width;
5212 _img.height = pic.height;
5213 _img.halfWidth = _img.width / 2;
5214 _img.halfHeight = _img.height / 2;
5215 _img.scale = 1;
5216 pic.left = 0 - pic.width / 2;
5217 pic.top = 0 - pic.height / 2;
5218 imgCtx.setTransform(_img.scale, 0, 0, _img.scale, _img.halfWidth, _img.halfHeight);
5219 imgCtx.drawImage(pic, pic.left, pic.top);
5220 initCanvas();
5221 canvas.initCanvas = initCanvas;
5222 window.addEventListener('resize', initCanvas);
5223 var btnA;
5224 btnA = leftButts.querySelector('.expandButtIcon a');
5225
5226 if (btnA) {
5227 var _expandStuff = function _expandStuff(e) {
5228 e.preventDefault();
5229
5230 if (!imageField.classList.contains('expanded')) {}
5231
5232 var btn = _btnA2.closest('.btn');
5233
5234 if (!imageField.classList.contains('expanded')) {
5235 imageBlock.widthBeforeExpanding = getComputedStyle(imageBlock).width;
5236 imageBlock.heightBeforeExpanding = getComputedStyle(imageBlock).height;
5237 imageField.classList.add('expanded');
5238 btn.classList.add('selected');
5239 initCanvas();
5240 } else {
5241 btn.classList.remove('selected');
5242 imageBlock.style.width = imageBlock.widthBeforeExpanding;
5243 imageBlock.style.height = imageBlock.heightBeforeExpanding;
5244 imageField.classList.remove('expanded');
5245 initCanvas();
5246 setTimeout(function () {
5247 imageBlock.style.width = 'initial';
5248 imageBlock.style.height = 'initial';
5249 }, 100);
5250 }
5251 };
5252
5253 var _btnA2 = btnA;
5254 btnA.addEventListener('click', _expandStuff);
5255 imageBlock.addEventListener('dblclick', _expandStuff);
5256 } //Поворот влево на 90 градусов
5257
5258
5259 btnA = leftButts.querySelector('.undoButtIcon a');
5260
5261 if (btnA) {
5262 btnA.addEventListener('click', function (e) {
5263 e.preventDefault();
5264 _img.rotation = Math.ceil(_img.rotation * 2) / 2;
5265 _img.rotation > 0 ? _img.rotation -= 0.5 : _img.rotation = 1.5;
5266 canvas.savedScale = canvas.scale * _img.scale;
5267 rotatePic();
5268 imgLog("\u0423\u0433\u043E\u043B \u043F\u043E\u0432\u043E\u0440\u043E\u0442\u0430 \u0438\u0437\u043E\u0431\u0440\u0430\u0436\u0435\u043D\u0438\u044F: ".concat(_img.rotation * 180, "\xB0"));
5269 });
5270 } //Поворот вправо на 90 градусов
5271
5272
5273 btnA = leftButts.querySelector('.redoButtIcon a');
5274
5275 if (btnA) {
5276 btnA.addEventListener('click', function (e) {
5277 e.preventDefault();
5278 _img.rotation = Math.floor(_img.rotation * 2) / 2;
5279 _img.rotation < 2 ? _img.rotation += 0.5 : _img.rotation = 0.5;
5280 canvas.savedScale = canvas.scale * _img.scale;
5281 rotatePic();
5282 imgLog("\u0423\u0433\u043E\u043B \u043F\u043E\u0432\u043E\u0440\u043E\u0442\u0430 \u0438\u0437\u043E\u0431\u0440\u0430\u0436\u0435\u043D\u0438\u044F: ".concat(_img.rotation * 180, "\xB0"));
5283 });
5284 } //Кнопка свободного вращения
5285
5286
5287 var lastClickedTime;
5288 btnA = leftButts.querySelector('.syncButtIcon a');
5289
5290 if (btnA) {
5291 btnA.addEventListener('click', function (e) {
5292 e.preventDefault();
5293 var btn = this.closest('.btn');
5294
5295 if (!btn.classList.contains('selected')) {
5296 disableButts();
5297 btn.classList.add('selected');
5298 canvas.savedScale = canvas.scale * _img.scale;
5299 _img.freeRotationMode = true;
5300 } else {
5301 disableButts();
5302 } //Проверка на двойной клик
5303
5304
5305 if (new Date().getTime() <= lastClickedTime + imageViewerDoubleClickTime) {
5306 //Это был двойной клик, значит надо сбросить значение данного свойства
5307 _img.rotation = 0;
5308 rotatePic();
5309 btn.classList.remove('selected');
5310 _img.freeRotationMode = false;
5311 } //Запоминаем время включения для отслеживания двойного клика
5312
5313
5314 lastClickedTime = new Date().getTime();
5315 });
5316 } //Инверсия изображения
5317
5318
5319 btnA = leftButts.querySelector('.adjustSolidButtIcon a');
5320
5321 if (btnA) {
5322 btnA.addEventListener('click', function (e) {
5323 e.preventDefault();
5324 var btn = this.closest('.btn');
5325
5326 if (!btn.classList.contains('selected')) {
5327 btn.classList.add('selected');
5328 _img.filters.invert = 100;
5329 imgLog("\u0418\u0437\u043E\u0431\u0440\u0430\u0436\u0435\u043D\u0438\u0435 \u0438\u043D\u0432\u0435\u0440\u0442\u0438\u0440\u043E\u0432\u0430\u043D\u043E");
5330 } else {
5331 btn.classList.remove('selected');
5332 _img.filters.invert = 0;
5333 imgLog("\u0418\u0437\u043E\u0431\u0440\u0430\u0436\u0435\u043D\u0438\u0435 \u0440\u0435\u0432\u0435\u0440\u0442\u0438\u0440\u043E\u0432\u0430\u043D\u043E");
5334 }
5335
5336 applyFilters();
5337 });
5338 } //Оттенок изображения
5339
5340
5341 btnA = leftButts.querySelector('.swatchBookIcon a');
5342
5343 if (btnA) {
5344 btnA.addEventListener('click', function (e) {
5345 e.preventDefault();
5346 var btn = this.closest('.btn');
5347
5348 if (!btn.classList.contains('selected')) {
5349 disableButts();
5350 btn.classList.add('selected');
5351 _img.freeHueMode = true;
5352 } else {
5353 disableButts();
5354 } //Проверка на двойной клик
5355
5356
5357 if (new Date().getTime() <= lastClickedTime + imageViewerDoubleClickTime) {
5358 //Это был двойной клик, значит надо сбросить значение данного свойства
5359 btn.classList.remove('selected');
5360 _img.freeHueMode = false;
5361 _img.filters.hue = 0;
5362 applyFilters();
5363 imgLog("\u041E\u0442\u0442\u0435\u043D\u043E\u043A \u0432\u043E\u0441\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D");
5364 } //Запоминаем время включения для отслеживания двойного клика
5365
5366
5367 lastClickedTime = new Date().getTime();
5368 });
5369 } //Контрастность изображения
5370
5371
5372 btnA = leftButts.querySelector('.adjustButtIcon a');
5373
5374 if (btnA) {
5375 btnA.addEventListener('click', function (e) {
5376 e.preventDefault();
5377 var btn = this.closest('.btn');
5378
5379 if (!btn.classList.contains('selected')) {
5380 disableButts();
5381 btn.classList.add('selected');
5382 _img.freeContrastMode = true;
5383 } else {
5384 disableButts();
5385 } //Проверка на двойной клик
5386
5387
5388 if (new Date().getTime() <= lastClickedTime + imageViewerDoubleClickTime) {
5389 //Это был двойной клик, значит надо сбросить значение данного свойства
5390 btn.classList.remove('selected');
5391 _img.freeContrastMode = false;
5392 _img.filters.contrast = 100;
5393 applyFilters();
5394 imgLog("\u041A\u043E\u043D\u0442\u0440\u0430\u0441\u0442\u043D\u043E\u0441\u0442\u044C \u0432\u043E\u0441\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D\u0430");
5395 } //Запоминаем время включения для отслеживания двойного клика
5396
5397
5398 lastClickedTime = new Date().getTime();
5399 });
5400 } //Яркость изображения
5401
5402
5403 btnA = leftButts.querySelector('.sunButtIcon a');
5404
5405 if (btnA) {
5406 btnA.addEventListener('click', function (e) {
5407 e.preventDefault();
5408 var btn = this.closest('.btn');
5409
5410 if (!btn.classList.contains('selected')) {
5411 disableButts();
5412 btn.classList.add('selected');
5413 _img.freeBrightnessMode = true;
5414 } else {
5415 disableButts();
5416 } //Проверка на двойной клик
5417
5418
5419 if (new Date().getTime() <= lastClickedTime + imageViewerDoubleClickTime) {
5420 //Это был двойной клик, значит надо сбросить значение данного свойства
5421 btn.classList.remove('selected');
5422 _img.freeBrightnessMode = false;
5423 _img.filters.brightness = 100;
5424 applyFilters();
5425 imgLog("\u042F\u0440\u043A\u043E\u0441\u0442\u044C \u0432\u043E\u0441\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D\u0430");
5426 } //Запоминаем время включения для отслеживания двойного клика
5427
5428
5429 lastClickedTime = new Date().getTime();
5430 });
5431 } //Насыщенность изображения
5432
5433
5434 btnA = leftButts.querySelector('.paletteButtIcon a');
5435
5436 if (btnA) {
5437 btnA.addEventListener('click', function (e) {
5438 e.preventDefault();
5439 var btn = this.closest('.btn');
5440
5441 if (!btn.classList.contains('selected')) {
5442 disableButts();
5443 btn.classList.add('selected');
5444 _img.freeSaturationMode = true;
5445 } else {
5446 disableButts();
5447 } //Проверка на двойной клик
5448
5449
5450 if (new Date().getTime() <= lastClickedTime + imageViewerDoubleClickTime) {
5451 //Это был двойной клик, значит надо сбросить значение данного свойства
5452 btn.classList.remove('selected');
5453 _img.freeSaturationMode = false;
5454 _img.filters.saturation = 100;
5455 applyFilters();
5456 imgLog("\u041D\u0430\u0441\u044B\u0449\u0435\u043D\u043D\u043E\u0441\u0442\u044C \u0432\u043E\u0441\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D\u0430");
5457 } //Запоминаем время включения для отслеживания двойного клика
5458
5459
5460 lastClickedTime = new Date().getTime();
5461 });
5462 } //Стирание фильтров
5463
5464
5465 btnA = leftButts.querySelector('.eraserButtIcon a');
5466
5467 if (btnA) {
5468 btnA.addEventListener('click', function (e) {
5469 e.preventDefault();
5470 disableButts();
5471 _img.filters = {
5472 invert: 0,
5473 hue: 0,
5474 contrast: 100,
5475 brightness: 100,
5476 saturation: 100
5477 };
5478 applyFilters();
5479 imgLog("\u0412\u0441\u0435 \u0444\u0438\u043B\u044C\u0442\u0440\u044B \u0441\u0442\u0451\u0440\u0442\u044B");
5480 });
5481 }
5482 };
5483
5484 canvas.addEventListener('mousedown', imgDrag);
5485 canvas.addEventListener('mousemove', imgMove);
5486 canvas.addEventListener('mouseup', imgDrop);
5487 canvas.addEventListener('mouseleave', imgDrop);
5488 canvas.addEventListener('wheel', imgWheel);
5489 _img.filters = {
5490 invert: 0,
5491 hue: 0,
5492 contrast: 100,
5493 brightness: 100,
5494 saturation: 100
5495 };
5496 leftButtsJSON = {
5497 type: "actionButtonGroup",
5498 class: "imageFieldRightControls",
5499 flags: ["unfocusable"],
5500 buttons: [{
5501 iconClass: "expandButtIcon",
5502 hint: "Развернуть на весь экран.\nДвойной клик по изображению делает то же самое."
5503 }, {
5504 iconClass: "undoButtIcon",
5505 hint: "Повернуть изображение против часовой стрелки."
5506 }, {
5507 iconClass: "redoButtIcon",
5508 hint: "Повернуть изображение по часовой стрелке."
5509 }, {
5510 iconClass: "syncButtIcon",
5511 hint: "Режим свободного вращения колёсиком.\nДвойная активация сбрасывает поворот."
5512 }]
5513 };
5514
5515 if (!imageField.classList.contains('noFilters')) {
5516 leftButtsJSON.buttons.push({
5517 iconClass: "adjustSolidButtIcon",
5518 hint: "Инвертировать изображение"
5519 }, {
5520 iconClass: "swatchBookIcon",
5521 hint: "Оттенок изображения.\nДвойная активация сбрасывает фильтр."
5522 }, {
5523 iconClass: "adjustButtIcon",
5524 hint: "Контрастность изображения.\nДвойная активация сбрасывает фильтр."
5525 }, {
5526 iconClass: "sunButtIcon",
5527 hint: "Яркость изображения.\nДвойная активация сбрасывает фильтр."
5528 }, {
5529 iconClass: "paletteButtIcon",
5530 hint: "Насыщенность изображения.\nДвойная активация сбрасывает фильтр."
5531 }, {
5532 iconClass: "eraserButtIcon",
5533 hint: "Стереть все фильтры."
5534 });
5535 }
5536
5537 leftButts.innerHTML = handleFieldAndGetHTML(leftButtsJSON);
5538 }
5539
5540 var rightButtsJSON = {
5541 type: "linkButtonGroup",
5542 class: "imageFieldRightControls",
5543 flags: ["unfocusable"],
5544 buttons: [{
5545 link: imageOrigLink ? imageOrigLink : imageSource,
5546 hint: "Открыть изображение в новом окне.",
5547 caption: "Просто ссылка"
5548 }]
5549 };
5550
5551 if (!imageField.classList.contains('noSearchButtons')) {
5552 rightButtsJSON.buttons.push({
5553 type: "yandexSearchByPic",
5554 hint: "Искать похожие картинки в Яндексе.",
5555 query: imageOrigLink ? imageOrigLink : imageSource
5556 });
5557 rightButtsJSON.buttons.push({
5558 type: "googleSearchByPic",
5559 hint: "Искать похожие картинки в другом поисковике.",
5560 query: imageOrigLink ? imageOrigLink : imageSource
5561 });
5562 }
5563
5564 rightButts.innerHTML = handleFieldAndGetHTML(rightButtsJSON);
5565};
5566
5567window.isInteger = function (s) {
5568 return /^\d+$/.test(s);
5569};
5570
5571window.isFloat = function (s) {
5572 return /^\d+(\.\d+)?$/.test(s);
5573};
5574
5575window.isEmail = function (s) {
5576 return /^([a-z\u00a1-\uffff0-9_\-.])+@([a-z\u00a1-\uffff0-9_\-.])+\.([a-z\u00a1-\uffff]{2,6})$/i.test(s);
5577};
5578
5579window.isIP = function (s) {
5580 return /^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$/gm.test(s);
5581};
5582
5583window.isTelNumber = function (n) {
5584 return /^(\s*)?(\+)?([- _():=+]?\d[- _():=+]?){10,14}(\s*)?$/.test(n);
5585};
5586
5587window.isLink = function (zelda) {
5588 return /^(?:(?:https?):\/\/)(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,}))\.?)(?::\d{2,5})?(?:[/?#]\S*)?$/i.test(zelda); //zelda.substr(0,7)=='http://' || zelda.substr(0,8)=='https://';
5589};
5590
5591window.isServiceLink = function (zelda, hyrule) {
5592 switch (hyrule) {
5593 case 'vkontakte':
5594 return /^http(s)?:\/\/(www\.)?vk\.com\/.*$/.test(zelda);
5595
5596 case 'youtube':
5597 return /^http(s)?:\/\/(www\.)?youtu(be)?\.(com)|(be)\/.*$/.test(zelda);
5598
5599 case 'yadisk':
5600 return /^http(s)?:\/\/(www\.)?yadi\.sk\/.*$/.test(zelda);
5601
5602 case 'telegram':
5603 return /^http(s)?:\/\/(www\.)?t\.me\/.*$/.test(zelda);
5604
5605 case 'instagram':
5606 return /^http(s)?:\/\/(www\.)?instagram\.com\/.*$/.test(zelda);
5607
5608 case 'facebook':
5609 return /^http(s)?:\/\/(www\.)?facebook\.com\/.*$/.test(zelda);
5610
5611 case 'wikipedia':
5612 return /^http(s)?:\/\/(www\.)?((ru\.)|(en\.)|(by\.)|(simple\.))?wikipedia\.org\/.*$/.test(zelda);
5613
5614 case 'kinopoisk':
5615 return /^http(s)?:\/\/(www\.)?kinopoisk\.ru\/.*$/.test(zelda);
5616
5617 case 'yamusic':
5618 return /^https:\/\/music.yandex.ru\/album\/\d+\/track\/\d+/.test(zelda);
5619 }
5620};
5621
5622window.isYaDiskLink = function (zelda) {
5623 return zelda.substr(0, 15) === 'http://yadi.sk/' || zelda.substr(0, 16) === 'https://yadi.sk/';
5624};
5625
5626window.isDate = function (text, format) {
5627 switch (format) {
5628 case 'YYYY-MM-DD':
5629 return /^([012]\d{3})-(0[1-9]|1[012])-(0[1-9]|[12]\d|3[01])$/.test(text);
5630
5631 case 'DD-MM-YYYY':
5632 return /^(0[1-9]|[12]\d|3[01])-(0[1-9]|1[012])-([012]\d{3})$/.test(text);
5633
5634 case 'MM-DD-YYYY':
5635 return /^(0[1-9]|1[012])-(0[1-9]|[12]\d|3[01])-([012]\d{3})$/.test(text);
5636
5637 case 'YYYY.MM.DD':
5638 return /^([012]\d{3})\.(0[1-9]|1[012])\.(0[1-9]|[12]\d|3[01])$/.test(text);
5639
5640 case 'DD.MM.YYYY':
5641 return /^(0[1-9]|[12]\d|3[01])\.(0[1-9]|1[012])\.([012]\d{3})$/.test(text);
5642
5643 case 'MM.DD.YYYY':
5644 return /^(0[1-9]|1[012])\.(0[1-9]|[12]\d|3[01])\.([012]\d{3})$/.test(text);
5645 }
5646
5647 return false;
5648};
5649
5650window.skipProtocolParametersAndOtherStuff = function (s) {
5651 if (!s) {
5652 return s;
5653 }
5654
5655 return s.replace(/^(?:https?:\/\/)?(?:www\.)?/i, "").replace(/(?:\?.*)?(?:#.*)?$/i, "").replace(/^(youtube\.)(.*)(\/featured|\/about|\/channels|\/community|\/playlists|\/videos)$/i, '$1$2');
5656};
5657
5658window.isOptional = function (props) {
5659 //Проверяет, есть ли у поля флаг опциональности (для необязательных полей)
5660 return !!(props && props.flags && props.flags.includes('optional'));
5661};
5662
5663window.isActive = function (btn) {
5664 return btn.classList.contains('active') || btn.dataset.type === 'linkButton';
5665};
5666
5667window.isGroupInfinite = function (groupDiv) {
5668 return groupDiv.classList.contains('infinite');
5669};
5670
5671window.activateButton = function (btn) {
5672 btn.classList.add('active');
5673};
5674
5675window.deactivateButton = function (btn) {
5676 btn.classList.remove('active');
5677};
5678
5679window.invertButton = function (btn) {
5680 btn.classList.remove('clear');
5681
5682 if (isActive(btn)) {
5683 deactivateButton(btn);
5684 } else {
5685 activateButton(btn);
5686 }
5687};
5688
5689window.date2Unix = function (date, format) {
5690 var dateArr = date.split('-');
5691
5692 switch (format) {
5693 case 'YYYY-MM-DD':
5694 return new Date(dateArr[0] + '-' + dateArr[1] + '-' + dateArr[2]).getTime();
5695
5696 case 'DD-MM-YYYY':
5697 return new Date(dateArr[2] + '-' + dateArr[1] + '-' + dateArr[0]).getTime();
5698
5699 case 'MM-DD-YYYY':
5700 return new Date(dateArr[1] + '-' + dateArr[2] + '-' + dateArr[0]).getTime();
5701
5702 case 'YYYY.MM.DD':
5703 return new Date(dateArr[0] + '.' + dateArr[1] + '.' + dateArr[2]).getTime();
5704
5705 case 'DD.MM.YYYY':
5706 return new Date(dateArr[2] + '.' + dateArr[1] + '.' + dateArr[0]).getTime();
5707
5708 case 'MM.DD.YYYY':
5709 return new Date(dateArr[1] + '.' + dateArr[2] + '.' + dateArr[0]).getTime();
5710 }
5711};
5712
5713window.isFirstDateAheadOfSecond = function (date1, format1, date2, format2) {
5714 return date2Unix(date1, format1) > date2Unix(date2, format2);
5715};
5716
5717window.ce = function (error, additional) {
5718 //Выводит в консоль ошибку с определённым текстом
5719 console.error((templateErrorsJSON[error] ? templateErrorsJSON[error] : error) + (additional ? ' (' + additional + ')' : ''));
5720};
5721
5722window.cl = function () {
5723 console.log.apply(_this8, arguments);
5724};
5725
5726window.escapeHTML = function (text) {
5727 var map = {
5728 '&': '&',
5729 '<': '<',
5730 '>': '>',
5731 '"': '"',
5732 "'": '''
5733 };
5734 return text.replace(/[&<>"']/g, function (m) {
5735 return map[m];
5736 });
5737};
5738
5739window.localize = function (slug, noConsole) {
5740 if (!window.l10n) {
5741 if (!noConsole) {
5742 console.error('[Вы пытаетесть что-то перевести функцией localize, но window.l10n у вас не определён (например, вызовом Handlebars-хелпера setLocalization).]');
5743 }
5744
5745 return;
5746 }
5747
5748 if (!window.l10n[slug]) {
5749 if (!noConsole) {
5750 console.error('[В window.l10n нет строк локализации для "' + slug + '"!]');
5751 }
5752
5753 return;
5754 }
5755
5756 var localizedString = window.l10n[slug][window.lang];
5757
5758 if (!localizedString && localizedString !== '') {
5759 localizedString = window.l10n[slug].ru;
5760 }
5761
5762 if (!localizedString && localizedString !== '') {
5763 localizedString = window.l10n[slug].en;
5764 }
5765
5766 return localizedString;
5767};
5768
5769window.isDescendant = function (parent, child) {
5770 if (!parent || !child) {
5771 return false;
5772 }
5773
5774 var node = child.parentNode;
5775
5776 while (node != null) {
5777 if (node === parent) {
5778 return true;
5779 }
5780
5781 node = node.parentNode;
5782 }
5783
5784 return false;
5785};
5786
5787window.checkFilesExtensions = function (field, extensions) {
5788 var soWhat = true;
5789 Array.from(field.querySelectorAll('.file__name')).forEach(function (name) {
5790 var result = false;
5791 Array.from(extensions).forEach(function (ext) {
5792 if (new RegExp('\.' + ext + '$', 'i').test(name.innerHTML)) {
5793 result = true;
5794 }
5795 });
5796
5797 if (!result) {
5798 soWhat = false;
5799 name.classList.add('red');
5800 }
5801 });
5802 return soWhat;
5803};
5804
5805window.getInputFieldHTML = function (field, propsIndex) {
5806 var title = '';
5807
5808 if (field.hint) {
5809 title += '<div class="buttPath">Подсказка:</div>' + field.hint + '\n';
5810 }
5811
5812 if (field.properties && field.properties.examples) {
5813 title += '<div class="buttPath">Примеры:</div>' + field.properties.examples + '\n';
5814 } //Подсказка
5815
5816
5817 var help = '';
5818
5819 if (field.titleHelp) {
5820 help = "<div class=\"helpContainer\"><i class=\"ffIcon\" data-icon=\"help\"></i><div class=\"popUpTitle\" style=\"height: 0;\"><div class=\"buttPath\">\u041F\u043E\u0434\u0441\u043A\u0430\u0437\u043A\u0430:</div>".concat(field.titleHelp, "</div></div>");
5821 }
5822
5823 return '' + '<div class="element ' + flagsToClassName(field.flags) + ' ' + (field.class ? field.class + ' ' : '') + '">' + '<div class="elementTitle">' + '<div class="stepQuestion">' + (field.title ? field.title + (field.properties && field.properties.format ? ' (в формате ' + field.properties.format + ')' : '') + ' ' : '') + help + '</div>' + '</div>' + '<div class="elementBody fieldType_' + field.type + ' fieldName_' + field.name + '">' + '<input class="clear ' + (field.flags && field.flags.includes('alien') ? 'alien' : '') + (title ? 'cursorHelp ' : '') + '" data-type="' + field.type + '" data-name="' + field.name + '" ' + (field.solutionName ? "data-solutionname=\"".concat(field.solutionName, "\"") : '') + (propsIndex || propsIndex === 0 ? 'data-props="' + propsIndex + '"' : '') + ' type="text" ' + (field.placeholder ? 'placeholder="' + field.placeholder + '"' : '') + ' ' + (field.title && !title ? 'title="' + field.title + '"' : 'data-title=\'' + field.title.replace(/'/g, '\"') + '\'') + ' ' + (field.value ? 'value="' + field.value + '"' : '') + ' ' + (field.flags && field.flags.includes('disabled') ? 'disabled' : '') + (field.flags && field.flags.includes('readOnly') ? 'readonly' : '') + '>' + (title ? '<div class="popUpTitle">' + title + '</div>' : '') + '</div>' + '</div>';
5824};
5825
5826window.getTextareaFieldHTML = function (field, propsIndex) {
5827 var title = '';
5828
5829 if (field.hint) {
5830 title += '<div class="buttPath">Подсказка:</div>' + field.hint + '\n';
5831 }
5832
5833 if (field.properties && field.properties.examples) {
5834 title += '<div class="buttPath">Примеры:</div>' + field.properties.examples + '\n';
5835 } //Подсказка
5836
5837
5838 var help = '';
5839
5840 if (field.titleHelp) {
5841 help = "<div class=\"helpContainer\"><i class=\"ffIcon\" data-icon=\"help\"></i><div class=\"popUpTitle\" style=\"height: 0;\"><div class=\"buttPath\">\u041F\u043E\u0434\u0441\u043A\u0430\u0437\u043A\u0430:</div>".concat(field.titleHelp, "</div></div>");
5842 }
5843
5844 return '' + '<div class="element ' + flagsToClassName(field.flags) + (field.flags && field.flags.includes('selected') ? ' toBeClickedAtStart ' : '') + ' ' + (field.class ? field.class + ' ' : '') + '">' + '<div class="elementTitle">' + '<div class="stepQuestion">' + (field.title ? field.title + (field.properties && field.properties.format ? ' (в формате ' + field.properties.format + ')' : '') + ' ' : '') + help + '</div>' + '</div>' + '<div class="elementBody fieldType_' + field.type + ' fieldName_' + field.name + '">' + '<textarea class="clear ' + (field.flags && field.flags.includes('alien') ? ' alien ' : '') + (title ? 'cursorHelp ' : '') + '" data-type="' + field.type + '" data-name="' + field.name + '" ' + (field.solutionName ? "data-solutionname=\"".concat(field.solutionName, "\"") : '') + (propsIndex || propsIndex === 0 ? 'data-props="' + propsIndex + '"' : '') + ' type="text" ' + (field.placeholder ? 'placeholder="' + field.placeholder + '"' : '') + ' ' + (field.title && !title ? 'title="' + field.title + '"' : 'data-title=\'' + field.title.replace(/'/g, '\"') + '\'') + ' ' + (field.flags && field.flags.includes('disabled') ? 'disabled' : '') + (field.flags && field.flags.includes('readOnly') ? 'readonly' : '') + '>' + (field.value ? field.value : '') + '</textarea>' + (title ? '<div class="popUpTitle"' + title + '</div>' : '') + '</div>' + '</div>';
5845};
5846
5847window.getSwitchButtonHTML = function (field, propsIndex) {
5848 var includesString = ' includes';
5849
5850 if (propsIndex && fieldsPropsArray[propsIndex].includes) {
5851 Array.from(fieldsPropsArray[propsIndex].includes).forEach(function (name) {
5852 includesString += ' ' + name;
5853 });
5854 } else {
5855 includesString = '';
5856 }
5857
5858 if (field.flags && field.flags.includes('selected')) {
5859 includesString += ' toBeClickedAtStart';
5860 }
5861
5862 var title = '';
5863
5864 if (field.hint) {
5865 title += '<div class="popUpTitle"><div class="buttPath">Подсказка:</div>' + field.hint + '\n</div>';
5866 includesString += ' hasHint';
5867 }
5868
5869 var additional = '';
5870
5871 if (field.help) {
5872 additional += '<div class="helpContainer"><i class="ffIcon" data-icon="help"></i><div class="popUpTitle" style="height: 0;"><div class="buttPath">Помощь:</div>' + field.help + '\n</div></div>';
5873 includesString += ' hasHelp';
5874 }
5875
5876 if (!field.caption) {
5877 field.caption = field.name;
5878 }
5879
5880 return '<div class="buttDiv fieldName_' + field.name + (field.flags && field.flags.includes('inline') ? ' inline' : '') + '">' + '<div class="btn switchButton correct' + includesString + flagsToClassName(field.flags) + (propsIndex && fieldsPropsArray[propsIndex].excludes ? ' excludesSomething' : '') + '" data-name="' + field.name + '" data-type="switchButton"' + (propsIndex || propsIndex === 0 ? ' data-props="' + propsIndex + '"' : '') + (field.radiogroup ? ' data-radiogroup="' + field.radiogroup + '"' : '') + (field.select ? ' data-select="' + field.select + '"' : '') + (field.checkboxgroup ? ' data-checkboxgroup="' + field.checkboxgroup + '"' : '') + (field.groupSolutionName ? ' data-groupsolutionname="' + field.groupSolutionName + '"' : '') + '>' + "<i class=\"ffIcon\" data-icon=\"".concat(field.icon ? field.icon : 'done', "\"></i>") + '<a>' + field.caption + '</a>' + title + '</div>' + additional + '</div>';
5881};
5882
5883window.getLinkButtonHTML = function (field, propsIndex) {
5884 field.type = field.service;
5885
5886 if (!field.caption) {
5887 field.caption = '';
5888 }
5889
5890 var includesString = 'includes';
5891
5892 if (propsIndex && fieldsPropsArray[propsIndex].includes) {
5893 Array.from(fieldsPropsArray[propsIndex].includes).forEach(function (name) {
5894 includesString += ' ' + name;
5895 });
5896 } else {
5897 includesString = '';
5898 }
5899
5900 if (field.flags && field.flags.includes('selected')) {
5901 includesString += ' toBeClickedAtStart';
5902 }
5903
5904 if (field.flags && field.flags.includes('mustBeClicked')) {
5905 includesString += ' mustBeClicked';
5906 }
5907
5908 var title = '';
5909
5910 if (field.hint) {
5911 title += '<div class="buttPath">Подсказка:</div>' + field.hint + '\n';
5912 }
5913
5914 if (field.properties && field.properties.examples) {
5915 title += '<div class="buttPath">Примеры:</div>' + field.properties.examples + '\n';
5916 }
5917
5918 var href, iconClass, caption;
5919
5920 if (!field.type) {
5921 href = field.link;
5922 iconClass = field.iconClass ? field.iconClass : 'defaultButtIcon';
5923 caption = field.caption;
5924 } else if (linkButtonTypes[field.type]) {
5925 href = field.link ? field.link : linkButtonTypes[field.type].link;
5926 iconClass = field.iconClass ? field.iconClass : linkButtonTypes[field.type].iconClass;
5927 caption = field.caption ? field.caption : linkButtonTypes[field.type].caption;
5928
5929 if (field.query) {
5930 href += encodeURIComponent(field.query);
5931 }
5932
5933 if (linkButtonTypes[field.type].linkAfter) {
5934 href += linkButtonTypes[field.type].linkAfter;
5935 } else if (field.linkAfter) {
5936 href += field.linkAfter;
5937 }
5938 } else {
5939 ce('wrong_linkButtonGroup_button_type', 'type:' + field.type + ', caption:' + field.caption);
5940 }
5941
5942 return '<div class="buttDiv linkButton_' + field.type + (field.flags && field.flags.includes('inline') ? ' inline' : '') + '"><div class="btn linkButton ' + iconClass + ' ' + field.class + ' ' + field.name + ' ' + includesString + flagsToClassName(field.flags) + '" data-type="linkButton"' + (propsIndex || propsIndex === 0 ? ' data-props="' + propsIndex + '"' : '') + ' data-name="' + field.name + '"><span class="linkButtonIconContainer"><i></i></span><a ' + (href ? 'target="_blank" class="unclicked" href="' + href + '"' : '') + '>' + caption + '</a>' + (title ? '<div class="popUpTitle">' + title + '</div>' : '') + '</div></div>';
5943};
5944
5945window.getImageViewerHTML = function (field, propsIndex) {
5946 //Подсказка
5947 var help = '';
5948
5949 if (field.titleHelp) {
5950 help = "<div class=\"helpContainer\"><i class=\"ffIcon\" data-icon=\"help\"></i><div class=\"popUpTitle\" style=\"height: 0;\"><div class=\"buttPath\">\u041F\u043E\u0434\u0441\u043A\u0430\u0437\u043A\u0430:</div>".concat(field.titleHelp, "</div></div>");
5951 }
5952
5953 var output = '<div class="element imageField fieldName_' + field.name + flagsToClassName(field.flags) + '">' + (field.title ? '<div class="stepTitle">' + field.title + help + '</div>' : '') + '<div class="imageHeader">' + '<div class="imageSource">' + field.link + '</div>' + (field.origLink ? '<div class="imageOriginalLink">' + field.origLink + '</div>' : '') + '<div class="imageInfo"></div>' + '<div class="imageButts"></div>' + '</div>' + '<div class="imageBlock">' + '<canvas></canvas>' + '<div class="imageConsole"></div>' + '</div>' + '<div class="imageFooter">' + '<div class="imageLeftButts"></div>' + '<div class="imageRightButts"></div>' + '</div>' + '</div>';
5954 return output;
5955};
5956
5957window.getFileFieldHTML = function (field, propsIndex) {
5958 //Подсказка
5959 var help = '';
5960
5961 if (field.titleHelp) {
5962 help = "<div class=\"helpContainer\"><i class=\"ffIcon\" data-icon=\"help\"></i><div class=\"popUpTitle\" style=\"height: 0;\"><div class=\"buttPath\">\u041F\u043E\u0434\u0441\u043A\u0430\u0437\u043A\u0430:</div>".concat(field.titleHelp, "</div></div>");
5963 }
5964
5965 var output = "<div class=\"element fileField fieldName_".concat(field.name, " ").concat(flagsToClassName(field.flags), "\" ").concat(field.caption ? "data-caption=\"".concat(field.caption, "\"") : '', "\n ").concat(propsIndex ? "data-props=\"".concat(propsIndex, "\"") : '', "\n ").concat(field.name ? "data-name=\"".concat(field.name, "\"") : '', "\n >\n <div class=\"stepTitle title\">").concat(field.title || '', " ").concat(help, "</div>\n <div class=\"fContainer\"></div>\n </div>");
5966 return output;
5967};
5968
5969window.flagsToClassName = function (flags) {
5970 if (!flags || !Array.isArray(flags)) {
5971 return '';
5972 }
5973
5974 function maybeRename(flag) {
5975 switch (flag) {
5976 case 'block':
5977 return '';
5978
5979 case 'oldSchool':
5980 return 'asButtons';
5981
5982 case 'focused':
5983 return 'focusMeOnStart';
5984
5985 default:
5986 return flag;
5987 }
5988 }
5989
5990 var s = ' ';
5991
5992 var _iterator52 = _createForOfIteratorHelper(flags),
5993 _step52;
5994
5995 try {
5996 for (_iterator52.s(); !(_step52 = _iterator52.n()).done;) {
5997 var flag = _step52.value;
5998 s += maybeRename(flag) + ' ';
5999 }
6000 } catch (err) {
6001 _iterator52.e(err);
6002 } finally {
6003 _iterator52.f();
6004 }
6005
6006 return s;
6007}; //Функция получает на вход поле из входной спеки, обрабатывает его и возвращает его HTML код
6008
6009
6010window.handleFieldAndGetHTML = function (field, handlebarsContext) {
6011 //Если у поля не прописаны обязательные поля - забиваем
6012 if (!field.type) {
6013 return;
6014 }
6015
6016 if (!field.title) {
6017 field.title = '';
6018 }
6019
6020 if (!handlebarsContext) {
6021 if (_this8 && _this8.getTask) {
6022 handlebarsContext = _this8.getTask().input_values;
6023 } else {
6024 handlebarsContext = window.handlebarsContext;
6025 }
6026 } //Проверка полей на операторы, плейсхолдеры и т.д.
6027
6028
6029 for (var p in field) {
6030 if (typeof field[p] != "string") {
6031 continue;
6032 }
6033
6034 var split = field[p].split('|||');
6035
6036 if (split[1]) {
6037 //Похоже, что поле с оператором
6038 switch (split[0]) {
6039 case 'INPUT':
6040 //В сплит3 вместо ▼ надо будет подставить значение входного поля сплит2
6041 var input = window.currentRenderingTask.getTask().input_values;
6042
6043 if (!split[1] || !split[2]) {
6044 ce('output_placeholder_input_insufficient_parameters', 'type:' + field.type + ', title:' + field.title + ', caption:' + field.caption);
6045 break;
6046 }
6047
6048 if (!input[split[1]] || typeof input[split[1]] === "string" && input[split[1]].length === 0) {
6049 if (field.default && p !== 'default') {
6050 field[p] = field.default;
6051 } else {
6052 return '';
6053 }
6054 } else {
6055 field[p] = split[2].replace('▼', input[split[1]]).replace('▼', input[split[1]]);
6056 }
6057
6058 break;
6059
6060 case 'somethingOther':
6061 break;
6062
6063 default:
6064 ce('output_placeholder_not_found', 'type:' + field.type + ', title:' + field.title + ', caption:' + field.caption);
6065 break;
6066 }
6067 }
6068 } //Локализация [v1.18+]
6069
6070
6071 for (var _p in field) {
6072 if (_typeof(field[_p]) == 'object' && (field[_p].ru || field[_p].en)) {
6073 field[_p] = field[_p][window.lang];
6074 }
6075 } //Компилируем Handlebars в свойствах поля
6076
6077
6078 function compileStringsHere(where) {
6079 for (var _p2 in where) {
6080 if (!where[_p2]) {//continue;
6081 } else if (typeof where[_p2] === 'string') {
6082 where[_p2] = Handlebars.compile(where[_p2])(handlebarsContext);
6083 } else if (_typeof(where[_p2]) === 'object') {
6084 compileStringsHere(where[_p2]);
6085 }
6086 /*if (Array.isArray(where[p])) {
6087 for (const e in where[p]) {
6088 where[p][e] = Handlebars.compile(where[p][e])(handlebarsContext);
6089 }
6090 } else
6091 if (where[p] instanceof Object) {
6092 compileStringsHere(where[p]);
6093 }*/
6094
6095 }
6096 }
6097
6098 if (handlebarsContext) {
6099 compileStringsHere(field);
6100 } //Если у поля есть массив properties - нам надо добавить его в fieldsPropsArray и в dataset поля добавить ссылку, т.к. потом, очевидно, пригодятся
6101
6102
6103 var propsIndex = null;
6104
6105 if (field.properties) {
6106 propsIndex = fieldsPropsArray.length;
6107 fieldsPropsArray.push(field.properties);
6108 } //Определимся с типом поля, т.к. от этого будут зависеть наши дальнейшие действия
6109
6110
6111 var groupHTML, help;
6112
6113 switch (field.type) {
6114 case 'date': //Поле со вводом даты. Обычный инпут.
6115
6116 case 'text': //Поле со вводом текста. Обычный инпут.
6117
6118 case 'email': //Поле со вводом мыла. Обычный инпут.
6119
6120 case 'link': //Поле со вводом ссылки. Обычный инпут.
6121
6122 case 'integer': //Поле со вводом целого числа. Обычный инпут.
6123
6124 case 'float':
6125 //Поле со вводом дробного числа. Обычный инпут.
6126 return getInputFieldHTML(field, propsIndex);
6127
6128 case 'textarea':
6129 return getTextareaFieldHTML(field, propsIndex);
6130
6131 case 'switchButton':
6132 return getSwitchButtonHTML(field, propsIndex);
6133
6134 case 'linkButton':
6135 if (field.fieldType === 'linkButtonGroup' && !field.service && !field.caption) {
6136 return '';
6137 } //Если надо вывести кнопки только для определённых языков, например
6138
6139
6140 return getLinkButtonHTML(field, propsIndex);
6141
6142 case 'image':
6143 return getImageViewerHTML(field, propsIndex);
6144
6145 case 'file':
6146 return getFileFieldHTML(field, propsIndex);
6147
6148 case 'title':
6149 return '<div class="element unfocusable ' + (field.class ? field.class : '') + '"><div class="elementTitle"><div class="title">' + field.caption + '</div></div></div>';
6150
6151 case 'title2':
6152 return '<div class="element unfocusable ' + (field.class ? field.class : '') + '"><div class="elementTitle"><div class="stepQuestion title">' + field.caption + '</div></div></div>';
6153
6154 case 'label':
6155 return '<div class="element unfocusable ' + (field.class ? field.class : '') + '">' + (field.title ? '<div class="elementTitle"><div class="stepTitle labelTitle">' + field.title + '</div></div>' : '') + '<div class="elementBody"><div class="label caption ' + (field.name ? field.name : '') + '">' + field.caption + '</div></div></div>';
6156
6157 case 'clickableLink':
6158 if (!field.caption) {
6159 field.caption = decodeURIComponent(field.link.replace(/\+/g, ' '));
6160 }
6161
6162 return '<div class="element unfocusable">' + (field.title ? '<div class="elementTitle"><div class="stepTitle clickableLinkTitle">' + field.title + ':</div></div>' : '') + '<div class="elementBody"><div class="link ' + (field.name ? field.name : '') + '"><a href="' + field.link + '" target="_blank"><i class="material-icons">open_in_new</i>' + field.caption + '</a></div></div></div>';
6163
6164 case 'radioGroup':
6165 case 'checkboxGroup':
6166 case 'buttonGroup':
6167 //Подсказка
6168 help = '';
6169
6170 if (field.titleHelp) {
6171 help = "<div class=\"helpContainer\"><i class=\"ffIcon\" data-icon=\"help\"></i><div class=\"popUpTitle\" style=\"height: 0;\"><div class=\"buttPath\">\u041F\u043E\u0434\u0441\u043A\u0430\u0437\u043A\u0430:</div>".concat(field.titleHelp, "</div></div>");
6172 }
6173
6174 groupHTML = field.title ? '<div class="stepTitle title">' + field.title + help + '</div>' : '';
6175 Array.from(field.buttons).forEach(function (button) {
6176 if (!field.name && field.type !== 'buttonGroup') {
6177 ce('no_field_name', 'type:' + field.type + ', title:' + field.title);
6178 return;
6179 }
6180
6181 if (!button.name) {
6182 ce('no_button_name', 'field type:' + field.type + ', field title:' + field.title + ', button caption:' + field.caption);
6183 return;
6184 }
6185
6186 button.type = 'switchButton';
6187 button[field.type.toLowerCase()] = field.name;
6188
6189 if (field.solutionName) {
6190 button['groupSolutionName'] = field.solutionName;
6191 }
6192
6193 if (!button.flags) {
6194 button.flags = [];
6195 }
6196
6197 if (field.flags && field.flags.includes('block')) {
6198 button.flags.push('block');
6199 }
6200
6201 if (!button.flags.includes('inline') && !button.flags.includes('block')) {
6202 button.flags.push('inline');
6203 }
6204
6205 groupHTML += handleFieldAndGetHTML(button);
6206 });
6207
6208 if (!field.title) {
6209 field.title = '';
6210 }
6211
6212 return "<div class=\"".concat(field.type, " element fieldName_").concat(field.name, " ").concat(field.name, " ").concat(flagsToClassName(field.flags), "\"\n data-name=\"").concat(field.name, "\"\n ").concat(field.solutionName ? "data-solutionname=\"".concat(field.solutionName, "\"") : '', "\n data-title='").concat(field.title.replace(/'/g, '\"'), "'\n >").concat(groupHTML, "</div>");
6213
6214 case 'select':
6215 groupHTML = '';
6216 Array.from(field.buttons).forEach(function (button) {
6217 if (!field.name && field.type !== 'buttonGroup') {
6218 ce('no_field_name', 'type:' + field.type + ', title:' + field.title);
6219 return;
6220 }
6221
6222 if (!button.name) {
6223 ce('no_button_name', 'field type:' + field.type + ', field title:' + field.title + ', button caption:' + field.caption);
6224 return;
6225 }
6226
6227 button.type = 'switchButton';
6228 button[field.type.toLowerCase()] = field.name;
6229
6230 if (field.solutionName) {
6231 button['groupSolutionName'] = field.solutionName;
6232 }
6233
6234 if (!button.flags) {
6235 button.flags = [];
6236 }
6237
6238 if (field.flags && field.flags.includes('block')) {
6239 button.flags.push('block');
6240 }
6241
6242 if (!button.flags.includes('inline') && !button.flags.includes('block')) {
6243 button.flags.push('inline');
6244 }
6245
6246 groupHTML += handleFieldAndGetHTML(button);
6247 });
6248
6249 if (!field.title) {
6250 field.title = '';
6251 }
6252
6253 var includesString,
6254 title = '';
6255
6256 if (field.hint) {
6257 title += '<div class="popUpTitle"><div class="buttPath">Подсказка:</div>' + field.hint + '\n</div>';
6258 includesString += ' hasHint';
6259 }
6260
6261 var additional = '';
6262
6263 if (field.help) {
6264 additional += '<div class="helpContainer"><i class="ffIcon" data-icon="help"></i><div class="popUpTitle" style="height: 0;"><div class="buttPath">Помощь:</div>' + field.help + '\n</div></div>';
6265 includesString += ' hasHelp';
6266 }
6267
6268 return '<div class="' + field.type + ' element fieldName_' + field.name + ' ' + field.name + ' ' + flagsToClassName(field.flags) + includesString + '" style="' + (field.width ? " width: ".concat(field.width, ";") : '') + '" data-name="' + field.name + '"' + (field.solutionName ? "data-solutionname=\"".concat(field.solutionName, "\"") : '') + ' data-title=\'' + field.title.replace(/'/g, '\"') + '\'>' + (field.title ? '<div class="stepTitle title">' + field.title + additional + '</div>' : '') + '<div class="selectHead">' + '<div class="selectDisplay">' + '<div class="selectText">' + '</div>' + '</div>' + '<div class="selectButtons">' + '<i class="ffIcon empty" data-icon="times"></i>' + '<i class="ffIcon arrow" data-icon="angle_down"></i>' + '</div>' + title + '</div>' + '<div class="selectContainer">' + groupHTML + '</div>' + '</div>';
6269
6270 case 'group':
6271 //Генерируем HTML всей внутрянки этой группы
6272 groupHTML = '';
6273 Array.from(field.groupFields).forEach(function (groupField) {
6274 groupHTML += handleFieldAndGetHTML(groupField);
6275 }); //Записываем её в наш массив свойств
6276
6277 propsIndex = fieldsPropsArray.length;
6278 fieldsPropsArray.push(groupHTML); //Возвращаем обёртку группы со всеми нужными параметрами. Внутрянку будем добавлять/удалять по мере необходимости
6279
6280 return "<div class=\"groupContainer fieldName_".concat(field.name, " ") + field.name + (field.flags && field.flags.includes('excluded') ? ' excluded' : '') + (field.flags && field.flags.includes('inline') ? ' inline' : '') + (field.flags && field.flags.includes('infinite') ? ' infinite' : '') + "\" data-name=\"".concat(field.name, "\" data-codepropindex=\"").concat(propsIndex, "\"") + (field.limit ? " data-limit=\"".concat(field.limit, "\"") : '') + (field.flags && field.flags.includes('excluded') ? ' data-excluded="excluded"' : '') + '></div>';
6281
6282 case 'actionButtonGroup':
6283 case 'linkButtonGroup':
6284 //Подсказка
6285 help = '';
6286
6287 if (field.titleHelp) {
6288 help = "<div class=\"helpContainer\"><i class=\"ffIcon\" data-icon=\"help\"></i><div class=\"popUpTitle\" style=\"height: 0;\"><div class=\"buttPath\">\u041F\u043E\u0434\u0441\u043A\u0430\u0437\u043A\u0430:</div>".concat(field.titleHelp, "</div></div>");
6289 }
6290
6291 groupHTML = field.title ? '<div class="stepTitle title">' + field.title + help + '</div>' : '';
6292
6293 if (!field.title) {
6294 field.title = '';
6295 }
6296
6297 if (!field.name) {
6298 field.name = '';
6299 }
6300
6301 if (!field.class) {
6302 field.class = field.name;
6303 }
6304
6305 Array.from(field.buttons).forEach(function (button) {
6306 button.service = button.type;
6307 button.type = 'linkButton';
6308 button.fieldType = field.type;
6309 button[field.type.toLowerCase()] = field.class;
6310
6311 if (field.type === 'actionButtonGroup') {
6312 button.link = '';
6313
6314 if (!button.iconClass) {
6315 button.iconClass = 'actionButtonNoIcon';
6316 }
6317 }
6318
6319 if (!button.flags) {
6320 button.flags = [];
6321 }
6322
6323 if (field.flags && field.flags.includes("block")) {
6324 button.flags.push("block");
6325 }
6326
6327 if (!button.name) {
6328 button.name = field.name;
6329 }
6330
6331 if (!button.class) {
6332 button.class = field.class;
6333 }
6334
6335 if (!button.flags.includes('inline') && !button.flags.includes('block')) {
6336 button.flags.push('inline');
6337 }
6338
6339 groupHTML += handleFieldAndGetHTML(button);
6340 });
6341 return '<div class="' + field.type + ' element ' + field.name + ' alien' + (field.flags && field.flags.includes('focused') ? ' focusMeOnStart' : '') + (field.flags && field.flags.includes('unfocusable') ? ' unfocusable ' : ' ') + field.class + '" data-name="' + field.name + '" data-title=\'' + field.title.replace(/'/g, '\"') + '\'>' + groupHTML + '</div>';
6342
6343 case 'view':
6344 return Handlebars.compile("\n {{#v name=\"".concat(field.name, "\" class=\"").concat(field.class ? field.class : '', " ").concat(field.flags && field.flags.includes('hidden') ? 'hidden' : '', "\"}}\n ").concat(field.content, "\n {{/v}}\n "))(handlebarsContext);
6345
6346 case 'null':
6347 return '';
6348 }
6349
6350 ce('wrong_field_type', 'type:' + field.type + ', title:' + field.title + ', caption:' + field.caption);
6351};
6352
6353window.getFieldsHTML = function (fields, context) {
6354 var fieldsName = '';
6355
6356 if (!context || !context[fields]) {
6357 if (_this8 && _this8.getTask) {
6358 context = _this8.getTask().input_values;
6359 } else {
6360 context = window.handlebarsContext;
6361 }
6362 }
6363
6364 if (fields && typeof fields === "string") {
6365 fieldsName = fields;
6366 fields = context[fieldsName];
6367 }
6368
6369 if (!fields || !Array.isArray(fields)) {
6370 console.log(context);
6371 ce('Что-то неправильное передано хелперу drawFields: ', [fieldsName, fields ? fields : '[пустота]']);
6372 return;
6373 }
6374
6375 var out = '';
6376 Array.from(fields).forEach(function (field) {
6377 out += handleFieldAndGetHTML.apply(this, [field, context]);
6378 });
6379
6380 if (fieldsName) {
6381 out = "<div class=\"theOneFieldsContainer fieldsName_".concat(fieldsName, "\" data-name=\"").concat(fieldsName, "\">").concat(out, "</div>");
6382 }
6383
6384 return out;
6385};
6386/* Всякие служебные переменные и JSONки */
6387
6388
6389window.specObjectNameOfTheField = [];
6390window.fieldsPropsArray = []; //Настройки шаблона, работающие через CSS (если какая-то из них false, то в класс сьюта добавится "no_[имя настройки]", например "no_taskList")
6391
6392window.cssNoClassSettings = ['taskSwitcher', 'taskCounter', 'taskList', 'taskMinimizer', 'notForDirect'];
6393window.cssClassSettings = ['fullScreenMode', 'verticalSuite']; //СВГшные иконки, доступные к использованию с классом ffIcon (в data-icon)
6394
6395window.svgIcons = {
6396 "keyboard_arrow_up": {
6397 "attributes": 'viewBox="0 0 320 512"',
6398 "path": "M177 159.7l136 136c9.4 9.4 9.4 24.6 0 33.9l-22.6 22.6c-9.4 9.4-24.6 9.4-33.9 0L160 255.9l-96.4 96.4c-9.4 9.4-24.6 9.4-33.9 0L7 329.7c-9.4-9.4-9.4-24.6 0-33.9l136-136c9.4-9.5 24.6-9.5 34-.1z"
6399 },
6400 "keyboard_tab": {
6401 "attributes": 'viewBox="0 0 448 512"',
6402 "path": "M448 88v336c0 13.3-10.7 24-24 24h-24c-13.3 0-24-10.7-24-24V88c0-13.3 10.7-24 24-24h24c13.3 0 24 10.7 24 24zm-280.5 66.4l65.6 65.6H24c-13.3 0-24 10.7-24 24v24c0 13.3 10.7 24 24 24h209.1l-65.6 65.6c-9.4 9.4-9.4 24.6 0 33.9l17 17c9.4 9.4 24.6 9.4 33.9 0L353.9 273c9.4-9.4 9.4-24.6 0-33.9L218.4 103.5c-9.4-9.4-24.6-9.4-33.9 0l-17 17c-9.4 9.4-9.4 24.6 0 33.9z"
6403 },
6404 "keyboard_backspace": {
6405 "attributes": 'viewBox="0 0 448 512"',
6406 "path": "M257.5 445.1l-22.2 22.2c-9.4 9.4-24.6 9.4-33.9 0L7 273c-9.4-9.4-9.4-24.6 0-33.9L201.4 44.7c9.4-9.4 24.6-9.4 33.9 0l22.2 22.2c9.5 9.5 9.3 25-.4 34.3L136.6 216H424c13.3 0 24 10.7 24 24v32c0 13.3-10.7 24-24 24H136.6l120.5 114.8c9.8 9.3 10 24.8.4 34.3z"
6407 },
6408 "invert_colors": {
6409 "attributes": 'viewBox="0 0 512 512"',
6410 "path": "M8 256c0 136.966 111.033 248 248 248s248-111.034 248-248S392.966 8 256 8 8 119.033 8 256zm248 184V72c101.705 0 184 82.311 184 184 0 101.705-82.311 184-184 184z"
6411 },
6412 "open_in_new": {
6413 "attributes": 'viewBox="0 0 576 512"',
6414 "path": "M448 279.196V464c0 26.51-21.49 48-48 48H48c-26.51 0-48-21.49-48-48V112c0-26.51 21.49-48 48-48h248a24 24 0 0 1 16.97 7.029l16 16C344.09 102.149 333.382 128 312 128H64v320h320V295.196c0-6.365 2.529-12.47 7.029-16.971l16-16C422.148 247.106 448 257.814 448 279.196zM576 37.333C576 16.715 559.285 0 538.667 0H380c-15.464 0-28 12.536-28 28v17.885c0 15.766 13.011 28.424 28.772 27.989l67.203-1.906L199.09 319.09c-9.429 9.363-9.457 24.605-.061 34.001l23.879 23.879c9.396 9.396 24.639 9.369 34.001-.06l247.122-248.885-1.906 67.203c-.434 15.76 12.224 28.772 27.99 28.772H548c15.464 0 28-12.536 28-28V37.333z"
6415 },
6416 "done": {
6417 "attributes": 'viewBox="0 0 512 512"',
6418 "path": "M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z"
6419 },
6420 "angle_down": {
6421 "attributes": 'viewBox="0 0 320 512"',
6422 "path": "M143 352.3L7 216.3c-9.4-9.4-9.4-24.6 0-33.9l22.6-22.6c9.4-9.4 24.6-9.4 33.9 0l96.4 96.4 96.4-96.4c9.4-9.4 24.6-9.4 33.9 0l22.6 22.6c9.4 9.4 9.4 24.6 0 33.9l-136 136c-9.2 9.4-24.4 9.4-33.8 0z"
6423 },
6424 "times": {
6425 "attributes": 'viewBox="0 0 352 512"',
6426 "path": "M242.72 256l100.07-100.07c12.28-12.28 12.28-32.19 0-44.48l-22.24-22.24c-12.28-12.28-32.19-12.28-44.48 0L176 189.28 75.93 89.21c-12.28-12.28-32.19-12.28-44.48 0L9.21 111.45c-12.28 12.28-12.28 32.19 0 44.48L109.28 256 9.21 356.07c-12.28 12.28-12.28 32.19 0 44.48l22.24 22.24c12.28 12.28 32.2 12.28 44.48 0L176 322.72l100.07 100.07c12.28 12.28 32.2 12.28 44.48 0l22.24-22.24c12.28-12.28 12.28-32.19 0-44.48L242.72 256z"
6427 },
6428 "eye": {
6429 "attributes": 'viewBox="0 0 576 512"',
6430 "path": "M572.52 241.4C518.29 135.59 410.93 64 288 64S57.68 135.64 3.48 241.41a32.35 32.35 0 0 0 0 29.19C57.71 376.41 165.07 448 288 448s230.32-71.64 284.52-177.41a32.35 32.35 0 0 0 0-29.19zM288 400a144 144 0 1 1 144-144 143.93 143.93 0 0 1-144 144zm0-240a95.31 95.31 0 0 0-25.31 3.79 47.85 47.85 0 0 1-66.9 66.9A95.78 95.78 0 1 0 288 160z"
6431 },
6432 "comment_smile": {
6433 "attributes": 'viewBox="0 0 512 512"',
6434 "path": "M256 32C114.6 32 0 125.1 0 240c0 49.6 21.4 95 57 130.7C44.5 421.1 2.7 466 2.2 466.5c-2.2 2.3-2.8 5.7-1.5 8.7S4.8 480 8 480c66.3 0 116-31.8 140.6-51.4 32.7 12.3 69 19.4 107.4 19.4 141.4 0 256-93.1 256-208S397.4 32 256 32zm64 133.2c14.8 0 26.8 12 26.8 26.8s-12 26.8-26.8 26.8-26.8-12-26.8-26.8 12-26.8 26.8-26.8zm-128 0c14.8 0 26.8 12 26.8 26.8s-12 26.8-26.8 26.8-26.8-12-26.8-26.8 12-26.8 26.8-26.8zm164.2 140.9C331.3 335.3 294.8 352 256 352c-38.8 0-75.3-16.7-100.2-45.9-5.8-6.7-5-16.8 1.8-22.5 6.7-5.7 16.8-5 22.5 1.8 18.8 22 46.5 34.6 75.8 34.6 29.4 0 57-12.6 75.8-34.7 5.8-6.7 15.9-7.5 22.6-1.8 6.8 5.8 7.6 15.9 1.9 22.6z"
6435 },
6436 "help": {
6437 "attributes": 'viewBox="0 0 448 512"',
6438 "path": "M224 202a42 42 0 1 0-42-42 42 42 0 0 0 42 42zm44 134h-12V236a12 12 0 0 0-12-12h-64a12 12 0 0 0-12 12v24a12 12 0 0 0 12 12h12v64h-12a12 12 0 0 0-12 12v24a12 12 0 0 0 12 12h88a12 12 0 0 0 12-12v-24a12 12 0 0 0-12-12z",
6439 "translucentPath": "M400 32H48A48 48 0 0 0 0 80v352a48 48 0 0 0 48 48h352a48 48 0 0 0 48-48V80a48 48 0 0 0-48-48zm-176 86a42 42 0 1 1-42 42 42 42 0 0 1 42-42zm56 254a12 12 0 0 1-12 12h-88a12 12 0 0 1-12-12v-24a12 12 0 0 1 12-12h12v-64h-12a12 12 0 0 1-12-12v-24a12 12 0 0 1 12-12h64a12 12 0 0 1 12 12v100h12a12 12 0 0 1 12 12z"
6440 },
6441 "helpRound": {
6442 "attributes": 'viewBox="0 0 512 512"',
6443 "path": "M504 256c0 136.997-111.043 248-248 248S8 392.997 8 256C8 119.083 119.043 8 256 8s248 111.083 248 248zM262.655 90c-54.497 0-89.255 22.957-116.549 63.758-3.536 5.286-2.353 12.415 2.715 16.258l34.699 26.31c5.205 3.947 12.621 3.008 16.665-2.122 17.864-22.658 30.113-35.797 57.303-35.797 20.429 0 45.698 13.148 45.698 32.958 0 14.976-12.363 22.667-32.534 33.976C247.128 238.528 216 254.941 216 296v4c0 6.627 5.373 12 12 12h56c6.627 0 12-5.373 12-12v-1.333c0-28.462 83.186-29.647 83.186-106.667 0-58.002-60.165-102-116.531-102zM256 338c-25.365 0-46 20.635-46 46 0 25.364 20.635 46 46 46s46-20.636 46-46c0-25.365-20.635-46-46-46z"
6444 }
6445}; //Возможные выводимые в консоль ошибки в шаблоне, для упрощения отладки и устранения проблем со входным JSON
6446
6447window.templateErrorsJSON = {
6448 "output_placeholder_not_found": 'Судя по всему, вы попытались использовать операторы в одном из полей JSONки - найдено сочетание "|||" Но вот имя оператора мне не понятно. Если вы ничего такого делать не собирались - пожалуйста, не используйте "|||" в JSONке.',
6449 "output_placeholder_input_insufficient_parameters": 'Недостаточно параметров у оператора в JSON. У оператора INPUT три параметра - сам оператора, имя входного поля и текст, в который вместо плейсхолдера "▼" (Alt+31) будет подставлено значение этого поля.',
6450 "wrong_field_type": 'Неверный тип у поля, вместо этого выведено "undefined"',
6451 "wrong_linkButtonGroup_button_type": 'Неверный тип у кнопки в linkButtonGroup, она не выведена.',
6452 "no_field_name": 'У поля отсутствует свойство name!',
6453 "no_button_name": 'У кнопки отсутствует свойство name!',
6454 "valuesToWrite_is_not_an_array": 'Свойство valuesToWrite не является массивом объектов!',
6455 "valuesToWrite_has_no_selector": 'У одного из объектов массива valuesToWrite отсутствует свойство "selector". Укажите его, чтобы было понятно, в свойство property элемента по какому селектору вставлять значение value.',
6456 "valuesToWrite_has_no_property": 'У одного из объектов массива valuesToWrite отсутствует свойство "property". Укажите его, чтобы было понятно, какое свойство заменять у указанного элемента с селектором selector (например, "value", если это input или textarea или "innerHTML" если это div или span).',
6457 "valuesToWrite_has_no_value": 'У одного из объектов массива valuesToWrite отсутствует свойство "value". Укажите его, чтобы было понятно, какое именно значение вставлять в свойство property элемента с селектором selector.',
6458 "valuesToWrite_target_not_found": 'Только что нажатая кнопка должна была прописать значение в свойство элемента по определённому селектору (свойство valuesToWrite во входном JSON), но нужный элемент по указанному селектору найти так и не удалось... ',
6459 "specmapper_conversion_type_not_found": 'Указанного типа конверсии в правиле specMapper`а не существует.',
6460 "specmapper_conversion_no_function": 'В specMapper`е указан тип конверсии - функция, но не указана сама функция.'
6461}; //Возможные типы linkButton и их параметры
6462
6463window.linkButtonTypes = {
6464 "yandexSearch": {
6465 iconClass: "yandexButtIcon",
6466 link: "https://yandex.ru/yandsearch?text=",
6467 caption: "Яндекс.Поиск"
6468 },
6469 "yandexPics": {
6470 iconClass: "yandexButtIcon",
6471 link: "https://yandex.ru/images/search?text=",
6472 caption: "Яндекс.Картинки"
6473 },
6474 "yandexSearchByPic": {
6475 iconClass: "yandexButtIcon",
6476 link: "https://yandex.by/images/search?url=",
6477 "linkAfter": "&rpt=imageview",
6478 caption: "Поиск по картинке"
6479 },
6480 "yandexNews": {
6481 iconClass: "yandexButtIcon",
6482 link: "https://news.yandex.ru/yandsearch?rpt=nnews2&grhow=clutop&text=",
6483 caption: "Яндекс.Новости"
6484 },
6485 "yandexVideo": {
6486 iconClass: "yandexButtIcon",
6487 link: "https://yandex.ru/video/search?text=",
6488 caption: "Яндекс.Видео"
6489 },
6490 "yandexTranslate": {
6491 iconClass: "yandexButtIcon",
6492 link: "https://translate.yandex.ru/?text=",
6493 caption: "Яндекс.Переводчик"
6494 },
6495 "googleSearch": {
6496 iconClass: "googleButtIcon",
6497 link: "https://www.google.ru/search?q=",
6498 caption: "Google.Поиск"
6499 },
6500 "googlePics": {
6501 iconClass: "googleButtIcon",
6502 link: "https://www.google.ru/search?&hl=ru&site=imghp&tbm=isch&source=hp&biw=1467&bih=682&q=",
6503 caption: "Google.Картинки"
6504 },
6505 "googleSearchByPic": {
6506 iconClass: "googleButtIcon",
6507 link: "https://www.google.by/searchbyimage?image_url=",
6508 caption: "Поиск по картинке"
6509 },
6510 "googleNews": {
6511 iconClass: "googleButtIcon",
6512 link: "https://www.google.ru/search?hl=ru&gl=ru&tbm=nws&authuser=0&q=",
6513 caption: "Google.Новости"
6514 },
6515 "googleVideo": {
6516 iconClass: "googleButtIcon",
6517 link: "https://www.google.ru/search?tbm=vid&hl=ru&source=hp&biw=&bih=&q=",
6518 caption: "Google.Видео"
6519 },
6520 "googleTranslate": {
6521 iconClass: "googleButtIcon",
6522 link: "https://translate.google.ru/#auto/ru/",
6523 caption: "Google.Переводчик"
6524 },
6525 //Украина
6526 "yandexSearchUA": {
6527 iconClass: "yandexButtIcon",
6528 link: "https://yandex.ua/search/?lr=143&msid=1464598804.64254.22885.27565&text=",
6529 caption: "Яндекс.Поиск (укр)"
6530 },
6531 "yandexVideoUA": {
6532 iconClass: "yandexButtIcon",
6533 link: "https://yandex.ua/video/search?text=",
6534 caption: "Яндекс.Видео (укр)"
6535 },
6536 "googleSearchUA": {
6537 iconClass: "googleButtIcon",
6538 link: "https://www.google.com.ua/#q=",
6539 caption: "Google.Поиск (укр)"
6540 },
6541 "googleVideoUA": {
6542 iconClass: "googleButtIcon",
6543 link: "https://www.google.com.ua/search?tbm=vid&hl=ru-UA&source=hp&biw=&bih=&q=",
6544 "linkAfter": "&tbm=vid",
6545 caption: "Google.Видео (укр)"
6546 }
6547};
6548window.theOneCSS = "\nbody {\n overflow-x: hidden;\n font-size: 14px;\n}\n.displayNone {\n display: none !important;\n}\n.block {\n font-family: Helvetica,Arial,sans-serif;\n border: 0;\n border-radius: 4px;\n padding: 20px 10px 10px 10px;\n margin: 10px auto 35px;\n position: relative;\n background: #fff;\n max-width: 1200px;\n box-shadow: 5px 5px 18px #0001;\n text-align: left;\n}\n.task_focused .block {\n border: 1px solid #81c45600;\n box-shadow: 5px 5px 18px #0002;\n}\n.blockTitle {\n position: absolute;\n top: -15px;\n background: #e6e6e6;\n display: inline-block;\n padding: 8px 10px 6px 10px;\n font-size: 14px;\n line-height: 14px;\n border-radius: 4px;\n font-weight: 600;\n transition: all .2s ease-in 0s;\n margin-left: 8px;\n}\n.blockButtons {\n position: absolute;\n display: flex;\n top: -15px;\n right: 18px;\n transition: all .2s ease-in 0s;\n}\n.blockButtons .blockTitle {\n position: relative;\n display: block;\n width: fit-content;\n margin-left: 18px;\n padding: 0px;\n height: 28px;\n top: initial;\n left: initial;\n right: initial;\n}\n.task_focused .blockTitle {\n opacity: 1;\n}\n/*.deskTitle {\n font-weight: 600;\n}*/\n.buttDiv {\n margin-right: 10px;\n}\n.buttDiv:last-child {\n margin-right: 0;\n}\n.btn {\n margin-left: 9px;\n margin-bottom: 5px;\n display: inline-block;\n transition: max-height .3s ease-in, max-width .3s ease-in, opacity .2s ease-in;\n max-height: 50px;\n opacity: 1;\n user-select: none;\n cursor: pointer;\n position: relative;\n -moz-user-select: none;\n}\n.element .btn {\n margin-left: 0;\n}\n.btn a {\n font-size: 15px;\n color: #404040;\n display: inline-block;\n padding: 5px 12px 6px;\n background: #FFFBEA;\n margin: 4px 0 0;\n border-radius: 3px;\n border: 1px solid #d9d9d9;\n cursor: pointer;\n font-weight: 400;\n white-space: normal;\n position: relative;\n min-width: 16px;\n transition: all .3s ease-in-out 0s, box-shadow .1s ease-in 0s;\n text-align: center;\n max-width: calc(100vw - 80px);\n overflow: hidden;\n}\n.btn.hasHint a, .btn.hasHelp a {\n cursor: help;\n}\n.element:not(.asButtons) .btn a {\n text-align: left;\n}\n.btn.underPressure a {\n box-shadow: 0 0 1px 0 #0002 !important;\n}\n.btn.underPressure {\n left: 1px;\n top: 1px;\n}\n.btn.underPressure .popUpTitle {\n top: 39px;\n left: -1px;\n}\n.btn.noIcon a {\n padding-left: 13px;\n}\n.btn:hover a {\n box-shadow: 0 0 8px 1px #ffe478;\n z-index: 1;\n}\n.btn>i {\n position: absolute;\n left: 3px;\n font-size: 22px;\n top: 0px;\n}\n/*@media (max-width: 900px) {\n .btn a {\n font-size: 14px;\n padding: 2px 6px;\n font-weight: 400;\n }\n .darkSide .btn a {\n font-weight: 100;\n }\n .btn:not(.linkButton) i {\n font-size: 19px;\n top: 5px;\n }\n}*/\ntable .btn a {\n padding: 3px 15px;\n margin-bottom: 8px;\n}\n.btn.active a {\n background: #ffe478;\n}\n.selected .btn, .filtered .btn.filterMe {\n max-height: 0;\n max-width: 0;\n margin: 0;\n opacity: 0;\n}\n.selected .buttons .btn.active {\n max-height: 50px;\n max-width: 400px;\n opacity: 1;\n}\n.selected .buttons {\n flex-wrap:nowrap;\n}\n.group {\n border-radius: 5px;\n}\n/*.stepNumber {\n z-index: -1;\n position: absolute;\n color: #ffe4789e;\n font-weight: 900;\n font-size: 55px;\n right: 2px;\n top: 0;\n opacity: .6;\n font-family: Helvetica, sans-serif;\n height: 141px;\n line-height: 94px;\n}*/\n.answerBlock {\n position: relative;\n}\n.stepQuestion {\n font-size: 14px;\n font-weight: 400;\n white-space: pre-wrap;\n}\n.stepTitle {\n font-weight: 600;\n margin-bottom: 5px;\n}\n.step {\n z-index: 9;\n position: relative;\n max-height: 0px;\n overflow: hidden;\n transition: all 0.5s cubic-bezier(0.6, 0, 0.4, 1);\n}\n.open {\n max-height: 2000px;\n overflow: visible;\n}\n/*.commentBlock .field_type_textarea {\n width: 100% !important;\n max-width: 500px;\n}\n.commentHint {\n font-size: 14px;\n}\n.field.field_type_checkbox {\n position: absolute;\n}\n.field.field_type_checkbox label {\n display: none;\n}*/\n.btn.btnDarkSide a {\n box-shadow: 5px 5px 18px #0002;\n margin: 0 20px 20px;\n padding: 4px;\n line-height: 12px;\n width: 26px;\n height: 26px;\n cursor: pointer;\n}\n.btn.btnDarkSide a i {\n top: 6px;\n left: 6px;\n}\n.btn.btnDarkSide {\n width: 100%;\n text-align: center;\n display: none;\n max-width: initial;\n margin: 0 auto;\n max-height: 100px;\n}\n.btn[name=result]>i {\n left: 0.45em;\n}\n.task-suite .task:first-child .btnDarkSide {\n display: block;\n}\n.darkButt {\n padding: 0 2px;\n}\n.blockButtons .blockTitle:not(.custom) .ffIcon {\n opacity: 0.6;\n cursor: pointer;\n transition: .3s ease-out;\n font-size: 28px;\n}\n.blockButtons .blockTitle:not(.custom):hover .ffIcon {\n opacity: 1;\n}\n.task:not(.task_focused) .blockTitle:not(.tabScroller) {\n display: none;\n}\n.btnDarkSide {\n display: none;\n}\n.task:first-child .btnDarkSide {\n display: block;\n}\n.task-suite:not(.verticalSuite) .task {\n max-height: 100vh;\n overflow-y: scroll;\n margin: 0;\n min-height: 100vh;\n}\n.task-suite {\n padding: 0;\n margin: 0;\n width: 100000px;\n min-height: 100vh;\n display: block;\n background: #f2f2f2;\n background-attachment: fixed !important;\n position: relative;\n left: -000vw;\n transition: left 0.3s ease-out;\n}\n.task {\n margin: 0 0 40px;\n display: inline-block;\n width: 100vw;\n background: none;\n border: 0 none !important;\n box-shadow: none !important;\n text-align: center;\n opacity: 0.7;\n transition: all .3s ease-in 0s;\n}\n.task.task_focused {\n opacity: 1;\n}\n/*@media (min-width: 1600px) {\n .task {\n width: calc(50% - 5px);\n }\n}*/\n.task:last-of-type {\n margin-bottom: 12px;\n}\n.task-suite.darkSide {\n /*background:\n radial-gradient(black 15%, transparent 16%) 0 0,\n radial-gradient(black 15%, transparent 16%) 8px 8px,\n radial-gradient(rgba(255,255,255,.1) 15%, transparent 20%) 0 1px,\n radial-gradient(rgba(255,255,255,.1) 15%, transparent 20%) 8px 9px;*/\n background-color: #253548;\n background-size:16px 16px;\n}\n.darkSide .block {\n /*background: rgba(16, 32, 85, 0.53);*/\n background:#23272B;\n color: #DCD8C7;\n}\n.darkSide .blockTitle {\n background-color: #E0DAC0;\n background-size: 20px 20px;\n color: #262626;\n}\n.darkSide .btn a {\n color: #262626;\n background: #E0DAC0;\n border: 1px solid #E0DAC0;\n font-weight: 400;\n}\n.darkSide .btn:hover a {\n border: 1px solid #585B5E;\n box-shadow: 0 0 0 0;\n background: #585B5E !important;\n color: #FFFBEA;\n}\n.darkSide .btn.active a {\n background: #4A4E52;\n color:#FFFBEA;\n border: 1px solid #585B5E;\n}\n.darkSide td {\n color: #e6e6e6;\n}\n.darkSide input, .darkSide textarea {\n background: #4A4E52 !important;\n border: 1px solid #585B5E !important;\n color: #FFFBEA !important;\n}\n.darkSide input::placeholder, .darkSide textarea::placeholder {\n color: #e6e6e6 !important;\n}\n.darkSide input::-webkit-input-placeholder, .darkSide textarea::-webkit-input-placeholder {\n color: #e6e6e6;\n}\n.darkSide input[disabled], .darkSide textarea[disabled] {\n color: #899094 !important;\n}\n.darkSide input.validationRejected, .darkSide textarea.validationRejected {\n box-shadow: 0px 0px 6px 0px #f33;\n}\n.darkSide input.validationPassed, .darkSide textarea.validationPassed {\n box-shadow: 0px 0px 6px 0px #3fbd4c;\n}\n.darkSide .icon_upload {\n background-image: url(\"data:image/svg+xml;charset=utf-8,%3Csvg width='14' height='13' viewBox='0 0 14 13' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M9 5v5H5V5H2l5-5 5 5H9zm-9 6h14v2H0v-2z' fill='%23999' fill-rule='evenodd'/%3E%3C/svg%3E\");\n}\n.hiddenField label {\n display: none;\n}\n.entityBlock, .classBlock {\n user-select: none;\n}\ni.material-icons {\n opacity: 0.9;\n}\ninput {\n display: block;\n margin: 5px 0 0px;\n width: 100%;\n opacity: 1;\n border: 1px solid #d9d9d9;\n border-radius: 3px;\n padding: 5px;\n font-size: 14px;\n}\ntextarea {\n display: block;\n width: 100%;\n min-height: 50px;\n max-height: 600px;\n margin-top: 5px;\n border: 1px solid #d9d9d9 !important;\n border-radius: 3px;\n padding: 5px;\n font-size: 14px;\n}\ntextarea:focus {\n border: 1px solid #b3b3b3;\n}\ntable tr td:last-child {\n font-size: 0.9em;\n}\n.clickableLinkTitle, .labelTitle {\n font-size: 14px;\n font-weight: 400;\n}\n.blockContent .label, .blockContent .link {\n margin-bottom: 10px;\n font-size: 14px;\n font-weight: 400;\n}\n.blockContent {\n width: 100%;\n}\n.stepTitle.title {\n margin-bottom: 10px;\n}\n/*.stepTitle.title:first-letter {\n color: #000;\n \tfont-size: 120%;\n \tfont-weight: 600;\n}*/\n.stepQuestion, .stepTitle {\n word-break: break-word;\n}\n.link {\n margin-bottom: 5px;\n word-wrap: break-word;\n}\n.link a {\n color: #A24F00;\n font-weight: 600;\n word-break: break-all;\n}\n.link i {\n font-size: 18px;\n position: relative;\n top: 3px;\n margin-right: 3px;\n}\n.darkSide .link a, .darkSide a {\n color: #ec7e00;\n}\ninput.validationRejected, textarea.validationRejected {\n box-shadow: 0px 0px 6px 0px #f33;\n}\ninput.validationPassed, textarea.validationPassed {\n box-shadow: 0px 0px 6px 0px #3fbd4c;\n}\n.groupContainer {\n max-height: 10000px;\n opacity: 1;\n transition: all .3s ease-in-out 0s;\n vertical-align: top;\n}\n.groupContainer.excluded {\n overflow: hidden;\n}\n.inline {\n display: inline-block;\n}\n.groupContainer.inline .group {\n border: 0 none;\n display: inline-block;\n padding: 0;\n}\n.groupContainer.inline.excluded {\n max-width: 0;\n}\n.groupContainer.infinite .groupContainer.infinite {\n margin-left: 20px;\n}\n.group.infiniteChild.infiniteSkipped {\n opacity: 0.6;\n}\n.buttDiv.inline {\n display: inline-block;\n}\n.excluded {\n max-height: 0 !important;\n opacity: 0;\n}\n.block.excluded, .block.main .block.excluded {\n margin: 0 !important;\n padding: 0 !important;\n}\n.switchButton>i {\n z-index: 2;\n font-size: 23px;\n top: calc(50% - 10px);\n left: 6px;\n color: #404040;\n opacity: 0;\n transition: all .3s ease-in-out 0s;\n}\n.switchButton.active>i {\n opacity: 0.9;\n}\n.darkSide .switchButton>i {\n color: #FFFBEA;\n}\n.switchButton.active a, .linkButton a {\n padding-left: 30px;\n}\n/*.stepTitle a {\n color: darkred;\n font-weight: 500;\n}\n.darkSide .stepTitle a {\n color: greenyellow;\n}*/\n.inputData > .stepTitle {\n font-weight: 600;\n}\n.title {\n margin: 1px 0 0px;\n}\n.element {\n padding: 9px;\n border-radius: 3px;\n margin-top: 2px;\n position: relative;\n}\n.element.focused {\n /*background: #57738033;*/\n background: rgba(0, 0, 0, 0.05);\n}\n.darkSide .element.focused {\n background: rgba(255, 255, 255, 0.07);\n}\n.elementTitle {\n font-weight: 600;\n font-size: 15px;\n}\n.hotkeyHint {\n position: absolute;\n right: 4px;\n top: 7px;\n font-size: 9px;\n opacity: 0;\n color: #888;\n z-index: 2;\n}\n.focused .hotkeyHint {\n opacity: 1;\n transition: all .9s ease-out 0s;\n}\n/*.butt {\n display: inline-block;\n margin-right: 6px;\n margin-left: 10px;\n margin-bottom: 5px;\n transition: max-height .3s ease-in, max-width .3s ease-in, opacity .2s ease-in;\n max-height: 50px;\n opacity: 1;\n cursor: pointer;\n position: relative;\n user-select: none;\n}\n.butt a {\n font-size: 15px;\n color: #404040;\n display: inline-block;\n padding: 8px 12px;\n background: #fc0;\n margin: 4px 0 0;\n border-radius: 3px;\n border: 1px solid #fc0;\n cursor: pointer;\n font-weight: 400;\n white-space: nowrap;\n position: relative;\n min-width: 16px;\n transition: all .3s ease-in-out 0s;\n text-align: center;\n max-width: calc(100vw - 80px);\n overflow: hidden;\n height: 32px;\n line-height: 14px;\n}\n.butt a:hover {\n background:#f2c200;\n border: 1px solid #f2c200;\n box-shadow: 0 0 10px 1px #ffe478;\n}\n.darkSide .butt a {\n border: 1px solid #E0DAC0;\n background: #E0DAC0;\n color: #262626;\n}\n.darkSide .butt a:hover {\n background:#e0e3e5;\n border: 1px solid #e0e3e5;\n box-shadow: 0 0 8px 1px #7296a8;\n}*/\n.task.noErrors .task__error {\n display: none;\n}\n.field_file__label {\n height: 15px;\n}\n.stepTitle.title {\n font-weight: 400;\n font-size: 14px;\n margin-bottom: 1px;\n}\n.linkButtonIconContainer {\n background: none;\n width: 24px;\n height: 24px;\n overflow: hidden;\n border-radius: 12px;\n position: absolute;\n top: 8px;\n left: 4px;\n opacity: 0.8;\n user-select: none;\n z-index: 2;\n}\n.darkSide .linkButtonIconContainer {\n background: none;\n}\n.linkButton i {\n display: inline-block;\n width: 17px;\n height: 17px;\n margin: 2px 3px;\n background-repeat: no-repeat;\n}\n\n/* \u041D\u043E\u0432\u044B\u0435 \u0447\u0435\u043A\u0431\u043E\u043A\u0441\u044B \u0438 \u0440\u0430\u0434\u0438\u043E\u0431\u0430\u0442\u0442\u043E\u043D\u044B */\n.checkboxGroup:not(.asButtons) .stepTitle {\n margin-bottom: 4px;\n}\n.checkboxGroup:not(.asButtons) .btn>i {\n top: 0px;\n left: -1px;\n}\n.checkboxGroup:not(.asButtons) .btn a {\n border: 0 none;\n background: none;\n margin: 0;\n padding: 1px 15px 1px 0px;\n}\n.checkboxGroup:not(.asButtons) .btn a:before {\n content: \"\";\n display: inline-block;\n background: #fff;\n width: 15px;\n height: 15px;\n border-radius: 3px;\n margin-right: 8px;\n position: relative;\n top: 3px;\n border: 1px solid #80808080;\n}\n.checkboxGroup:not(.asButtons) .btn.active a:before {\n background: #ffe478;\n}\n.checkboxGroup:not(.asButtons) .btn:hover a {\n box-shadow: none;\n}\n.darkSide .checkboxGroup:not(.asButtons) .btn.active a, .darkSide .checkboxGroup:not(.asButtons) .btn a {\n background: none;\n border: none;\n color: #FFFBEA;\n}\n.darkSide .checkboxGroup:not(.asButtons) .btn>i {\n color: #000;\n width: 19px;\n left: -1px;\n top: 1px;\n}\n\n.radioGroup:not(.asButtons) .stepTitle {\n margin-bottom: 4px;\n}\n.radioGroup:not(.asButtons) .btn>i {\n top: 10px;\n left: 6px;\n width: 7px;\n background: #000;\n height: 7px;\n border-radius: 5px;\n}\n.radioGroup:not(.asButtons) .btn>i svg {\n display: none;\n}\n.radioGroup:not(.asButtons) .btn a {\n border: 0 none;\n background: none;\n margin: 0;\n padding: 1px 15px 1px 0px;\n}\n.radioGroup:not(.asButtons) .btn a:before {\n content: \"\";\n display: inline-block;\n background: #fff;\n width: 17px;\n height: 17px;\n border-radius: 17px;\n margin-right: 8px;\n position: relative;\n top: 3px;\n border: 1px solid #80808080;\n}\n.radioGroup:not(.asButtons) .btn.active a:before {\n background: #ffe478;\n}\n.radioGroup:not(.asButtons) .btn:hover a {\n box-shadow: none;\n}\n.darkSide .radioGroup:not(.asButtons) .btn.active a, .darkSide .radioGroup:not(.asButtons) .btn a {\n background: none;\n border: none;\n color: #FFFBEA;\n}\n\n/* \u041F\u0435\u0440\u0435\u043E\u043F\u0440\u0435\u0434\u0435\u043B\u0435\u043D\u0438\u044F \u0434\u043B\u044F \u0432\u0435\u0440\u0442\u0438\u043A\u0430\u043B\u044C\u043D\u043E\u0433\u043E \u0441\u044C\u044E\u0442\u0430 */\n.task-suite.verticalSuite {\n width: 100%;\n overflow-x: hidden;\n}\n\n\n/* \u041F\u0435\u0440\u0435\u043B\u0438\u0441\u0442\u044B\u0432\u0430\u043D\u0438\u0435 \u0432\u043A\u043B\u0430\u0434\u043E\u043A */\n.tabScroller {\n right: 18px;\n height: 23px !important;\n padding-top: 5px !important;\n z-index: 200;\n user-select: none;\n}\n.tabScroller i {\n line-height: 0;\n font-size: 32px;\n font-weight: 500;\n display: none;\n margin: -10px 0;\n}\n.leftScroll i:nth-of-type(2) {\n transform: rotate(180deg);\n}\n.rightScroll i:first-child {\n transform: rotate(180deg) !important;\n}\n.tabScroller i:nth-of-type(odd) {\n display: inline-block;\n}\n.task:first-of-type .tabScroller .leftScroll i:first-of-type, .task:last-of-type .tabScroller .rightScroll i:first-of-type{\n display: none;\n}\n.task:first-of-type .tabScroller .leftScroll i:last-of-type, .task:last-of-type .tabScroller .rightScroll i:last-of-type {\n display: inline-block;\n}\n.curTab, .tabsCount {\n margin: 0px 0px;\n}\n.leftScroll i:last-of-type, .rightScroll i:last-of-type {\n color: #808080;\n}\n.leftScroll, .rightScroll {\n cursor: pointer;\n}\n.tabNumbers {\n font-size: 16px;\n font-weight: 600;\n margin: 0 10px;\n top: 1px;\n position: relative;\n font-family: sans-serif;\n opacity: 0.8;\n}\n.verticalSuite .leftScroll svg, .verticalSuite .rightScroll svg {\n transform: rotate(90deg);\n}\n.verticalSuite .tabScroller {\n padding-left: 0;\n padding-right: 0;\n}\n.tabScroller .ffIcon {\n opacity: 0.6;\n transition: .3s ease-out;\n}\n.tabScroller:hover .ffIcon {\n opacity: 1;\n}\n\n/* \u0421\u0432\u043E\u0440\u0430\u0447\u0438\u0432\u0430\u043B\u043A\u0430 \u0431\u043B\u043E\u043A\u043E\u0432 */\n.block.minimizable .blockTitle {\n cursor: pointer;\n}\n.miniMaxi {\n user-select: none;\n}\n.miniMaxi i {\n font-size: 24px;\n transform: rotate(180deg);\n transition: all .2s ease-in 0s;\n padding: 0px;\n opacity: 0.8;\n margin: -7px -3px -7px 4px;\n}\n.minimized .miniMaxi i {\n transform: rotate(270deg);\n}\n.block:not(.minimizable)>div>div>.miniMaxi {\n display: none;\n}\n.blockContent {\n /* max-height: 1500px; */\n transition: max-height .5s ease-out 0s, overflow 0s ease 1s;\n overflow: visible;\n}\n.blockContent.minimized {\n max-height: 0px;\n overflow: hidden;\n}\n.blockContent>.helpContainer {\n position: absolute;\n left: auto;\n right: 3px;\n top: 2px;\n}\n.block:not(:hover)>.blockContent>.helpContainer:not(.triggered) {\n opacity: 0;\n}\n.minimized .searchField {\n opacity: 0;\n}\n.popUpTitle {\n padding: 4px 8px;\n position: absolute;\n left: 0px;\n top: 15px;\n color: black;\n white-space: pre-line;\n width: 400px;\n font-size: 14px;\n border: 1px solid #A6A6A6;\n z-index: 3000;\n border-radius: 5px;\n background: #fffe;\n opacity: 0;\n transition: opacity .3s ease-in-out 0s, visibility 0s ease 1.2s;\n visibility: hidden;\n box-shadow: 0 0 10px #000A;\n}\ndiv:not(.helpContainer)>.popUpTitle:hover {\n display: none;\n}\n/*.searchField .popUpTitle {\n line-height: initial;\n}*/\n.btn .popUpTitle {\n top: 40px;\n}\n.elementBody .popUpTitle {\n top: 40px;\n}\n.upperPopups .popUpTitle {\n top: initial;\n bottom: 35px;\n}\n.darkSide .popUpTitle {\n background: #222e;\n border-color: #666;\n color: snow;\n}\n.helpContainer {\n vertical-align: top;\n display: inline-block;\n position: relative;\n left: 2px;\n font-size: 15px;\n cursor: default;\n opacity: 1;\n top: 5px;\n transition: all .3s ease-out 0s;\n}\n.helpContainer .ffIcon {\n cursor: pointer;\n padding: 0;\n color: #A6A6A6;\n margin: 0;\n transition: color .3s ease-in;\n top: -1px;\n position: relative;\n}\n.blockContent>.helpContainer .ffIcon {\n width: 1.2em;\n height: 1.2em;\n}\n.helpContainer.triggered .popUpTitle, .helpContainer.openedByKey .popUpTitle, .openedByKey .popUpTitle {\n opacity: 1;\n visibility: visible;\n transition: opacity .3s ease-in-out 0s, visibility 0s ease 0s;\n}\n.helpContainer .popUpTitle {\n transition: opacity .3s ease-in-out 0s, visibility 0s ease .3s;\n display: flex;\n padding: 1px;\n}\n/*.element:not(.focused):not(:hover) .helpContainer {\n transition: none;\n}*/\n.helpContainer .ffIcon:hover, .helpContainer.openedByKey .ffIcon {\n color: #737373;\n}\n.darkSide .helpContainer .ffIcon {\n color: #808080;\n}\n.darkSide .helpContainer .ffIcon:hover, .darkSide .helpContainer.openedByKey .ffIcon {\n color: #BFBFBF;\n}\n.element:not(.focused):not(:hover) .helpContainer {\n opacity: 0;\n}\n.elementTitle>.stepQuestion>.helpContainer, .element.fileField>.stepTitle>.helpContainer {\n display: inline-block;\n top: 1px;\n}\n.element>.stepTitle>.helpContainer {\n top: 2px;\n}\n.element:not(:hover) .elementTitle>.stepQuestion>.helpContainer:not(.triggered) {\n opacity: 0;\n}\n.buttPath {\n font-weight: 600;\n}\n.elementBody {\n position: relative;\n}\n.cursorHelp {\n cursor: help;\n}\n.popUpTitleInner {\n overflow-y: auto;\n padding: 3px 7px;\n word-break: break-word;\n}\n/*.suggest:hover .popUpTitle, */.btn:hover .popUpTitle, .elementBody:hover .popUpTitle, .selectHead:hover .popUpTitle {\n opacity: 1;\n transition: opacity .3s ease-in-out .9s, visibility 0s ease .9s;\n visibility: visible;\n }\n/*.suggest.final {\n order: 0;\n background: #cdf9c4;\n}\n.suggest.final:hover {\n background: #88ff71;\n}\n.suggest.notFinal {\n order: 1;\n background: #ffefb2;\n}\n.suggest.notFinal:hover {\n background: #ffdb4d;\n}\n.darkSide .suggest.final {\n background: #386b00;\n}\n.darkSide .suggest.notFinal {\n background: #715a00;\n}\n.darkSide .suggest.notFinal:hover {\n background: #a98700;\n}\n.darkSide .suggest.final:hover {\n background: #5baf00;\n}*/\n.img__container__imitation {\n background-position: 0%;\n}\n\n/* \u0412\u044B\u0432\u043E\u0434 \u043E\u0448\u0438\u0431\u043E\u043A */\n.errorMsgBlock {\n position: absolute;\n color: white;\n bottom: 100%;\n background: #fa7067;\n padding: 2px 2px;\n font-size: 14px;\n border-width: 7px;\n display: none;\n white-space: pre-line;\n z-index: 1000;\n}\n.withSomeError .errorMsgBlock {\n display: flex;\n}\n.errorMsgBlock:after {\n border-width: 9px;\n content: \" \";\n bottom: -18px;\n z-index: 100;\n border-style: solid;\n pointer-events: none;\n left: 10px;\n height: 0;\n width: 0;\n background: 0 none;\n position: absolute;\n border-color: #fa7067 #0000 #0000;\n}\n.errorMsgBlockInner {\n overflow-y: auto;\n max-height: 100%;\n padding: 2px 5px 2px 5px;\n}\n.darkSide .errorMsgBlock {\n background: #fb5c56;\n}\n.darkSide .errorMsgBlock:before {\n border-color: #fb5c56 #0000 #0000;\n}\n.element.withSomeError {\n background-color: #f9baaf33;\n}\n.element.focused.withSomeError {\n background-color: #b7585833;\n}\n.darkSide .element.withSomeError {\n background-color: #ff000014;\n}\n.darkSide .element.focused.withSomeError {\n background-color: #ff19192e;\n}\n\n/* \u0421\u043F\u0438\u0441\u043E\u043A \u0442\u0430\u0441\u043A\u043E\u0432 \u0432 \u0441\u044C\u044E\u0442\u0435 */\n.taskName {\n display: none;\n}\n.tabList {\n width: max-content;\n position: absolute;\n right: 0px;\n transition: max-height .2s cubic-bezier(0, 0.3, 0.24, 0.59) 0s;\n max-width: 50vw;\n cursor: default;\n overflow: hidden;\n}\n.tabScroller:hover .tabList {\n transition: max-height .2s cubic-bezier(0, 0.3, 0.24, 0.59) 0.7s;\n}\n.tabList>div {\n z-index: 1000;\n border: 1px solid #81c45600;\n border-radius: 5px;\n padding: 5px 0;\n position: relative;\n margin-top: 10px;\n top: 0;\n background: white;\n box-shadow: 5px 5px 18px #0002;\n}\n.tabListItemNumber, .tabListItemName {\n display: inline-block;\n margin: 1px 7px 1px 14px;\n}\n.tabListItemNumber {\n font-size: 11px;\n position: absolute;\n left: -10px;\n top: 4px;\n}\n.tabListItem {\n opacity: 0.7;\n padding: 3px 0px 3px 4px;\n font-weight: 400;\n position: relative;\n margin: 2px 5px 2px 4px;\n cursor: pointer;\n}\n.tabListItem.selected {\n color: #404040;\n opacity: 0.8;\n font-weight: 600;\n cursor: default;\n background: #ffe478;\n border-radius: 3px;\n}\n.tabListItem:not(.selected):hover {\n opacity: 1;\n}\n.darkSide .tabList>div {\n border-color: #7296a8;\n background-color: #E0DAC0;\n color: #3a454b;\n}\n.darkSide .tabListItem.selected {\n color: #253548;\n opacity: 0.93;\n}\n.task:not(.task_focused) .tabList>div {\n border-color: #cccccc;\n}\n.task:not(.task_focused) .tabListItem.selected {\n background-color: #d9d9d9;\n}\n/* \u0414\u0438\u0440\u0435\u043A\u0442 */\n.no_notForDirect .bannerContainer {\n border: 1px solid #bbb;\n padding: 10px;\n width: fit-content;\n border-radius: 2px;\n display: inline-block;\n text-align: left;\n}\n.no_notForDirect .bannerTitle {\n font-size: 16px;\n line-height: 20px;\n position: relative;\n font-weight: bold;\n margin-bottom: 5px;\n display: flex;\n}\n.no_notForDirect.darkSide .bannerTitle a {\n color: aqua;\n}\n.no_notForDirect .bannerDomain {\n font-size: 12px;\n line-height: 16px;\n color: rgb(0, 102, 0);\n font-weight: bold;\n margin-top: 8px;\n}\n.no_notForDirect.darkSide .bannerDomain {\n color: #36e400;\n}\n.no_notForDirect .fieldName_verdict .buttDiv.fieldName_Yes {\n width: calc(25% - 7px);\n}\n.no_notForDirect .fieldName_checkboxes {\n margin-left: 25%;\n}\n.no_notForDirect .bannerInfoLine>div {\n display: inline-block;\n margin-right: 5px;\n}\n.no_notForDirect .bannerInfoLine {\n margin-bottom: 3px;\n}\n.no_notForDirect .bilValue {\n font-weight: 600;\n margin: 0 !important;\n}\n.no_notForDirect .topFlexBox {\n display: flex;\n flex-wrap: wrap;\n}\n.no_notForDirect .bannerElement {\n flex-grow: 10;\n text-align: center;\n padding-top: 0;\n margin-right: 20px;\n}\n.no_notForDirect .bannerInfoElement {\n background: #81c456;\n padding: 7px 15px;\n border-radius: 5px;\n border: 1px solid #81c456;\n flex-shrink: 0;\n margin-left: 9px;\n margin-bottom: 9px;\n}\n.no_notForDirect .bannerTitle a {\n color: blue;\n transition: all .1s ease-in;\n opacity: 1;\n}\n.no_notForDirect .bannerTitle .ffIcon {\n display: none;\n}\n.no_notForDirect .bannerTitle.visited .ffIcon {\n display: block;\n}\n.no_notForDirect .bannerTitle a:hover {\n opacity: 0.75;\n}\n.no_notForDirect .bannerTitle.visited a {\n color: #7000ff;\n}\n.no_notForDirect.darkSide .bannerTitle.visited a {\n color: #b843ff;\n}\n.no_notForDirect .bannerTitle .ffIcon {\n font-size: 25px;\n position: relative;\n top: -3px;\n left: -2px;\n color: #bbb;\n margin: 0 -1px;\n}\n.no_notForDirect .popUpTitle {\n width: 555px;\n}\n@media (max-width: 768px) {\n .no_notForDirect .block {\n max-width: initial;\n }\n .helpContainer {\n display: none;\n }\n}\n.misprinted {\n color: red;\n}\n\n/* SVG\u0448\u043A\u0438 */\n.yandexButtIcon i {\n background-image: url('data:image/svg+xml;utf8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 256 512\"><path fill=\"tomato\" d=\"M153.1 315.8L65.7 512H2l96-209.8c-45.1-22.9-75.2-64.4-75.2-141.1C22.7 53.7 90.8 0 171.7 0H254v512h-55.1V315.8h-45.8zm45.8-269.3h-29.4c-44.4 0-87.4 29.4-87.4 114.6 0 82.3 39.4 108.8 87.4 108.8h29.4V46.5z\"></path></svg>');\n margin-left: 6px;\n}\n.googleButtIcon i {\n background-image: url('data:image/svg+xml;utf8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 488 512\"><path fill=\"royalblue\" d=\"M488 261.8C488 403.3 391.1 504 248 504 110.8 504 0 393.2 0 256S110.8 8 248 8c66.8 0 123 24.5 166.3 64.9l-67.5 64.9C258.5 52.6 94.3 116.6 94.3 256c0 86.5 69.1 156.6 153.7 156.6 98.2 0 135-70.4 140.8-106.9H248v-85.3h236.1c2.3 12.7 3.9 24.9 3.9 41.4z\"></path></svg>');\n left: -1px;\n}\n.undoButtIcon i {\n background-image: url('data:image/svg+xml;utf8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"><g><path class=\"fa-secondary\" fill=\"currentColor\" d=\"M129 383a12 12 0 0 1 16.37-.56A166.77 166.77 0 0 0 256 424c93.82 0 167.24-76 168-166.55C424.79 162 346.91 87.21 254.51 88a166.73 166.73 0 0 0-113.2 45.25L84.69 76.69A247.12 247.12 0 0 1 255.54 8C392.35 7.76 504 119.19 504 256c0 137-111 248-248 248a247.11 247.11 0 0 1-166.18-63.91l-.49-.46a12 12 0 0 1 0-17z\" opacity=\"0.4\"></path><path class=\"fa-primary\" fill=\"currentColor\" d=\"M49 41l134.06 134c15.09 15.15 4.38 41-17 41H32a24 24 0 0 1-24-24V57.94C8 36.56 33.85 25.85 49 41z\"></path></g></svg>');\n margin-left: 1px;\n}\n.redoButtIcon i {\n background-image: url('data:image/svg+xml;utf8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"><g><path class=\"fa-secondary\" fill=\"currentColor\" d=\"M422.66 422.66a12 12 0 0 1 0 17l-.49.46A247.11 247.11 0 0 1 256 504C119 504 8 393 8 256 8 119.19 119.65 7.76 256.46 8a247.12 247.12 0 0 1 170.85 68.69l-56.62 56.56A166.73 166.73 0 0 0 257.49 88C165.09 87.21 87.21 162 88 257.45 88.76 348 162.18 424 256 424a166.77 166.77 0 0 0 110.63-41.56A12 12 0 0 1 383 383z\" opacity=\"0.4\"></path><path class=\"fa-primary\" fill=\"currentColor\" d=\"M504 57.94V192a24 24 0 0 1-24 24H345.94c-21.38 0-32.09-25.85-17-41L463 41c15.15-15.15 41-4.44 41 16.94z\"></path></g></svg>');\n margin-left: 0px;\n}\n.syncButtIcon i {\n background-image: url('data:image/svg+xml;utf8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"><g><path class=\"fa-secondary\" fill=\"currentColor\" d=\"M8 454.06V320a24 24 0 0 1 24-24h134.06c21.38 0 32.09 25.85 17 41l-41.75 41.75A166.82 166.82 0 0 0 256.16 424c77.41-.07 144.31-53.14 162.78-126.85a12 12 0 0 1 11.65-9.15h57.31a12 12 0 0 1 11.81 14.18C478.07 417.08 377.19 504 256 504a247.14 247.14 0 0 1-171.31-68.69L49 471c-15.15 15.15-41 4.44-41-16.94z\" opacity=\"0.4\"></path><path class=\"fa-primary\" fill=\"currentColor\" d=\"M12.3 209.82C33.93 94.92 134.81 8 256 8a247.14 247.14 0 0 1 171.31 68.69L463 41c15.12-15.12 41-4.41 41 17v134a24 24 0 0 1-24 24H345.94c-21.38 0-32.09-25.85-17-41l41.75-41.75A166.8 166.8 0 0 0 255.85 88c-77.46.07-144.33 53.18-162.79 126.85A12 12 0 0 1 81.41 224H24.1a12 12 0 0 1-11.8-14.18z\"></path></g></svg>');\n margin-left: 1px;\n}\n.adjustButtIcon i {\n background-image: url('data:image/svg+xml;utf8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 640 512\"><g><path class=\"fa-secondary\" fill=\"currentColor\" d=\"M568 256c0 137-111 248-248 248V8c137 0 248 111 248 248z\" opacity=\"0.4\"></path><path class=\"fa-primary\" fill=\"currentColor\" d=\"M320 8v496C183 504 72 393 72 256S183 8 320 8z\"></path></g></svg>');\n margin-left: 1px;\n margin-top: 3px;\n width: 21px;\n height: 21px;\n}\n.adjustSolidButtIcon i {\n background-image: url('data:image/svg+xml;utf8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 576 512\"><g><path class=\"fa-secondary\" fill=\"currentColor\" d=\"M545.3 226L439.6 329l25 145.5c4.5 26.1-23 46-46.4 33.7l-130.7-68.6V0a31.62 31.62 0 0 1 28.7 17.8l65.3 132.4 146.1 21.2c26.2 3.8 36.7 36.1 17.7 54.6z\" opacity=\"0.4\"></path><path class=\"fa-primary\" fill=\"currentColor\" d=\"M110.4 474.5l25-145.5L29.7 226c-19-18.5-8.5-50.8 17.7-54.6l146.1-21.2 65.3-132.4A31.62 31.62 0 0 1 287.5 0v439.6l-130.7 68.6c-23.4 12.3-50.9-7.6-46.4-33.7z\"></path></g></svg>');\n margin-left: 1px;\n width: 18px;\n height: 18px;\n}\n.swatchBookIcon i {\n background-image: url('data:image/svg+xml;utf8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"><g><path class=\"fa-secondary\" fill=\"currentColor\" d=\"M435.56 167.1l-90.33-90.51a31.89 31.89 0 0 0-45.1-.07l-.07.07-75.5 75.65V416c0 3-.67 5.73-.87 8.64l211.87-212.28a32.05 32.05 0 0 0 0-45.26zM63.88 192v64h63.93v-64zm416.18 128H373.29L187.15 506.51c-2.06 2.07-4.49 3.58-6.67 5.49h299.58A32 32 0 0 0 512 480V352a32 32 0 0 0-31.94-32zM63.88 64v64h63.93V64z\" opacity=\"0.4\"></path><path class=\"fa-primary\" fill=\"currentColor\" d=\"M159.68 0H31.94A32 32 0 0 0 0 32v384a95.81 95.81 0 1 0 191.62 0V32a32 32 0 0 0-31.94-32zM95.81 440a24 24 0 1 1 24-24 24 24 0 0 1-24 24zM63.88 256v-64h63.88v64zm0-128V64h63.88v64z\"></path></g></svg>');\n margin-left: 1px;\n width: 18px;\n height: 18px;\n}\n.sunButtIcon i {\n background-image: url('data:image/svg+xml;utf8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"><g><path class=\"fa-secondary\" fill=\"currentColor\" d=\"M502.42 240.5l-94.7-47.3 33.5-100.4c4.5-13.6-8.4-26.5-21.9-21.9l-100.4 33.5-47.41-94.8a17.31 17.31 0 0 0-31 0l-47.3 94.7L92.7 70.8c-13.6-4.5-26.5 8.4-21.9 21.9l33.5 100.4-94.7 47.4a17.31 17.31 0 0 0 0 31l94.7 47.3-33.5 100.5c-4.5 13.6 8.4 26.5 21.9 21.9l100.41-33.5 47.3 94.7a17.31 17.31 0 0 0 31 0l47.31-94.7 100.4 33.5c13.6 4.5 26.5-8.4 21.9-21.9l-33.5-100.4 94.7-47.3a17.33 17.33 0 0 0 .2-31.1zm-155.9 106c-49.91 49.9-131.11 49.9-181 0a128.13 128.13 0 0 1 0-181c49.9-49.9 131.1-49.9 181 0a128.13 128.13 0 0 1 0 181z\" opacity=\"0.4\"></path><path class=\"fa-primary\" fill=\"currentColor\" d=\"M352 256a96 96 0 1 1-96-96 96.15 96.15 0 0 1 96 96z\"></path></g></svg>');\n margin-left: 1px;\n width: 18px;\n height: 18px;\n}\n.paletteButtIcon i {\n background-image: url('data:image/svg+xml;utf8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"><g><path class=\"fa-secondary\" fill=\"currentColor\" d=\"M204.29 5c-99.4 19.4-179.5 99.29-199.1 198.4-37 187 131.7 326.39 258.8 306.69 41.2-6.4 61.4-54.59 42.5-91.69-23.1-45.4 9.9-98.4 60.9-98.4h79.7c35.8 0 64.8-29.6 64.9-65.31C511.49 97.13 368.09-26.87 204.29 5zM96 320a32 32 0 1 1 32-32 32 32 0 0 1-32 32zm32-128a32 32 0 1 1 32-32 32 32 0 0 1-32 32zm128-64a32 32 0 1 1 32-32 32 32 0 0 1-32 32zm128 64a32 32 0 1 1 32-32 32 32 0 0 1-32 32z\" opacity=\"0.4\"></path><path class=\"fa-primary\" fill=\"currentColor\" d=\"M96 256a32 32 0 1 0 32 32 32 32 0 0 0-32-32zm32-128a32 32 0 1 0 32 32 32 32 0 0 0-32-32zm128-64a32 32 0 1 0 32 32 32 32 0 0 0-32-32zm128 64a32 32 0 1 0 32 32 32 32 0 0 0-32-32z\"></path></g></svg>');\n margin-left: 1px;\n width: 18px;\n height: 18px;\n}\n.expandButtIcon i {\n background-image: url('data:image/svg+xml;utf8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"><g><path class=\"fa-secondary\" fill=\"currentColor\" d=\"M148 32H24A23.94 23.94 0 0 0 0 56v124a12 12 0 0 0 12 12h40a12 12 0 0 0 12-12V96h84a12 12 0 0 0 12-12V44a12 12 0 0 0-12-12zm288 288h-40a12 12 0 0 0-12 12v84h-84a12 12 0 0 0-12 12v40a12 12 0 0 0 12 12h124a23.94 23.94 0 0 0 24-24V332a12 12 0 0 0-12-12z\" opacity=\"0.4\"></path><path class=\"fa-primary\" fill=\"currentColor\" d=\"M148 416H64v-84a12 12 0 0 0-12-12H12a12 12 0 0 0-12 12v124a23.94 23.94 0 0 0 24 24h124a12 12 0 0 0 12-12v-40a12 12 0 0 0-12-12zM424 32H300a12 12 0 0 0-12 12v40a12 12 0 0 0 12 12h84v84a12 12 0 0 0 12 12h40a12 12 0 0 0 12-12V56a23.94 23.94 0 0 0-24-24z\"></path></g></svg>');\n}\n.washerButtIcon i {\n background-image: url('data:image/svg+xml;utf8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 446 512\"><g><path class=\"fa-secondary\" fill=\"currentColor\" d=\"M298 300a51.75 51.75 0 0 0 36.11-14.69A110.76 110.76 0 0 1 336 304a112 112 0 0 1-224 0 110.76 110.76 0 0 1 1.89-18.69 51.79 51.79 0 0 0 73.24-1 51.23 51.23 0 0 0 73.74 0A51.81 51.81 0 0 0 298 300z\" opacity=\"0.4\"></path><path class=\"fa-primary\" fill=\"currentColor\" d=\"M384 0H64A64 64 0 0 0 0 64v416a32 32 0 0 0 32 32h384a32 32 0 0 0 32-32V64a64 64 0 0 0-64-64zM184 64a24 24 0 1 1-24 24 24 24 0 0 1 24-24zM64 88a24 24 0 1 1 24 24 24 24 0 0 1-24-24zm160 360a144 144 0 1 1 144-144 144 144 0 0 1-144 144z\"></path></g></svg>');\n margin-left: 1px;\n}\n.eraserButtIcon i {\n background-image: url('data:image/svg+xml;utf8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"><g><path class=\"fa-secondary\" fill=\"currentColor\" d=\"M512 428v40a12 12 0 0 1-12 12H144a48 48 0 0 1-33.94-14.06l-96-96a48 48 0 0 1 0-67.88l136-136 227.88 227.88L355.88 416H500a12 12 0 0 1 12 12z\" opacity=\"0.4\"></path><path class=\"fa-primary\" fill=\"currentColor\" d=\"M377.94 393.94l120-120a48 48 0 0 0 0-67.88l-160-160a48 48 0 0 0-67.88 0l-120 120 45.25 45.25z\"></path></g></svg>');\n margin-left: 1px;\n}\n.defaultButtIcon i {\n background-image: url('data:image/svg+xml;utf8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 650 512\"><path fill=\"grey\" d=\"M448 279.196V464c0 26.51-21.49 48-48 48H48c-26.51 0-48-21.49-48-48V112c0-26.51 21.49-48 48-48h248a24 24 0 0 1 16.97 7.029l16 16C344.09 102.149 333.382 128 312 128H64v320h320V295.196c0-6.365 2.529-12.47 7.029-16.971l16-16C422.148 247.106 448 257.814 448 279.196zM576 37.333C576 16.715 559.285 0 538.667 0H380c-15.464 0-28 12.536-28 28v17.885c0 15.766 13.011 28.424 28.772 27.989l67.203-1.906L199.09 319.09c-9.429 9.363-9.457 24.605-.061 34.001l23.879 23.879c9.396 9.396 24.639 9.369 34.001-.06l247.122-248.885-1.906 67.203c-.434 15.76 12.224 28.772 27.99 28.772H548c15.464 0 28-12.536 28-28V37.333z\"></path></svg>');\n margin-top: 4px;\n}\n.uploadButtIcon i {\n background-image: url('data:image/svg+xml;utf8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"><g><path fill=\"currentColor\" d=\"M488 351.92H352v8a56 56 0 0 1-56 56h-80a56 56 0 0 1-56-56v-8H24a23.94 23.94 0 0 0-24 24v112a23.94 23.94 0 0 0 24 24h464a23.94 23.94 0 0 0 24-24v-112a23.94 23.94 0 0 0-24-24zm-120 132a20 20 0 1 1 20-20 20.06 20.06 0 0 1-20 20zm64 0a20 20 0 1 1 20-20 20.06 20.06 0 0 1-20 20z\" opacity=\"0.4\"></path><path fill=\"currentColor\" d=\"M192 359.93v-168h-87.7c-17.8 0-26.7-21.5-14.1-34.11L242.3 5.62a19.37 19.37 0 0 1 27.3 0l152.2 152.2c12.6 12.61 3.7 34.11-14.1 34.11H320v168a23.94 23.94 0 0 1-24 24h-80a23.94 23.94 0 0 1-24-24z\"></path></g></svg>');\n margin-left: 1px;\n}\n.thrashButtIcon i {\n background-image: url('data:image/svg+xml;utf8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"><g><path fill=\"currentColor\" d=\"M32 464a48 48 0 0 0 48 48h288a48 48 0 0 0 48-48V96H32zm272-288a16 16 0 0 1 32 0v224a16 16 0 0 1-32 0zm-96 0a16 16 0 0 1 32 0v224a16 16 0 0 1-32 0zm-96 0a16 16 0 0 1 32 0v224a16 16 0 0 1-32 0z\" opacity=\"0.4\"></path><path fill=\"currentColor\" d=\"M432 32H312l-9.4-18.7A24 24 0 0 0 281.1 0H166.8a23.72 23.72 0 0 0-21.4 13.3L136 32H16A16 16 0 0 0 0 48v32a16 16 0 0 0 16 16h416a16 16 0 0 0 16-16V48a16 16 0 0 0-16-16zM128 160a16 16 0 0 0-16 16v224a16 16 0 0 0 32 0V176a16 16 0 0 0-16-16zm96 0a16 16 0 0 0-16 16v224a16 16 0 0 0 32 0V176a16 16 0 0 0-16-16zm96 0a16 16 0 0 0-16 16v224a16 16 0 0 0 32 0V176a16 16 0 0 0-16-16z\"></path></g></svg>');\n margin-left: 1px;\n}\n.editButtIcon i {\n background-image: url('data:image/svg+xml;utf8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 576 512\"><g class=\"fa-group\"><path class=\"fa-secondary\" fill=\"currentColor\" d=\"M564.6 60.2l-48.8-48.8a39.11 39.11 0 0 0-55.2 0l-35.4 35.4a9.78 9.78 0 0 0 0 13.8l90.2 90.2a9.78 9.78 0 0 0 13.8 0l35.4-35.4a39.11 39.11 0 0 0 0-55.2zM427.5 297.6l-40 40a12.3 12.3 0 0 0-3.5 8.5v101.8H64v-320h229.8a12.3 12.3 0 0 0 8.5-3.5l40-40a12 12 0 0 0-8.5-20.5H48a48 48 0 0 0-48 48v352a48 48 0 0 0 48 48h352a48 48 0 0 0 48-48V306.1a12 12 0 0 0-20.5-8.5z\" opacity=\"0.4\"></path><path class=\"fa-primary\" fill=\"currentColor\" d=\"M492.8 173.3a9.78 9.78 0 0 1 0 13.8L274.4 405.5l-92.8 10.3a19.45 19.45 0 0 1-21.5-21.5l10.3-92.8L388.8 83.1a9.78 9.78 0 0 1 13.8 0z\"></path></g></svg>');\n margin-left: 1px;\n}\n.kinopoiskButtIcon i { /* by @stillnoise */\n background-image: url('data:image/svg+xml;utf8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 16 16\"><path fill=\"orangered\" d=\"M8,16 C3.581722,16 0,12.418278 0,8 C0,3.581722 3.581722,0 8,0 C12.418278,0 16,3.581722 16,8 C16,12.418278 12.418278,16 8,16 Z M8,9 C8.55228475,9 9,8.55228475 9,8 C9,7.44771525 8.55228475,7 8,7 C7.44771525,7 7,7.44771525 7,8 C7,8.55228475 7.44771525,9 8,9 Z M5,7 C6.1045695,7 7,6.1045695 7,5 C7,3.8954305 6.1045695,3 5,3 C3.8954305,3 3,3.8954305 3,5 C3,6.1045695 3.8954305,7 5,7 Z M11,7 C12.1045695,7 13,6.1045695 13,5 C13,3.8954305 12.1045695,3 11,3 C9.8954305,3 9,3.8954305 9,5 C9,6.1045695 9.8954305,7 11,7 Z M11,13 C12.1045695,13 13,12.1045695 13,11 C13,9.8954305 12.1045695,9 11,9 C9.8954305,9 9,9.8954305 9,11 C9,12.1045695 9.8954305,13 11,13 Z M5,13 C6.1045695,13 7,12.1045695 7,11 C7,9.8954305 6.1045695,9 5,9 C3.8954305,9 3,9.8954305 3,11 C3,12.1045695 3.8954305,13 5,13 Z\"></path></svg>');\n}\n.linkButtonIconContainer i {\n top: 0px;\n left: 2px;\n}\n\n/* SVG-\u0448\u043D\u044B\u0435 \u0438\u043A\u043E\u043D\u043A\u0438 */\n.ffIcon {\n display: inline-block;\n width: 1em;\n height: 1em;\n padding: 3px;\n color: #111;\n box-sizing: border-box;\n}\n.ffIcon svg {\n display: block;\n width: 100%;\n height: 100%;\n}\n.darkSide .link .ffIcon {\n color: #e0dac0;\n}\n\n/* \u041D\u0430\u0441\u0442\u0440\u043E\u0439\u043A\u0438 \u0448\u0430\u0431\u043B\u043E\u043D\u0430 */\n.no_taskList .tabList {\n display: none;\n}\n.no_taskCounter .tabNumbers {\n width: 0 !important;\n overflow: hidden;\n display: inline-block;\n margin: 0 5px;\n}\n.no_taskSwitcher .tabScroller {\n display: none;\n}\n.no_taskMinimizer .miniMaxi {\n display: none;\n}\n.no_taskMinimizer .block.minimizable .blockTitle {\n cursor: default;\n}\n\n/* \u041F\u0440\u043E\u0441\u043C\u043E\u0442\u0440\u0449\u0438\u043A \u043A\u0430\u0440\u0442\u0438\u043D\u043E\u043A (\u043D\u0430\u0447\u0438\u043D\u0430\u044F \u0441 1.20) */\n.imageField {\n user-select: none;\n}\n.imageField .imageBlock {\n overflow: hidden;\n flex-grow: 10;\n position: relative;\n}\n.imageField .imageBlock img {\n position: relative;\n}\n.imageField .imageBlock img {\n position: relative;\n cursor: grab;\n transition: all .1s ease-out;\n transform: none;\n}\n.imageConsole {\n position: absolute;\n bottom: 0;\n right: 0;\n color: white;\n text-shadow: 0 0 10px black, 0 0 5px black;\n opacity: 0;\n transition: all .3s ease-out;\n margin: 0 2px 1px 0;\n}\n.imageField canvas {\n width: 100%;\n height: 400px;\n image-rendering: optimizeQuality;\n image-rendering: -moz-crisp-edges;\n background-image: linear-gradient(45deg, #8883 25%, transparent 25%),linear-gradient(-45deg, #8883 25%, transparent 25%),linear-gradient(45deg, transparent 75%, #8883 75%),linear-gradient(-45deg, transparent 75%, #8883 75%);\n background-size: 20px 20px;\n background-position: 0 0, 0 10px, 10px -10px, -10px 0px;\n cursor: grab;\n}\n.dragging.imageField canvas {\n cursor: grabbing;\n}\n.imageField.expanded {\n position: fixed;\n z-index: 3000;\n background: white;\n width: calc(100vw - 50px) !important;\n height: calc(100vh - 50px);\n left: 10px;\n top: 10px;\n padding: 15px;\n box-shadow: 0 0 35px 0px #000;\n display: flex;\n flex-direction: column;\n margin: 0 !important;\n}\n.imageField.expanded canvas {\n height: 100% !important;\n width: 100% !important;\n}\n.imageField .stepTitle {\n text-align: center;\n margin-bottom: 8px;\n}\n.imageField.expanded .stepTitle {\n display: none;\n}\n.imageFooter {\n display: flex;\n justify-content: space-between;\n}\n.imageFooter .btn a {\n width: 0px;\n overflow: hidden;\n color: #0000;\n padding-right: 0;\n padding-left: 12px;\n z-index: 10;\n background: #0000;\n margin-top: 0;\n white-space: nowrap;\n height: 17px;\n}\n.imageFooter .linkButtonIconContainer {\n text-align: center;\n top: 4px;\n left: 4px;\n opacity: 0.9;\n}\n.imageSource {\n display: none;\n}\n.imageOriginalLink {\n display: none;\n}\n.darkSide .imageFooter .btn:hover a {\n background: #0000 !important;\n color: #0000;\n}\n.imageFooter>div>.element {\n padding: 0;\n}\n.imageFooter>div>.element .btn {\n margin-bottom: 0;\n margin-top: 3px;\n}\n.imageFooter>div>.element .buttDiv:last-child {\n margin-right: 0;\n}\n.imageFooter .hotkeyHint {\n right: 2px;\n font-size: 8px;\n top: 1px;\n}\n.darkSide .imageField.expanded.focused {\n background: rgb(56, 68, 74);\n border: 1px solid #7296a8;\n}\n\n.darkSide .imageField .btn a {\n border: 0 none;\n}\n.imageFooter>div>.element .btn {\n border-radius: 3px;\n margin-top: 6px;\n width: 30px;\n height: 29px;\n transition: all .2s ease-out;\n background: #FFFBEA;\n}\n.darkSide .imageFooter>div>.element .btn {\n background: #E0DAC0;\n}\n.darkSide .imageFooter>div>.element .btn.selected {\n background: #4A4E52;\n}\n.imageFooter>div>.element .btn.selected {\n background: #ffe478;\n}\n.imageRightButts .popUpTitle {\n left: initial;\n right: 0;\n}\n.imageField.noCanvas .imageConsole {\n display: none;\n}\n.imageField.noCanvas canvas {\n position: absolute;\n left: 0;\n}\n.imageField.noCanvas .justAnImage {\n object-fit: contain;\n width: 100%;\n height: 100%;\n}\n.imageField.noCanvas .imageBlock {\n max-height: 400px;\n text-align: center;\n}\n.imageField.noCanvas.expanded .imageBlock {\n max-height: 100%;\n}\n/* Select - \u044D\u043B\u0435\u043C\u0435\u043D\u0442 \u0441 \u0432\u0435\u0440\u0441\u0438\u0438 1.22 */\n.select.element {\n position: relative;\n min-width: 200px;\n width: 300px;\n display: block;\n}\n.select.element .stepTitle {\n margin-bottom: 5px;\n}\n.selectContainer {\n max-height: 0;\n overflow: hidden;\n transition: all .3s ease-out;\n position: absolute;\n width: calc(100% - 20px);\n background: #f2f2f2;\n z-index: 1000;\n padding: 0 9px 0;\n border-radius: 3px;\n margin-left: -9px;\n}\n.select.opened .selectContainer {\n max-height: 800px;\n overflow-y: auto;\n padding: 0 10px 9px 9px;\n}\n.selectContainer .buttDiv {\n display: block;\n width: 100%;\n margin: 0;\n}\n.selectContainer .btn {\n width: 100%;\n margin: 0;\n max-height: initial;\n}\n.selectContainer .btn a {\n padding: 2px 24px 3px 14px;\n margin-top: -1px;\n display: block;\n width: min-content;\n min-width: calc(100% - 38px);\n white-space: pre-line;\n max-height: initial;\n text-align: left;\n}\n.selectContainer .btn i {\n top: calc(50% - 13px);\n left: initial;\n right: -1px;\n}\n.selectContainer .btn .hotkeyHint {\n right: initial;\n left: 3px;\n top: 1px;\n}\n.selectHead {\n border: 1px solid #d9d9d9;\n border-radius: 3px;\n height: 26px;\n display: flex;\n background: #FFFBEA;\n}\n.selectDisplay {\n flex-grow: 10;\n overflow: hidden;\n cursor: pointer;\n}\n.selectButtons i {\n width: 26px;\n height: 26px;\n}\n.selectButtons .empty {\n display: none;\n}\n.selectButtons .arrow svg {\n height: 30px;\n top: -4px;\n position: relative;\n}\n.selectButtons i {\n width: 26px;\n height: 26px;\n opacity: 0.5;\n cursor: pointer;\n transition: all .2s ease-out;\n}\n.selectButtons i:hover {\n opacity: 1;\n}\n.selectText {\n width: 2000px;\n padding: 4px 8px;\n}\n.darkSide .selectText {\n color: #23272b;\n}\n.darkSide .select .selectHead, .darkSide .blockTitle .select .selectHead {\n background: #E0DAC0;\n border: 1px solid #E0DAC0;\n}\n.darkSide .select .selectContainer {\n padding: 0 4px 0 2px;\n left: 16px;\n}\n.darkSide .select.opened .selectContainer {\n padding: 3px 4px 2px 2px;\n}\n.select:not(.opened) .ffIcon.arrow {\n transform: rotate(90deg);\n}\n.select.opened .ffIcon.arrow {\n transform: rotate(0deg);\n}\n.blockTitle .select .selectHead {\n background-color: #e6e6e6;\n}\n.blockTitle .select {\n padding: 0;\n margin: 0;\n}\n.blockTitle .select .selectContainer {\n padding: 0px 4px 0px 2px;\n left: 8px;\n top: 29px;\n width: calc(100% - 4px);\n}\n.blockTitle .select.opened .selectContainer {\n padding: 2px 4px 2px 2px;\n}\n.blockTitle .select .selectText {\n padding-top: 6px;\n opacity: 0.8;\n}\n.blockTitle .select.focused .selectHead {\n background-color: #d9d9d9;\n}\n.darkSide .blockTitle .select.focused .selectHead {\n background-color: #d9d9d9;\n}\n.selectHead .popUpTitle {\n top: 60px;\n left: 9px;\n}\n.selectContainer .btn .popUpTitle {\n max-width: calc(100% - 20px);\n top: -2000px;\n visibility: hidden;\n}\n.selectContainer .btn:hover .popUpTitle {\n top: 20px;\n display: block;\n visibility: visible;\n}\n/* \u0417\u0430\u0433\u0440\u0443\u0437\u0447\u0438\u043A \u0444\u0430\u0439\u043B\u043E\u0432 */\n.fileField .fcFileLine {\n display: flex;\n}\n.fileField .fcFileLine div {\n font-weight: 600;\n padding: 0;\n}\n.fileField .fcFileName {\n display: flex;\n align-items: center;\n margin-right: 10px !important;\n margin-left: 10px;\n}\n.fileField .fcFileLine.uploading .fcFileName {\n opacity: .9;\n animation: blink 1.2s linear infinite;\n}\n.fileField .fcontainer.somethingIsUploading .btn.uploadButtIcon, .fileField .fcontainer.somethingIsUploading .btn.uploadButtIcon a {\n opacity: 0.7;\n left: 0px;\n top: 0px;\n cursor: wait;\n}\n.fileField .btn a {\n padding-top: 2px;\n padding-bottom: 3px;\n color: black;\n}\n.fileField .fUploader .element {\n margin: 0 0 0 15px;\n padding: 0;\n}\n.fileField .linkButtonIconContainer {\n top: 6px;\n width: 20px;\n}\n.fileField>.stepTitle {\n display: flex;\n justify-content: flex-start;\n flex-wrap: wrap;\n align-items: center;\n}\n.fileField>.stepTitle .btn {\n margin: 0 0 4px;\n}\n.fileField .errorMsgBlock {\n bottom: 45px;\n}\n/* \u041C\u043E\u0434\u0430\u043B\u044C\u043D\u044B\u0435 \u043E\u043A\u043D\u0430 */\n.window {\n position: fixed;\n left: 0;\n top: 0;\n width: 100vw;\n height: 100vh;\n max-width: initial;\n max-height: initial;\n z-index: 1002;\n margin: 0;\n padding: 0;\n background: #0008;\n display: none;\n justify-content: center;\n align-items: center;\n}\n.window.opened {\n display: flex;\n}\n.windowButtons>div {\n text-align: right;\n padding-right: 0;\n padding-bottom: 0;\n margin-bottom: -4px;\n}\n.windowContent {\n padding: 1px;\n max-height: calc(100vh - 115px);\n overflow: auto;\n}\n.windowBox {\n margin: 0;\n}\n.window.large .windowBox {\n flex-grow: 1;\n}\n.window:not(.hasTitle) .blockTitle {\n display: none;\n}\n.window:not(.hasTitle) .windowBox {\n padding-top: 10px;\n}\n.window:not(.large) .errorMsgBlock {\n position: relative;\n top: -7px;\n}\n.windowContent .element.withSomeError {\n margin-top: 23px;\n}\n\n/* \u0424\u043E\u0440\u043C\u0430 \u043F\u043E\u0438\u0441\u043A\u0430 \u043F\u043E \u0433\u043B\u043E\u0441\u0441\u0430\u0440\u0438\u044E */\n.glossarySearchForm {\n display: none !important;\n position: absolute;\n right: 1px;\n width: 400px;\n top: 32px;\n max-width: 80vw;\n background: #4a4e52;\n padding: 3px 5px 3px 26px;\n border-radius: 3px;\n}\n.btnGlossarySearch.active .glossarySearchForm {\n display: block !important;\n}\n.glossarySearchForm .gloss_button {\n display: none;\n}\n.glossarySearchForm .gloss_container .input_clear {\n top: 3px !important;\n font-size: 32px !important;\n left: -26px !important;\n width: 18px;\n}\n.glossarySearchForm .gloss_container .input_bordering {\n border: 1px solid #81c45600 !important;\n border-radius: 4px;\n}\n.glossarySearchForm .gloss_container .input_bordering input {\n margin: 0;\n font-size: 15px !important;\n width: 100% !important;\n padding-right: 8px !important;\n}\n.glossarySearchForm .gloss_container {\n visibility: visible !important;\n height: 30px;\n display: block;\n margin-bottom: -3px;\n width: 100% !important;\n}\n.darkSide .glossarySearchForm .gloss_container {\n margin-bottom: -1px;\n}\n\n/* \u0412\u0438\u0434\u044B (view) */\n.view.hidden {\n position: absolute;\n z-index: -1;\n top: 0;\n visibility: hidden;\n}\n.view.hidden.excluded {\n display: none;\n}\n\n/* actionButton */\n.btn.actionButtonNoIcon a {\n padding-left: 12px;\n}\n.actionButtonNoIcon .linkButtonIconContainer {\n display: none;\n}\n\n/* \u0412\u043E\u0437\u0434\u0443\u0448\u043D\u044B\u0439 \u0448\u0430\u0440 (baloon) */\n.baloon {\n border: 1px solid #81c456;\n padding: 10px;\n margin-top: 5px;\n background: #81c456;\n margin-bottom: 0 !important;\n border-radius: 5px;\n}\n.baloonTitle {\n font-size: 15px;\n font-weight: 600;\n margin: 0 0 5px;\n}\n.baloon.full {\n width: 100%;\n}\n\n/* flexBox */\n.flexBox {\n display: flex;\n justify-content: start;\n margin: -10px 0 0 -10px;\n}\n.flexBox>div {\n margin: 10px 0 0 10px !important;\n flex-grow: 10;\n min-width: 30%;\n}\n@media all and (max-width: 1000px) {\n .flexBox {\n flex-wrap: wrap;\n }\n}\n\n/* infoLine */\n.infoLine {\n line-height: 26px;\n display: flex;\n}\n.infoLine .ilTitle {\n display: inline-block;\n margin-right: 8px;\n font-size: 15px;\n flex-shrink: 0;\n max-width: 100%;\n}\n.infoLine .ilContent {\n font-weight: 600;\n white-space: pre-line;\n position: relative;\n top: 0px;\n word-break: break-word;\n}\n.infoLine.infoLines {\n flex-wrap: wrap;\n}\n.infoLine.infoLines .ilContent {\n font-weight: 400;\n margin-left: 10px;\n}\n.infoLine div:not(.infoLine) {\n line-height: initial;\n}\n\n/* \u0421\u043F\u043E\u0439\u043B\u0435\u0440\u044B (\u0427\u0435\u043D\u0434\u043B\u0435\u0440 \u0438 \u041C\u043E\u043D\u0438\u043A\u0430 \u043F\u043E\u0436\u0435\u043D\u044F\u0442\u0441\u044F, \u041B\u0435\u044F - \u0441\u0435\u0441\u0442\u0440\u0430 \u041B\u044E\u043A\u0430, \u0438 \u0432\u0441\u0451 \u0432 \u0442\u0430\u043A\u043E\u043C \u043A\u043B\u044E\u0447\u0435) */\n.spoiler.element .spoilerHead .btn {\n width: 100%;\n margin: 0;\n}\n.spoiler.element .spoilerHead .btn a {\n width: calc(100% - 44px);\n text-align: left;\n padding: 3px 12px 3px 28px;\n margin: 0;\n}\n.spoiler.element .spoilerHead .buttDiv {\n margin: 0;\n}\n.spoiler.element .spoilerHead .hotkeyHint {\n top: 4px;\n right: 7px;\n}\n.spoiler.element .spoilerHead .btn i {\n top: 2px;\n padding: 0;\n left: 4px;\n opacity: 0.8 !important;\n color: black;\n}\n.spoiler.element:not(.opened) .spoilerHead .btn i {\n transform: rotate(-90deg);\n top: 1px;\n left: 5px;\n}\n.spoiler.element.opened .spoilerHead .btn i, .spoiler.element:hover .spoilerHead .btn i {\n opacity: 1 !important;\n}\n.spoiler.element .spoilerContainer {\n max-height: 10000px;\n transition: all .3s cubic-bezier(1, 0.01, 0.68, 0.4) 0s;\n}\n.spoiler.element:not(.opened) .spoilerContainer {\n max-height: 0;\n overflow: hidden;\n transition: all .3s cubic-bezier(0.32, 0.63, 0.01, 1.01) 0s;\n}\n.spoiler.element:not(.opened) .spoilerHead .btn a {\n background-color: #FFFBEA;\n}\n.spoiler.element {\n transition: all .3s ease-out;\n padding: 8px;\n border: 1px solid #fff0;\n}\n.spoiler.element .spoilerContent {\n padding-top: 10px;\n white-space: pre-line;\n}\n.spoiler.element.opened {\n border: 1px solid #D5D9D3;\n}\n.darkSide .spoiler.element.opened {\n border-color: #4A4E52;\n}\n.darkSide .spoiler.element:not(.opened) .spoilerHead .btn:hover a {\n background: #585B5E;\n}\n.darkSide .spoiler.element:not(.opened) .spoilerHead .btn a {\n background: #E0DAC0;\n}\n.darkSide .spoiler.element .spoilerHead .btn:hover i, .darkSide .spoiler.element.opened .spoilerHead .btn i {\n color: #FFFBEA;\n}\n\n/* \u041F\u043E\u043B\u043D\u043E\u044D\u043A\u0440\u0430\u043D\u043D\u044B\u0439 \u0440\u0435\u0436\u0438\u043C */\n.fullScreenMode .task {\n padding: 0;\n overflow: hidden !important;\n margin: 0;\n}\n.fullScreenMode .task > *:not(.window):not(.customContextMenu) {\n /* \u0417\u0430\u0431\u044B\u043B \u0443\u0436\u0435, \u0437\u0449\u0430\u0447\u0435\u043C \u044D\u0442\u043E.\n display: none;*/\n}\n.fullScreenMode .block.main {\n height: calc(100vh - 32px);\n width: 100%;\n margin: 0;\n padding: 0;\n border: 0 none;\n border-radius: 0px;\n display: block;\n margin-top: 32px;\n max-width: 100%;\n background: #f2f2f2;\n}\n.fullScreenMode .block.main .block {\n margin: 20px 10px 35px !important;\n}\n.fullScreenMode .block:not(.windowBox) {\n max-width: initial;\n}\n.darkSide.fullScreenMode .block.main {\n background: #090D12;\n}\n.fullScreenMode .block.main > .blockContent {\n overflow-x: hidden;\n height: 100%;\n}\n.fullScreenMode .block.main>.blockContent>.blockButtons, .fullScreenMode .block.main>.blockContent>.blockTitle {\n position: fixed;\n top: 0px;\n background: white;\n display: none;\n}\n.fullScreenMode .task_focused .block.main, .fullScreenMode .task_focused .block.main>.blockContent>.blockButtons, .fullScreenMode .task_focused .block.main>.blockContent>.blockTitle {\n display: flex;\n}\n.fullScreenMode .task_focused .block.main>.blockContent>.blockTitle {\n box-shadow: 0 0 5px 0 #0005;\n}\n.fullScreenMode .block.main>.blockContent>.blockTitle {\n width: calc(100vw - 20px);\n margin: 0;\n border: 0;\n border-radius: 0;\n z-index: 1000;\n height: 32px;\n align-items: center;\n padding: 0 10px;\n}\n.fullScreenMode .block.main>.blockContent>.blockButtons {\n z-index: 1001;\n right: 10px;\n top: 2px;\n}\n.darkSide.fullScreenMode .block.main>.blockContent>.blockButtons, .darkSide.fullScreenMode .block.main>.blockContent>.blockTitle {\n color: #DCD8C7;\n background: #23272B;\n}\n.fullScreenMode .task_focused .block.main>.blockContent>.blockTitle {\n box-shadow: 0 0 5px 0 #0005;\n}\n.darkSide.fullScreenMode .task_focused .block.main>.blockContent>.blockTitle {\n box-shadow: 0 0 5px 0 #DCD8C750;\n}\n.fullScreenMode .task__error {\n top: 33px;\n}\n\n/* \u041A\u043E\u043D\u0442\u0435\u043A\u0441\u0442\u043D\u043E\u0435 \u043C\u0435\u043D\u044E */\n.customContextMenu {\n position: fixed;\n z-index: 9000;\n}\n.customContextMenuItemsContainer {\n border-radius: 5px;\n padding: 1px 2px 1px 1px;\n background: #808080;\n box-shadow: 3px 3px 18px 2px #0004;\n}\n.customContextMenuActionButtons {\n padding: 0;\n margin: 0;\n}\n.customContextMenuActionButtons .buttDiv {\n margin-right: 15px;\n}\n.customContextMenuActionButtons .btn {\n width: 100%;\n margin: 0px;\n}\n.customContextMenuActionButtons .btn a {\n width: 100%;\n margin: 1px;\n padding: 2px 7px;\n border: 0;\n border-radius: 0;\n}\n.customContextMenuRightClickField {\n border: 1px dashed #fffbea;\n border-radius: 5px 5px 0 0;\n margin: 1px 0 1px 1px;\n color: #fffbea;\n cursor: pointer;\n padding: 2px 7px;\n}\n.customContextMenuActionButtons .hotkeyHint {\n right: -13px;\n top: 0px;\n}\n\n/* \u041C\u0435\u043B\u043E\u0447\u0451\u0432\u043A\u0430 \u0438 \u043F\u0435\u0440\u0435\u043E\u043F\u0440\u0435\u0434\u0435\u043B\u0435\u043D\u0438\u044F */\n.leftPopup .popUpTitle {\n left: initial;\n right: 0;\n}\n\n/* \u041C\u043E\u0431\u0438\u043B\u044C\u043D\u043E-\u0441\u043F\u0435\u0446\u0438\u0444\u0438\u0447\u043D\u044B\u0435 \u0441\u0442\u0438\u043B\u0438 */\n\n@media all and (max-width: 768px) {\n .task {\n padding: 25px 0 10px 0;\n overflow: hidden;\n }\n .task:first-child {\n padding-top: 0;\n }\n .block {\n border-left: none !important;\n border-right: none !important;\n border-radius: 0 !important;\n padding: 10px;\n margin-top: 0;\n }\n .blockTitle {\n padding: 15px 12px !important;\n margin: 0;\n top: -12px;\n width: 100%;\n left: -10px;\n border-left: none !important;\n border-right: none !important;\n border-radius: 0 !important;\n position: relative;\n }\n .tabList {\n max-width: 96vw;\n left: 7vw;\n }\n textarea {\n min-height: 100px !important;\n }\n .element.focused:not(.imageField), .darkSide .element.focused:not(.imageField) {\n background: none;\n }\n .buttDiv {\n margin: 0 10px 10px 0;\n padding: 0;\n }\n .btn {\n margin: 0;\n }\n .btn.active {\n margin: 0 !important;\n }\n .btn a {\n min-height: 19px;\n margin: 0;\n padding: 7px 15px 6px;\n vertical-align: baseline !important;\n }\n .btn.linkButton a {\n padding-left: 30px;\n }\n .btn.linkButton.actionButtonNoIcon a {\n padding-left: 16px;\n }\n .linkButtonIconContainer {\n top: 6px;\n }\n .popUpTitle {\n display: none !important;\n }\n .hotkeyHint {\n display: none;\n }\n .switchButton>i {\n top: 6px;\n }\n .stepTitle.title {\n margin: 0 0 10px 0;\n }\n input {\n min-height: 42px;\n }\n .btn:hover a, .darkSide .btn:hover a {\n box-shadow: none;\n }\n .blockContent>.buttDiv {\n margin-left: 9px;\n }\n .blockButtons .blockTitle {\n padding: 2px 10px !important;\n margin: 0 !important;\n }\n .blockButtons {\n position: relative;\n top: -11px;\n width: 100vw;\n left: -10px;\n flex-wrap: wrap;\n }\n .blockButtons .blockTitle.tabScroller {\n width: max-content;\n text-align: right;\n padding-top: 7px !important;\n flex-grow: 10;\n }\n .blockButtons .blockTitle.custom {\n order: 10;\n border-top: 0 none;\n padding: 5px 12px !important;\n width: 100%;\n }\n .glossarySearchForm {\n width: 100vw;\n position: fixed;\n left: 0;\n max-width: 100vw;\n }\n .glossarySearchForm .gloss_container .input_bordering {\n border-radius: 0;\n border-left: 0 !important;\n border-right: 0 !important;\n box-shadow: -2px 3px 9px 0px #0008;\n }\n .imageField .btn.linkButton a {\n padding-left: 12px;\n padding-top: 3px;\n height: 19px;\n }\n .checkboxGroup:not(.asButtons) .btn a:before, .radioGroup:not(.asButtons) .btn a:before {\n border: 1px solid #d9d9d9;\n }\n .errorMsgBlock:after {\n border-color:transparent;\n border-top-color: rgb(250, 112, 103);\n }\n}\n\n";
6549$('head').append('<meta name="referrer" content="no-referrer" />');
6550document.ready = new Promise(function (resolve) {
6551 if (document.readyState === 'complete') {
6552 resolve();
6553 } else {
6554 var onReady = function onReady() {
6555 resolve();
6556 document.removeEventListener('DOMContentLoaded', onReady, true);
6557 window.removeEventListener('load', onReady, true);
6558 };
6559
6560 document.addEventListener('DOMContentLoaded', onReady, true);
6561 window.addEventListener('load', onReady, true);
6562 }
6563});
6564
6565function extend(ParentClass, constructorFunction, prototypeHash) {
6566 constructorFunction = constructorFunction || function () {};
6567
6568 prototypeHash = prototypeHash || {};
6569
6570 if (ParentClass) {
6571 constructorFunction.prototype = Object.create(ParentClass.prototype);
6572 }
6573
6574 for (var i in prototypeHash) {
6575 constructorFunction.prototype[i] = prototypeHash[i];
6576 }
6577
6578 return constructorFunction;
6579}