· 6 years ago · Mar 30, 2020, 12:08 AM
1var _this8 = void 0;
2
3function _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); }
4
5function _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; } } }; }
6
7function _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); }
8
9function _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; }
10
11/*
12*
13* Методы класса Assignment
14*
15*/
16window.theOneAssignmentMethods = {};
17/*
18*
19* Методы класса Task
20*
21*/
22
23window.theOneTaskPrototype = {
24 //Тут проводятся все манипуляции со входной спекой и ШВшными JSONками.
25 getTemplateData: function getTemplateData() {
26 window.currentRenderingTask = this;
27
28 if (!this.theOneSolution) {
29 this.theOneSolution = {
30 __theOneCustomStorage: {
31 __actionLog: [],
32 __timingsStorage: {
33 total: 0
34 },
35 __uploadingFiles: {}
36 }
37 };
38 }
39
40 var templateData = TolokaHandlebarsTask.prototype.getTemplateData.call(this); //Объект, в котором будут храниться все обработчики событий для элементов из JSONок
41
42 this.elementEventHandlers = {}; //Установка флагов режима только для чтения и просмотра выполненных заданий
43
44 if (this.getWorkspaceOptions().isReadOnly) {
45 this.isReadOnly = true;
46 }
47
48 if (this.getWorkspaceOptions().isReviewMode) {
49 this.isReviewMode = true;
50 } //Установка мобильного флага для Handlebars
51
52
53 if (this.getWorkspaceOptions().agent !== 'FRONTEND') {
54 templateData.thisIsMobileDevice = true;
55 window.thisIsMobileDevice = true;
56 } //Если это режим просмотра выполненных заданий - нужно позаботиться о том, чтобы в шаблоне отображалась соответствующая солюшке картина
57
58
59 if (this.isReviewMode || this.isReadOnly) {
60 if (usingOneRawData && this.getSolution().output_values.__theOneRawData) {
61 this.startingSolution = JSON.parse(this.getSolution().output_values.__theOneRawData);
62
63 if (printSolution) {
64 console.log('Стартовая солюшка из __theOneRawData: ', this.startingSolution);
65 }
66 } else {
67 this.startingSolution = this.flattenSolution(this.getSolution().output_values);
68
69 if (printSolution) {
70 console.log('Стартовая солюшка: ', this.getSolution().output_values, ', преобразована в: ', this.startingSolution);
71 }
72 }
73 }
74
75 templateData = setTemplateData.apply(this, [templateData]);
76 return templateData;
77 },
78 //Распрямляет солюшку, превращая её в одноэтажный объект. Это чтобы когда солюшка со сложной структурой пришла на вход, ШВ мог из неё вытянуть состояние всех своих контролов.
79 //Вам сюда лезть не надо.
80 flattenSolution: function flattenSolution(solution) {
81 var flatSolution = {};
82
83 function addToFlat(o, name) {
84 if (Object.prototype.toString.call(o) === "[object Object]") {
85 for (var subO in o) {
86 addToFlat(o[subO], subO);
87 }
88 } else {
89 flatSolution[name] = o;
90 }
91 }
92
93 for (var o in solution) {
94 addToFlat(solution[o], o);
95 } //Если одно и то же поле маппится в несколько разных - предпочтение отдаем тому, которое маппится без функции.
96
97
98 var thereWasAFunction = [];
99
100 if (useSpecMapper) {
101 for (var prop in solution) {
102 if (specMapper[prop] && specMapper[prop].from) {
103 var fromNames = Array.isArray(specMapper[prop].from) ? specMapper[prop].from : [specMapper[prop].from];
104
105 var _iterator = _createForOfIteratorHelper(fromNames),
106 _step;
107
108 try {
109 for (_iterator.s(); !(_step = _iterator.n()).done;) {
110 var origName = _step.value;
111
112 if (!flatSolution[origName] || !Array.isArray(flatSolution[origName]) && thereWasAFunction[origName] && !specMapper[prop].f && !specMapper[prop].function) {
113 flatSolution[origName] = solution[prop];
114 thereWasAFunction[origName] = specMapper[prop].f || specMapper[prop].function;
115 } else if (Array.isArray(flatSolution[origName]) && Array.isArray(solution[prop])) {
116 var _iterator2 = _createForOfIteratorHelper(solution[prop]),
117 _step2;
118
119 try {
120 for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
121 var e = _step2.value;
122
123 if (!flatSolution[origName].includes(e)) {
124 flatSolution[origName].push(e);
125 }
126 }
127 } catch (err) {
128 _iterator2.e(err);
129 } finally {
130 _iterator2.f();
131 }
132 }
133 }
134 } catch (err) {
135 _iterator.e(err);
136 } finally {
137 _iterator.f();
138 }
139 }
140 }
141 }
142
143 return flatSolution;
144 },
145 //"Придумывает" уникальное в рамках таска имя для чего-нибудь
146 comeUpWithAnUniqueName: function comeUpWithAnUniqueName(justNumber) {
147 if (!this.uniqueNameCounter) {
148 this.uniqueNameCounter = 0;
149 }
150
151 return "".concat(justNumber ? '' : 'uniqueAutoName').concat(this.uniqueNameCounter++);
152 },
153 //Для случаев, когда для имён нужно получить слаги, а потом сконвертировать обратно в имена - эти две функции
154 getSlugForName: function getSlugForName(name) {
155 var dictionary = this.getStorageItem('__uniqueSlugsDictionary') || {};
156
157 for (var _slug in dictionary) {
158 if (dictionary[_slug] === name) {
159 return _slug;
160 }
161 }
162
163 var slug = this.comeUpWithAnUniqueName();
164 dictionary[slug] = name;
165 this.setStorageItem('__uniqueSlugsDictionary', dictionary);
166 return slug;
167 },
168 getNameForSlug: function getNameForSlug(slug) {
169 var dictionary = this.getStorageItem('__uniqueSlugsDictionary') || {};
170 return dictionary[slug] || false;
171 },
172 //Парсит входную JSONку и делает с ней необходимые операции прежде чем отдать её во входную спеку.
173 //Вам сюда тоже лезть не надо.
174 getJSON: function getJSON(JSON, namesPostfix) {
175 var task = this,
176 json =
177 /*stringified*/
178 JSON,
179 postfix = '__clone_' + namesPostfix;
180
181 var getElementByName = function getElementByName(name) {
182 var _iterator3 = _createForOfIteratorHelper(this),
183 _step3;
184
185 try {
186 for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
187 var e = _step3.value;
188
189 if (e.name && e.name === name) {
190 return e;
191 }
192 }
193 } catch (err) {
194 _iterator3.e(err);
195 } finally {
196 _iterator3.f();
197 }
198
199 return false;
200 };
201
202 var getElementIndexByName = function getElementIndexByName(name) {
203 for (var i in this) {
204 if (this[i].name && this[i].name === name) {
205 return i;
206 }
207 }
208
209 return false;
210 };
211
212 var setPropValue = function setPropValue(propName, value) {
213 this[propName] = value;
214 };
215
216 function initEventHandlers(element, fNames, namePrefix) {
217 var eHandlers = {};
218
219 var _iterator4 = _createForOfIteratorHelper(fNames),
220 _step4;
221
222 try {
223 for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
224 var hName = _step4.value;
225
226 if (element[hName] && typeof element[hName] == 'function') {
227 eHandlers[hName] = element[hName];
228 }
229 }
230 } catch (err) {
231 _iterator4.e(err);
232 } finally {
233 _iterator4.f();
234 }
235
236 if (Object.keys(eHandlers)) {
237 if (!element.name) {
238 element.name = task.comeUpWithAnUniqueName();
239 }
240
241 task.elementEventHandlers[namePrefix ? "".concat(namePrefix, "___").concat(element.name) : element.name] = eHandlers;
242 }
243 }
244
245 function _initJSONArray(json, isRecursive) {
246 Array.from(json).forEach(function (element) {
247 if (!element.name) {
248 element.name = task.comeUpWithAnUniqueName();
249 } //Первым делом добавляем к имени постфикс, если он есть (когда мы в setTemplateData множим какую-то джсонку)
250
251
252 if (namesPostfix || namesPostfix === 0) {
253 element.name += postfix;
254 } //Еслим мы в режиме просмотра размеченного задания и есть солюшка
255
256
257 if (!isRecursive && task.startingSolution) {
258 element = task.prepareJSONElement(element);
259 }
260
261 if (element.type && element.type === 'group') {
262 initJSONArray(element.groupFields, 'recursive'); //Функция получения порядкового номера элемента по имени
263
264 element.groupFields.getElementIndexByName = getElementIndexByName; //Функция получения элемента по имени
265
266 element.groupFields.getElementByName = getElementByName;
267 } else {
268 //В целях оптимизации запоминаем, в каких именно элементах JSONки спеки содержится элемент с данным именем
269 task.theOneSpec.specByName[element.name] = JSON; //Функция установки свойству определённого значения
270
271 element.setPropValue = setPropValue; //Если это элемент с массивом кнопок
272
273 if (element.buttons) {
274 //Функция получения кнопки по имени
275 element.buttons.getByName = getElementByName; //Функция получения порядкового номера кнопки по имени
276
277 element.buttons.getIndexByName = getElementIndexByName; //Функция добавления кнопки элементу в конец
278
279 element.buttons.add = function (buttJSON) {
280 buttJSON.setPropValue = setPropValue;
281 initEventHandlers(buttJSON, ['onClick', 'onActivation', 'onDeactivation'], element.name);
282 element.buttons.push(buttJSON);
283 }; //Функция добавления кнопки элементу в начало
284
285
286 element.buttons.addToStart = function (buttJSON) {
287 buttJSON.setPropValue = setPropValue;
288 element.buttons.unshift(buttJSON);
289 }; //Функция удаления кнопки у элемента по имени или индексу
290
291
292 element.buttons.remove = function (nameOrIndex) {
293 var index;
294
295 if (typeof nameOrIndex == "string") {
296 index = element.buttons.getButtonIndexByName(nameOrIndex);
297
298 if (!index) {
299 return;
300 }
301 } else {
302 index = nameOrIndex;
303 }
304
305 delete task.elementEventHandlers[this[index].name];
306 element.buttons.splice(index, 1);
307 }; //Проверяем кнопки на наличие обработчиков, и при необходимости обрабатываем их
308
309
310 var _iterator5 = _createForOfIteratorHelper(element.buttons),
311 _step5;
312
313 try {
314 for (_iterator5.s(); !(_step5 = _iterator5.n()).done;) {
315 var butt = _step5.value;
316
317 if (!butt.name) {
318 butt.name = task.comeUpWithAnUniqueName();
319 } //Добавляем указанный постфикс ссылкам на другие элементы, которым мы уже постфикс добавили
320
321
322 if (namesPostfix || namesPostfix === 0) {
323 if (butt.properties) {
324 for (var _i = 0, _arr = ['includes', 'excludes']; _i < _arr.length; _i++) {
325 var propName = _arr[_i];
326
327 if (butt.properties[propName]) {
328 for (var e in butt.properties[propName]) {
329 butt.properties[propName][e] += postfix;
330 }
331 }
332 }
333 }
334 } //Функция установки свойству определённого значения
335
336
337 butt.setPropValue = setPropValue;
338 initEventHandlers(butt, ['onClick', 'onActivation', 'onDeactivation'], element.name);
339 }
340 } catch (err) {
341 _iterator5.e(err);
342 } finally {
343 _iterator5.f();
344 }
345 } //Обработка инпутов и текстарей
346
347
348 if (['text', 'textarea', 'integer', 'float', 'link', 'date', 'email'].includes(element.type)) {
349 initEventHandlers(element, ['onInput', 'validation']);
350 }
351 }
352 });
353 }
354
355 var initJSONArray = _initJSONArray.bind(task);
356
357 initJSONArray(json); //Функция получения порядкового номера элемента по имени
358
359 json.getElementIndexByName = getElementIndexByName; //Функция получения элемента по имени
360
361 json.getElementByName = getElementByName;
362 return json;
363 },
364 //Рекурсивная функция для подготовки входной JSONки. Если на вход пришла солюшка - раскидывает значения из неё по описанным в JSONке элементам.
365 //Вам сюда уж точно лезть не нужно.
366 prepareJSONElement: function prepareJSONElement(e, solution) {
367 var task = this;
368
369 if (!solution) {
370 solution = task.startingSolution;
371 }
372
373 if (!e.type) {
374 return e;
375 }
376
377 function putSelectedToFlags(flags) {
378 if (!flags) {
379 flags = [];
380 }
381
382 if (!flags.includes('selected')) {
383 flags.push('selected');
384 }
385
386 return flags;
387 }
388
389 function removeSelectedFromFlags(flags) {
390 if (flags && flags.includes('selected')) {
391 flags.splice(flags.indexOf('selected'), 1);
392 }
393
394 return flags;
395 }
396
397 switch (e.type) {
398 case 'date': //Поле со вводом даты. Обычный инпут.
399
400 case 'text': //Поле со вводом текста. Обычный инпут.
401
402 case 'email': //Поле со вводом мыла. Обычный инпут.
403
404 case 'link': //Поле со вводом ссылки. Обычный инпут.
405
406 case 'integer': //Поле со вводом целого числа. Обычный инпут.
407
408 case 'float': //Поле со вводом дробного числа. Обычный инпут.
409
410 case 'textarea':
411 if (solution[e.name]) {
412 e.value = escapeHTML(solution[e.name]);
413 } else {
414 e.value = '';
415 }
416
417 break;
418
419 case 'switchButton':
420 if (solution[e.name]) {
421 e.flags = putSelectedToFlags(e.flags);
422 } else {
423 e.flags = removeSelectedFromFlags(e.flags);
424 }
425
426 break;
427
428 case 'radioGroup':
429 e.buttons.forEach(function (butt) {
430 if (butt.name === solution[e.name]) {
431 butt.flags = putSelectedToFlags(butt.flags);
432 } else {
433 butt.flags = removeSelectedFromFlags(butt.flags);
434 }
435 });
436 break;
437
438 case 'checkboxGroup':
439 e.buttons.forEach(function (butt) {
440 if (solution[e.name] && solution[e.name].includes(butt.name)) {
441 butt.flags = putSelectedToFlags(butt.flags);
442 } else {
443 butt.flags = removeSelectedFromFlags(butt.flags);
444 }
445 });
446 break;
447
448 case 'buttonGroup':
449 e.buttons.forEach(function (butt) {
450 if (solution[butt.name]) {
451 butt.flags = putSelectedToFlags(butt.flags);
452 } else {
453 butt.flags = removeSelectedFromFlags(butt.flags);
454 }
455 });
456 break;
457
458 case 'group':
459 if (e.name && solution[e.name] && e.flags && e.flags.includes('infinite')) {
460 //Обработка "бесконечных" групп
461 var newGroupFields = [];
462 Array.from(solution[e.name]).forEach(function (sol) {
463 e.groupFields.forEach(function (groupField) {
464 var newField = $.extend(true, {}, groupField);
465 newField = task.prepareJSONElement(newField, sol);
466 newGroupFields.push(newField);
467 });
468 });
469 e.groupFields = newGroupFields;
470 } else {
471 //Обработка обычных групп
472 e.groupFields.forEach(function (groupField) {
473 groupField = task.prepareJSONElement(groupField);
474 });
475 }
476
477 break;
478 }
479
480 return e;
481 },
482 //Классический Толоковский онРендер. Дополняйте как хотите после комментариев "Кастомные штуки" в зависимости от того, когда именно должен выполниться ваш код.
483 onRender: function onRender() {
484 var _this2 = this;
485
486 var root = this.getDOMElement(),
487 task = this;
488 this.errorMSGs = []; //Инициализируем модальные окна
489
490 this.initModalWindows(); //Биндим события на статические элементы
491
492 this.initFieldsHere(root); //Инициализация кастомных элементов заголовка
493
494 if (window.customTitleElements) {
495 var _iterator6 = _createForOfIteratorHelper(customTitleElements),
496 _step6;
497
498 try {
499 for (_iterator6.s(); !(_step6 = _iterator6.n()).done;) {
500 var e = _step6.value;
501
502 if (e.onRender) {
503 e.onRender.apply(this, [root.querySelector('.blockTitle.' + e.name)]);
504 }
505 }
506 } catch (err) {
507 _iterator6.e(err);
508 } finally {
509 _iterator6.f();
510 }
511 } //[Директ] - если чел кликал по ссылке в баннере последние 24 часа - убираем анкликед и прописываем визитед
512
513
514 if (!notForDirect && this.storage) checkDomain: {
515 var link = root.querySelector('.bannerTitle a');
516
517 if (!link) {
518 break checkDomain;
519 }
520
521 var domain = link.dataset.domain;
522
523 if (!domain) {
524 break checkDomain;
525 }
526
527 var result = this.storage.getItem('funfrogDirectDomainVisited_' + domain);
528
529 if (result) {
530 link.parentElement.classList.add('visited');
531 link.classList.remove('unclicked');
532 } else {
533 link.addEventListener('click', function () {
534 link.parentElement.classList.add('visited');
535 task.storage.setItem('funfrogDirectDomainVisited_' + domain, true); //Теперь надо перелопатить все ссылки на этот домен во всем сьюте и им тоже прописать visited и убрать анкликед
536
537 var _iterator7 = _createForOfIteratorHelper(root.parentElement.querySelectorAll('.bannerTitle a')),
538 _step7;
539
540 try {
541 for (_iterator7.s(); !(_step7 = _iterator7.n()).done;) {
542 var otherLink = _step7.value;
543
544 if (otherLink.dataset.domain !== domain) {
545 continue;
546 }
547
548 otherLink.parentElement.classList.add('visited');
549 otherLink.classList.remove('unclicked');
550 task.turnOffErrorClassOnParentElementTagBlock(otherLink);
551 }
552 } catch (err) {
553 _iterator7.e(err);
554 } finally {
555 _iterator7.f();
556 }
557 });
558 }
559 } //На все некликанные ссылки вешаем обработчик, чтобы они стали кликанными при кликании по ним
560
561 var _iterator8 = _createForOfIteratorHelper(root.querySelectorAll('a.unclicked')),
562 _step8;
563
564 try {
565 for (_iterator8.s(); !(_step8 = _iterator8.n()).done;) {
566 var _link = _step8.value;
567
568 _link.addEventListener('click', function () {
569 task.turnOffErrorClassOnParentElementTagBlock(this);
570 this.classList.remove('unclicked');
571 });
572 } //Контекстные менюшки
573
574 } catch (err) {
575 _iterator8.e(err);
576 } finally {
577 _iterator8.f();
578 }
579
580 if (contextMenu && customContextMenu) {
581 root.addEventListener('contextmenu', function (e) {
582 _this2.handleContextMenuOpen(e);
583 });
584 }
585
586 document.ready.then(function () {
587 //Если по чему-то надо кликать-кликаем
588 Array.from(root.querySelectorAll('.toBeClickedAtStart')).forEach(function (clickTarget) {
589 try {
590 task.getAssignment().getTaskSuite().focusElement(clickTarget.dataset.number);
591 } catch (e){
592 clickTarget.click();
593 }
594
595 clickTarget.classList.remove('toBeClickedAtStart');
596 }); //Если на старте есть солюшка - применяем её
597
598 if (!task.startingSolution && Object.keys(_this2.theOneSolution).length > 1) {
599 _this2.loadSolution(_this2.theOneSolution);
600 } //Если у нас режим просмотра выполенных заданий - удаляем все пустые бесконечные группы
601
602
603 if (_this2.isReadOnly) {
604 var emptyGroups = root.querySelectorAll('.groupContainer.infinite>.group:not(:first-child):not(.hasSomething)');
605
606 if (emptyGroups) {
607 for (var i = 0; i < emptyGroups.length; i++) {
608 emptyGroups[i].parentElement.removeChild(emptyGroups[i]);
609 }
610 }
611 }
612
613 task.rendered = true; //Кастомные штуки
614
615 if (window.onTaskReady) {
616 onTaskReady.apply(task, [root]);
617 }
618 }).catch(function (err) {
619 return console.error(err);
620 }); //Кастомные штуки
621
622 if (window.onTaskRender) {
623 onTaskRender.apply(this, [root]);
624 }
625 },
626 //Закрывает ШВшные контекстные меню
627 closeAllContextMenus: function closeAllContextMenus() {
628 var root = this.getDOMElement();
629
630 var _iterator9 = _createForOfIteratorHelper(root.querySelectorAll('.customContextMenu')),
631 _step9;
632
633 try {
634 for (_iterator9.s(); !(_step9 = _iterator9.n()).done;) {
635 var oldMenu = _step9.value;
636 oldMenu.parentElement.removeChild(oldMenu);
637 }
638 } catch (err) {
639 _iterator9.e(err);
640 } finally {
641 _iterator9.f();
642 }
643
644 this.customContextMenuOpened = false;
645 },
646 //Открывает контекстное меню
647 handleContextMenuOpen: function handleContextMenuOpen(event) {
648 var _this3 = this;
649
650 var menuScreenMargin = 20,
651 root = this.getDOMElement(); //Make sure ни одно контекстное меню не открыто
652
653 this.closeAllContextMenus();
654
655 if (event.target.closest('.customContextMenuItemsContainer')) {
656 return;
657 }
658
659 var menu = document.createElement('div');
660 menu.className = 'customContextMenu';
661 menu.innerHTML = '<div class="customContextMenuItemsContainer alien"><div class="customContextMenuRightClickField">Правый клик сюда<br>откроет меню браузера</div></div>';
662 var itemsContainer = menu.querySelector('.customContextMenuItemsContainer');
663 var itemsArray = [];
664
665 var addItem = function addItem(item) {
666 if (item.isVisible && !item.isVisible.apply(_this3, [])) {
667 return;
668 }
669
670 itemsArray.push(item);
671 };
672
673 var _iterator10 = _createForOfIteratorHelper(customContextMenu.items),
674 _step10;
675
676 try {
677 for (_iterator10.s(); !(_step10 = _iterator10.n()).done;) {
678 var itemName = _step10.value;
679
680 if (standardContextMenuItems[itemName]) {
681 addItem(standardContextMenuItems[itemName]);
682 }
683 }
684 } catch (err) {
685 _iterator10.e(err);
686 } finally {
687 _iterator10.f();
688 }
689
690 var _iterator11 = _createForOfIteratorHelper(customContextMenu.customItems),
691 _step11;
692
693 try {
694 for (_iterator11.s(); !(_step11 = _iterator11.n()).done;) {
695 var item = _step11.value;
696 addItem(item);
697 }
698 } catch (err) {
699 _iterator11.e(err);
700 } finally {
701 _iterator11.f();
702 }
703
704 if (itemsArray.length < 1) {
705 return;
706 }
707
708 event.preventDefault();
709 this.customContextMenuOpened = true;
710
711 var handleRootClick = function handleRootClick(e) {
712 if (!e.target.closest('.customContextMenu')) {
713 _this3.closeAllContextMenus();
714
715 root.removeEventListener('click', handleRootClick);
716 }
717 };
718
719 root.addEventListener('click', handleRootClick);
720 itemsContainer.innerHTML += getFieldsHTML(this.getJSON([{
721 type: "actionButtonGroup",
722 name: "customContextMenuActionButtons",
723 flags: ["block", "unfocusable"],
724 buttons: itemsArray
725 }]));
726 root.appendChild(menu);
727 this.initFieldsHere(itemsContainer); //console.log(event);
728
729 var task = fullScreenMode ? event.target.closest('.block.main') || event.target.closest('.windowContent') || event.target.closest('.task') : event.target.closest('.windowContent') || event.target.closest('.task');
730
731 if (!task) {
732 menu.style.left = event.clientX + 'px';
733 menu.style.top = event.clientY + 'px';
734 } else {
735 var menuRect = menu.getBoundingClientRect(),
736 taskRect = task.getBoundingClientRect();
737
738 if (menuRect.width + event.clientX + menuScreenMargin > taskRect.width) {
739 menu.style.left = event.clientX - menuRect.width + 'px';
740 } else {
741 menu.style.left = event.clientX + 'px';
742 }
743
744 if (menuRect.height + event.clientY + menuScreenMargin > taskRect.height) {
745 menu.style.top = event.clientY - menuRect.height + 'px';
746 } else {
747 menu.style.top = event.clientY + 'px';
748 }
749 }
750 },
751 //Инициализация всех используемых в шаблоне модальных окон
752 initModalWindows: function initModalWindows() {
753 var _this4 = this;
754
755 var root = this.getDOMElement(),
756 task = this,
757 _window = window;
758 this.windows = {};
759 this.activeWindowsChain = []; //Все открытые в данный момент окна, в порядке открытия
760
761 if (!window.modalWindows) {
762 window.modalWindows = {};
763 } //Добавляем ШВшные окна
764
765
766 modalWindows._fileWithSameNameAlreadyUploadedMessageWindow = {
767 title: 'Ой!',
768 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!",
769 buttons: [{
770 caption: 'Понятно.',
771 f: function f(window) {
772 window.close();
773 }
774 }]
775 };
776
777 var _loop = function _loop(windowName) {
778 var windowObj = modalWindows[windowName];
779 var window = root.querySelector('.window.' + windowName);
780
781 if (!window) {
782 //Окно не описали в Handlebars - значит сами его создадим
783 var div = document.createElement('div');
784 div.innerHTML = _this4.compileHandlebars("{{#window name=\"".concat(windowName, "\"}}{{/window}}"));
785 window = div.querySelector('.window.' + windowName);
786 root.appendChild(window);
787 }
788
789 if (windowObj.flags && windowObj.flags.includes('temporary')) {
790 window.isTemporary = true;
791 }
792
793 window.className += flagsToClassName(windowObj.flags);
794
795 window.open = function () {
796 window.classList.add('opened');
797 _window.activeWindow = window;
798 task.activeWindowsChain.push(window);
799 window.content.scrollTop = 0;
800
801 if (window.isTemporary) {
802 window.setContent(windowObj.content);
803 task.initFieldsHere(window.content);
804 } //Обновлянем все просмотрщики с канвасами
805
806
807 var _iterator12 = _createForOfIteratorHelper(window.querySelectorAll('.imageField .imageBlock canvas')),
808 _step12;
809
810 try {
811 for (_iterator12.s(); !(_step12 = _iterator12.n()).done;) {
812 var c = _step12.value;
813
814 if (c.initCanvas) {
815 c.initCanvas();
816 }
817 }
818 } catch (err) {
819 _iterator12.e(err);
820 } finally {
821 _iterator12.f();
822 }
823
824 clearTimeout(_window.focusTimeout);
825 _window.focusTimeout = setTimeout(function () {
826 task.getAssignment().getTaskSuite().focusNextElement(!!(windowObj.flags && windowObj.flags.includes('noScrollAfterOpening')));
827 }, 100);
828 if (window.onOpen) window.onOpen();
829 return new Promise(function (resolve, reject) {
830 window.resolve = function (obj) {
831 window.isResolved = true;
832 resolve(obj);
833 };
834
835 window.reject = function (obj) {
836 reject(obj);
837 };
838 });
839 };
840
841 window.close = function () {
842 window.classList.remove('opened');
843 task.activeWindowsChain.splice(task.activeWindowsChain.indexOf(window), 1);
844 _window.activeWindow = task.activeWindowsChain[task.activeWindowsChain.length - 1];
845 clearTimeout(_window.focusTimeout);
846 _window.focusTimeout = setTimeout(function () {
847 task.getAssignment().getTaskSuite().focusNextElement(!!(windowObj.flags && windowObj.flags.includes('noScrollAfterClosing')));
848 }, 100);
849 if (window.onClose) window.onClose();
850
851 if (window.isTemporary) {
852 window.setContent('');
853 }
854
855 if (!window.isResolved && window.reject) {
856 window.reject();
857 }
858
859 delete window.isResolved;
860 };
861
862 if (windowObj.onOpen) window.onOpen = function () {
863 windowObj.onOpen.apply(task, [window]);
864 };
865 if (windowObj.onRender) window.onRender = function () {
866 windowObj.onRender.apply(task, [window]);
867 };
868 if (windowObj.onClose) window.onClose = function () {
869 windowObj.onClose.apply(task, [window]);
870 };
871 var buttonsHTML = '';
872
873 if (windowObj.buttons) {
874 var buttonsJSON = {
875 type: "actionButtonGroup",
876 class: "noIcon windowButtons_" + windowName,
877 buttons: []
878 };
879 var i = 0;
880
881 var _iterator13 = _createForOfIteratorHelper(windowObj.buttons),
882 _step13;
883
884 try {
885 for (_iterator13.s(); !(_step13 = _iterator13.n()).done;) {
886 var butt = _step13.value;
887 butt.number = i++;
888 buttonsJSON.buttons.push({
889 iconClass: 'winButton' + butt.number,
890 caption: butt.caption,
891 name: butt.name
892 });
893 }
894 } catch (err) {
895 _iterator13.e(err);
896 } finally {
897 _iterator13.f();
898 }
899
900 buttonsHTML = handleFieldAndGetHTML.apply(_this4, [buttonsJSON]);
901 }
902
903 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>");
904
905 if (windowObj.buttons) {
906 var _iterator14 = _createForOfIteratorHelper(windowObj.buttons),
907 _step14;
908
909 try {
910 var _loop2 = function _loop2() {
911 var butt = _step14.value;
912
913 if (butt.f) {
914 window.querySelector('.windowButtons .winButton' + butt.number).addEventListener('click', function (e) {
915 butt.f.apply(task, [window]);
916 });
917 }
918 };
919
920 for (_iterator14.s(); !(_step14 = _iterator14.n()).done;) {
921 _loop2();
922 }
923 } catch (err) {
924 _iterator14.e(err);
925 } finally {
926 _iterator14.f();
927 }
928 } //Установка заголовка
929
930
931 window.titleBlock = window.querySelector('.blockTitle');
932
933 window.setTitle = function (title) {
934 window.titleBlock.innerHTML = title;
935 window.classList.add('hasTitle');
936 };
937
938 window.clearTitle = function () {
939 window.titleBlock.innerHTML = '';
940 window.classList.remove('hasTitle');
941 };
942
943 if (windowObj.title) {
944 window.setTitle(windowObj.title);
945 } //Установка содержимого
946
947
948 window.content = window.querySelector('.windowContent');
949
950 window.setContent = function (content) {
951 window.content.innerHTML = task.compileHandlebars(content);
952 };
953
954 window.clearContent = function () {
955 window.content.innerHTML = '';
956 };
957
958 if (windowObj.content && !window.isTemporary) {
959 window.setContent(windowObj.content);
960 }
961
962 _this4.windows[windowName] = window;
963 if (window.onRender) window.onRender();
964 };
965
966 for (var windowName in modalWindows) {
967 _loop(windowName);
968 }
969 },
970 //Обработчик, выполняющийся при любой смене ШВшной солюшки.
971 onSolutionChange: function onSolutionChange(solution) {
972 if (!this.rendered) {
973 return;
974 }
975
976 if (!solution) {
977 var eAndS = this.gatherErrorsAndSolution('Без вывода ошибок');
978 solution = eAndS.solution; //Проверки
979
980 if (window.solutionChecks) {
981 solutionChecks.apply(this, [solution]);
982 }
983 } //Если в солюшке есть кастомный объект - сохраняем его, чтобы не затёрся
984
985
986 if (this.theOneSolution && this.theOneSolution.__theOneCustomStorage) {
987 solution.__theOneCustomStorage = this.theOneSolution.__theOneCustomStorage;
988 }
989
990 this.theOneSolution = solution;
991 this.theOneSolutionUpdated = true;
992 },
993 //Очищает значения(солюшку) на всех контролах в указанном месте
994 clearSolutionHere: function clearSolutionHere(where) {
995 var root = this.getDOMElement(),
996 task = this;
997 Array.from(where.querySelectorAll('.btn.active')).forEach(function (btn) {
998 if (btn.closest('.alien')) {
999 return;
1000 }
1001
1002 task.buttonSmashed(btn, true);
1003 task.checksAfterValidation(btn);
1004 });
1005 Array.from(where.querySelectorAll('input, textarea')).slice().reverse().forEach(function (input) {
1006 if (input.closest('.alien')) {
1007 return;
1008 }
1009
1010 input.value = '';
1011 input.classList.remove('validationRejected', 'validationPassed');
1012 task.checksAfterValidation(input);
1013 });
1014 this.onSolutionChange();
1015 },
1016 //Загружает в таск переданную солюшку
1017 loadSolution: function loadSolution(solution, where) {
1018 var task = this,
1019 root = where ? where : this.getDOMElement();
1020
1021 if (!solution) {
1022 solution = this.theOneSolution;
1023 } //Обнуляем все контролы
1024
1025
1026 this.clearSolutionHere(root);
1027
1028 function recursiveApplying(solution, where) {
1029 var _loop3 = function _loop3(eName) {
1030 var e = void 0,
1031 ea = void 0;
1032
1033 if (where.classList.contains('groupContainer') && where.classList.contains('infinite')) {
1034 e = where.querySelector(':scope>.group:last-child .fieldName_' + eName + '');
1035 } else {
1036 ea = where.querySelectorAll('.fieldName_' + eName);
1037 e = ea[ea.length - 1];
1038 }
1039
1040 if (!e) {
1041 return "continue";
1042 } //Разбираемся, что это за элемент и в зависимости от этого применяем значение
1043
1044
1045 if (e.classList.contains('fieldType_textarea')) {
1046 var textarea = e.querySelector('textarea');
1047
1048 if (!textarea) {
1049 return "continue";
1050 }
1051
1052 textarea.value = solution[eName];
1053 task.validateInput(textarea);
1054 } else if (e.classList.contains('radioGroup') || e.classList.contains('select')) {
1055 var btn = e.querySelector('.fieldName_' + solution[eName] + ' .btn');
1056
1057 if (!btn) {
1058 return "continue";
1059 }
1060
1061 if (!isActive(btn)) task.buttonSmashed(btn);
1062 } else if (e.classList.contains('checkboxGroup')) {
1063 Array.from(solution[eName]).forEach(function (buttName) {
1064 var btn = e.querySelector('.fieldName_' + buttName + ' .btn');
1065
1066 if (!btn) {
1067 return;
1068 }
1069
1070 if (!isActive(btn)) task.buttonSmashed(btn);
1071 });
1072 } else if (e.classList.contains('buttDiv') && solution[eName]) {
1073 var _btn = e.querySelector('.btn');
1074
1075 if (!_btn) {
1076 return "continue";
1077 }
1078
1079 if (!isActive(_btn)) task.buttonSmashed(_btn);
1080 } 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')) {
1081 var input = e.querySelector('input');
1082
1083 if (!input) {
1084 return "continue";
1085 }
1086
1087 input.value = solution[eName];
1088 task.validateInput(input);
1089 } else if (e.classList.contains('fileField') && e.classList.contains('virtual') && solution[eName]) {
1090 var fuFiles = task.getDOMElement().querySelector('.__fileUploader .field_file__files'),
1091 fileDeleteButtons = fuFiles.querySelectorAll('.field_file__files__file .file__delete');
1092 e.clear();
1093
1094 if (fileDeleteButtons && fileDeleteButtons.length > 0) {
1095 task._fileUploader.uploadedFilesDivs = fuFiles.querySelectorAll('.field_file__files__file'); //Отрисовываем в контроле загруженные в нём файлы, которые есть в солюшке
1096
1097 var _iterator15 = _createForOfIteratorHelper(fileDeleteButtons),
1098 _step15;
1099
1100 try {
1101 for (_iterator15.s(); !(_step15 = _iterator15.n()).done;) {
1102 var fileDeleteButt = _step15.value;
1103 var attachmentId = fileDeleteButt.dataset.attachmentId;
1104 var item = task.getStorageItem('__uploadingFiles')[attachmentId];
1105
1106 if (item) {
1107 item.fileName = fileDeleteButt.parentElement.querySelector('.file__name').innerHTML;
1108
1109 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))) {
1110 item.fileField = e;
1111 item.fileLine = e.addFileLine(item.fileName, attachmentId);
1112 }
1113 }
1114 }
1115 } catch (err) {
1116 _iterator15.e(err);
1117 } finally {
1118 _iterator15.f();
1119 }
1120 }
1121
1122 e.validate({}, true);
1123 } else if (e.classList.contains('groupContainer')) {
1124 Array.from(solution[eName]).forEach(function (s) {
1125 recursiveApplying(s, e);
1126 task.checksAfterValidation(e);
1127 });
1128 }
1129 };
1130
1131 for (var eName in solution) {
1132 var _ret = _loop3(eName);
1133
1134 if (_ret === "continue") continue;
1135 }
1136 } //Применяем солюшку
1137
1138
1139 recursiveApplying(solution, root);
1140 this.onSolutionChange(solution);
1141 },
1142 //Инициализирует неинициализированные поля. Вешает на отрисованные ШВшные элементы обработчики событий. Вам сюда лезть не надо, на свои элементы вешайте всё, что хотите, в onRender.
1143 initFieldsHere: function initFieldsHere(whereToInit) {
1144 var task = this,
1145 root = this.getDOMElement();
1146
1147 if (!whereToInit) {
1148 return;
1149 } //Пробегаемся по всем инпутам и вешаем на них валидэйшн
1150
1151
1152 Array.from(whereToInit.querySelectorAll('input:not(.initialized), textarea:not(.initialized)')).forEach(function (input) {
1153 if (input.parentElement.classList.contains('searchField') || input.classList.contains('comment')) {
1154 return;
1155 }
1156
1157 input.addEventListener('input', function () {
1158 if (this.closest('.alien')) {
1159 return;
1160 } //Если у нас ридонли и инпут попытались поменять - возвращаем как было
1161
1162
1163 if (task.rendered && (task.isReadOnly || this.interfaceBlocked) && !this.closest('.notReadOnly')) {
1164 if (this.dataset.name && task.theOneSolution[this.dataset.name]) {
1165 if (this.value !== task.theOneSolution[this.dataset.name]) {
1166 this.value = task.theOneSolution[this.dataset.name];
1167 }
1168 } else {
1169 if (this.value !== '') {
1170 this.value = '';
1171 }
1172 }
1173 }
1174
1175 if (task.validateInput(this)) {
1176 task.lastChangedElement = this;
1177 }
1178
1179 task.checksAfterValidation(this);
1180 task.onSolutionChange(); //Проверки для обработчиков событий
1181
1182 var eHandlers = task.elementEventHandlers[input.dataset.name];
1183
1184 if (eHandlers) {
1185 if (eHandlers.onInput) {
1186 eHandlers.onInput.apply(task, [this]);
1187 }
1188 }
1189 });
1190
1191 if (window.actionLogger) {
1192 input.addEventListener('change', function () {
1193 //Логируем нажатие
1194 var charLimit = window.actionLoggerInputAndTextareaCharLimit || 201;
1195 task.logAction('inputChange', {
1196 buttonName: this.dataset.name,
1197 value: this.value.length < charLimit ? this.value : "[>".concat(charLimit, " \u0441\u0438\u043C\u0432\u043E\u043B\u043E\u0432]")
1198 });
1199 });
1200 }
1201
1202 task.validateInput(input);
1203 input.classList.add('initialized');
1204 }); //Теперь кнопки
1205
1206 Array.from(whereToInit.querySelectorAll('.btn:not(.initialized)')).forEach(function (btn) {
1207 btn.addEventListener('click', function (event) {
1208 if (btn.dataset.select && isActive(btn) && btn.closest('.cantUnselect')) {
1209 return;
1210 }
1211
1212 if (btn.closest('.readOnly') && task.rendered === true) {
1213 return;
1214 }
1215
1216 task.buttonSmashed(this);
1217
1218 if (!this.closest('.actionButtonGroup') && !this.closest('.linkButtonGroup')) {
1219 task.checksAfterValidation(this);
1220 task.onSolutionChange();
1221 } //Проверки для обработчиков событий
1222
1223
1224 var eventHandlersName = btn.closest('.element') && btn.closest('.element').dataset.name ? "".concat(btn.closest('.element').dataset.name, "___").concat(btn.dataset.name) : btn.dataset.name;
1225 var buttHandlers = task.elementEventHandlers[eventHandlersName];
1226
1227 if (buttHandlers) {
1228 if (buttHandlers.onClick) {
1229 buttHandlers.onClick.apply(task, [btn]);
1230 }
1231 } //Логируем нажатие
1232
1233
1234 var additional = {};
1235 var action = 'buttonClick';
1236
1237 if (this.dataset.name && this.dataset.name !== "undefined") {
1238 additional.buttonName = this.dataset.name;
1239 }
1240
1241 if (!this.closest('.actionButtonGroup') && !this.closest('.linkButtonGroup')) {
1242 additional.isActive = isActive(this);
1243 }
1244
1245 var e = this.closest('.element');
1246
1247 if (e && e.dataset.name) {
1248 if (!e.classList.contains('spoiler')) {
1249 additional.element = e.dataset.name;
1250 } else {
1251 additional.spoiler = e.dataset.name;
1252
1253 if (isActive(this)) {
1254 action = 'spoilerOpen';
1255 } else {
1256 action = 'spoilerClose';
1257 }
1258 }
1259 }
1260
1261 if (this.closest('.groupContainer') && this.closest('.groupContainer').dataset.name) {
1262 additional.group = this.closest('.groupContainer').dataset.name;
1263 }
1264
1265 task.logAction(action, additional);
1266 });
1267 btn.addEventListener('mousedown', function () {
1268 btn.classList.add('underPressure');
1269 });
1270 btn.addEventListener('mousedown', function () {
1271 btn.classList.add('touchstart');
1272 });
1273 btn.addEventListener('mouseup', function () {
1274 btn.classList.remove('underPressure');
1275 });
1276 btn.addEventListener('mouseleave', function () {
1277 btn.classList.remove('underPressure');
1278 });
1279 btn.addEventListener('touchend', function () {
1280 btn.classList.remove('underPressure');
1281 });
1282 btn.classList.add('initialized');
1283 }); //Селекты
1284
1285 Array.from(whereToInit.querySelectorAll('.select.element:not(.initialized)')).forEach(function (select) {
1286 select.querySelector('.selectButtons .arrow').addEventListener('click', function (e) {
1287 select.classList.toggle('opened');
1288 });
1289 select.querySelector('.selectDisplay').addEventListener('click', function (e) {
1290 select.classList.toggle('opened');
1291 });
1292
1293 select.onKey = function (code, e) {
1294 if (code === "Space" && (!document.activeElement || document.activeElement && !document.activeElement.closest('.noHotkeys'))) {
1295 e.preventDefault();
1296 select.querySelector('.selectButtons .arrow').click();
1297 }
1298 };
1299
1300 select.classList.add('initialized');
1301 }); //Спойлеры
1302
1303 Array.from(whereToInit.querySelectorAll('.spoiler.element:not(.initialized)')).forEach(function (spoiler) {
1304 spoiler.querySelector('.spoilerHead .btn').addEventListener('click', function (e) {
1305 spoiler.classList.toggle('opened');
1306
1307 if (spoiler.classList.contains('opened')) {//task.getAssignment().getTaskSuite().maybeScrollToThisElement(spoiler, true);
1308 }
1309 });
1310
1311 spoiler.onKey = function (code, e) {
1312 if (code === "Space" && (!document.activeElement || document.activeElement && !document.activeElement.closest('.noHotkeys'))) {
1313 e.preventDefault();
1314 spoiler.querySelector('.spoilerHead .btn').click();
1315 }
1316 };
1317
1318 spoiler.classList.add('initialized');
1319 }); //Поля загрузки файлов
1320
1321 var initFilesFields = function initFilesFields() {
1322 Array.from(whereToInit.querySelectorAll('.fileField.element:not(.initialized)')).forEach(function (fileField) {
1323 task.initFileField(fileField);
1324 });
1325 };
1326
1327 if (this.rendered) {
1328 initFilesFields();
1329 } else {
1330 document.ready.then(function () {
1331 initFilesFields();
1332 }).catch(function (err) {
1333 return console.error(err);
1334 });
1335 } //Инициализируем все ШВшные просмотрщики изображений
1336
1337
1338 Array.from(whereToInit.querySelectorAll('.element.imageField:not(.initialized)')).forEach(function (field) {
1339 initImageField(field);
1340 field.classList.add('initialized');
1341 }); //Вставляем СВГ-иконки
1342
1343 this.insertIcons(); //Биндим действия на все helpContainer'ы
1344
1345 var _iterator16 = _createForOfIteratorHelper(root.querySelectorAll('.helpContainer:not(.initialized)')),
1346 _step16;
1347
1348 try {
1349 for (_iterator16.s(); !(_step16 = _iterator16.n()).done;) {
1350 var q = _step16.value;
1351 initHelpContainer(q);
1352 } //Перестраиваем все горячие клавиши
1353
1354 } catch (err) {
1355 _iterator16.e(err);
1356 } finally {
1357 _iterator16.f();
1358 }
1359
1360 if (this.rendered) {
1361 this.getAssignment().getTaskSuite().initTheOneHotkeys();
1362 } //Пробегаемся по всем groupContainer'ам и рисуем в них первый набор полей (может и последний, в зависимости от параметра infinite)
1363
1364
1365 Array.from(whereToInit.querySelectorAll('.groupContainer:not(.alien)')).forEach(function (gc) {
1366 if (gc.closest('.alien') || gc.parentElement.classList.contains('group') && gc.parentElement !== whereToInit) {
1367 return;
1368 }
1369
1370 task.addGroup(gc);
1371 });
1372 },
1373 //После валидации иногда нужно проводить разные проверки, все они находятся тут.
1374 //Если вы будете ковырять и модифицировать стандартные "бесконечные" группы полей - возможно, надо будет тут чёнить дописать.
1375 checksAfterValidation: function checksAfterValidation(validatedElement) {
1376 //Проверки для элементов внутри групп
1377 var gc = validatedElement.closest('.groupContainer'),
1378 props = null;
1379
1380 if (gc) {
1381 //Валидируем группы в ГруппКонтейнере
1382 Array.from(gc.querySelectorAll(':scope>.group')).forEach(function (group) {
1383 //.hasSomething
1384 if (group.querySelector('.validationPassed') || group.querySelector('.validationRejected') || group.querySelector('.active')) {
1385 group.classList.add('hasSomething');
1386 } else {
1387 group.classList.remove('hasSomething');
1388 } //.correct
1389
1390
1391 if (!group.querySelector('.validationRejected') && !group.querySelector(':scope>.element .clear:not(.skipMe)')) {
1392 group.classList.add('correct');
1393 } else {
1394 group.classList.remove('correct');
1395 }
1396 }); //Если валидированный элемент принадлежит к группе с бесконечными элементами - надо, мб, сгенерировать новых или, наоборот, удалить лишние
1397
1398 if (isGroupInfinite(gc)) {
1399 //Добавление новых, если все группы в контейнере корректны
1400 if (!gc.querySelector(':scope>.group:not(.correct)') && (!gc.dataset.limit || gc.dataset.limit && gc.querySelectorAll(':scope>.group').length < parseInt(gc.dataset.limit))) {
1401 this.addGroup(gc);
1402 } //Удаление пустых
1403
1404
1405 var emptyGroups = gc.querySelectorAll(':scope>.group:not(.hasSomething)');
1406 var startFrom = 1;
1407
1408 if (gc.querySelectorAll(':scope>.group').length > 1 && gc.querySelector(':scope>.group.hasSomething:not(.correct)')) {
1409 startFrom = 0;
1410 }
1411
1412 if (emptyGroups && emptyGroups.length > startFrom) {
1413 for (var i = startFrom; i < emptyGroups.length; i++) {
1414 gc.removeChild(emptyGroups[i]);
1415 }
1416 }
1417 }
1418 }
1419 },
1420 //Выполняется для каждой активированной ШВшной кнопки, будь то свитчБаттон, радиобаттон или чекбокс - без разницы.
1421 //Если вам нужно что-то делать после нажатия на кнопки (пусть даже некоторые) - внизу функции есть отличное место для кастомного кода. Хотя пишите куда хотите))))
1422 buttonSmashed: function buttonSmashed(btn, isTemporary) {
1423 if (this.rendered && (this.interfaceBlocked || this.isReadOnly && !btn.closest('.notReadOnly') || btn.closest('.disabled'))) {
1424 return;
1425 }
1426
1427 var root = this.getDOMElement(),
1428 task = this;
1429
1430 if (!btn.dataset.type || window.buttonsDisabled) {
1431 return;
1432 }
1433
1434 this.hideTaskError();
1435 this.turnOffErrorClassOnParentElementTagBlock(btn);
1436 var props = null;
1437
1438 if (btn.dataset.props) {
1439 props = fieldsPropsArray[btn.dataset.props];
1440 } //Производит всякие проверки в зависимости от свойств этот кнопки
1441
1442
1443 function propsChecks() {
1444 //Создадим массив выключенных в данный момент групп, чтобы не включить ничего лишнего
1445 var excludedArr = [];
1446
1447 if (props.includes || props.excludes) {
1448 Array.from(root.querySelectorAll('.btn.active.excludesSomething')).forEach(function (btn) {
1449 var props;
1450
1451 if (btn.dataset.props) {
1452 props = fieldsPropsArray[btn.dataset.props];
1453 } else {
1454 return;
1455 }
1456
1457 if (props.excludes) {
1458 props.excludes.forEach(function (groupName) {
1459 excludedArr.push(groupName);
1460 });
1461 }
1462 });
1463 } //Если эта кнопка должна при включении что-то исключать или включать: делаем это
1464
1465
1466 if (props.excludes) {
1467 Array.from(props.excludes).forEach(function (excludedClass) {
1468 var excludedDivs = root.querySelectorAll(':not(.includes).' + excludedClass);
1469 Array.from(excludedDivs).forEach(function (excludedDiv) {
1470 if (isActive(btn)) {
1471 excludedDiv.classList.add('excluded'); //И снимаем там все галки
1472
1473 Array.from(excludedDiv.querySelectorAll('.btn.active')).forEach(function (btn) {
1474 btn.click();
1475 });
1476 } else {
1477 if (!excludedArr.includes(excludedClass) && !(excludedDiv.dataset.excluded && excludedDiv.dataset.excluded === 'excluded')) {
1478 excludedDiv.classList.remove('excluded'); //Обновлянем все просмотрщики с канвасами
1479
1480 var _iterator17 = _createForOfIteratorHelper(excludedDiv.querySelectorAll('.imageField .imageBlock canvas')),
1481 _step17;
1482
1483 try {
1484 for (_iterator17.s(); !(_step17 = _iterator17.n()).done;) {
1485 var c = _step17.value;
1486
1487 if (c.initCanvas) {
1488 c.initCanvas();
1489 }
1490 }
1491 } catch (err) {
1492 _iterator17.e(err);
1493 } finally {
1494 _iterator17.f();
1495 }
1496 }
1497 }
1498 });
1499 });
1500 }
1501
1502 if (props.includes) {
1503 Array.from(props.includes).forEach(function (includedClass) {
1504 //Сначала проверим, может ничего и не надо делать
1505 if (!isActive(btn) && root.querySelector('.active.includes.' + includedClass)) {
1506 return;
1507 }
1508
1509 var includedDivs = root.querySelectorAll(':not(.includes).' + includedClass);
1510 Array.from(includedDivs).forEach(function (includedDiv) {
1511 if (!isActive(btn)) {
1512 includedDiv.classList.add('excluded'); //И снимаем там все галки
1513
1514 if (includedDiv.querySelector('.btn.active')) {
1515 Array.from(includedDiv.querySelectorAll('.btn.active')).forEach(function (btn) {
1516 btn.click();
1517 });
1518 }
1519 } else {
1520 if (!excludedArr.includes(includedClass)) {
1521 includedDiv.classList.remove('excluded'); //Обновлянем все просмотрщики с канвасами
1522
1523 var _iterator18 = _createForOfIteratorHelper(includedDiv.querySelectorAll('.imageField .imageBlock canvas')),
1524 _step18;
1525
1526 try {
1527 for (_iterator18.s(); !(_step18 = _iterator18.n()).done;) {
1528 var c = _step18.value;
1529
1530 if (c.initCanvas) {
1531 c.initCanvas();
1532 }
1533 }
1534 } catch (err) {
1535 _iterator18.e(err);
1536 } finally {
1537 _iterator18.f();
1538 }
1539 }
1540 }
1541 });
1542 });
1543 } //valuesToWrite - массив обьектов, описывающий какое значение записать в определённое свойство объекта с указанным селектором
1544
1545
1546 if (props.valuesToWrite) {
1547 if (!Array.isArray(props.valuesToWrite)) {
1548 ce('valuesToWrite_is_not_an_array', btn.parentElement.className);
1549 } else {
1550 props.valuesToWrite.forEach(function (valObj) {
1551 var ok = true;
1552
1553 if (!valObj.selector) {
1554 ce('valuesToWrite_has_no_selector', btn.parentElement.className);
1555 ok = false;
1556 }
1557
1558 if (!valObj.property) {
1559 ce('valuesToWrite_has_no_property', btn.parentElement.className);
1560 ok = false;
1561 }
1562
1563 if (!valObj.value && valObj.value !== '') {
1564 ce('valuesToWrite_has_no_value', btn.parentElement.className);
1565 ok = false;
1566 }
1567
1568 if (ok === false) {
1569 return;
1570 }
1571
1572 var target = root.querySelector(valObj.selector);
1573
1574 if (!target) {
1575 ce('valuesToWrite_target_not_found', 'Класс кнопки: ' + btn.parentElement.className + ', селектор: ' + valObj.selector);
1576 ok = false;
1577 } else {
1578 target[valObj.property] = valObj.value;
1579 }
1580 });
1581 }
1582 }
1583 }
1584
1585 switch (btn.dataset.type) {
1586 case 'switchButton':
1587 //Если эта кнопка - часть группы радиобаттонов и её включили, то снимаем остальные радиобаттоны в этой группе
1588 if ((btn.dataset.radiogroup || btn.dataset.select) && !isActive(btn)) {
1589 Array.from(btn.parentElement.parentElement.querySelectorAll('.btn.active')).forEach(function (b) {
1590 task.buttonSmashed(b);
1591 });
1592 } //Перекрашиваем кнопку
1593
1594
1595 invertButton(btn);
1596
1597 if (props) {
1598 propsChecks();
1599 }
1600
1601 break;
1602
1603 case 'linkButton':
1604 if (props) {
1605 propsChecks();
1606 }
1607
1608 btn.classList.add('clicked');
1609 break;
1610 } //Если это кнопка селекта
1611
1612
1613 if (btn.dataset.select) {
1614 var select = btn.closest('.select'),
1615 selectText = select.querySelector('.selectText');
1616
1617 if (isActive(btn)) {
1618 selectText.innerHTML = btn.querySelector('a').innerHTML;
1619 select.dataset.selection = btn.dataset.name;
1620 } else {
1621 selectText.innerHTML = '';
1622 select.dataset.selection = '';
1623 }
1624
1625 select.classList.remove('opened');
1626 } //Проверки для обработчиков событий
1627
1628
1629 var eventHandlersName = btn.closest('.element') && btn.closest('.element').dataset.name ? "".concat(btn.closest('.element').dataset.name, "___").concat(btn.dataset.name) : btn.dataset.name;
1630 var buttHandlers = this.elementEventHandlers[eventHandlersName];
1631
1632 if (!isTemporary && buttHandlers && btn.lastActivationState !== isActive(btn)) {
1633 if (buttHandlers.onActivation && isActive(btn)) {
1634 buttHandlers.onActivation.apply(this, [btn]);
1635 }
1636
1637 if (buttHandlers.onDeactivation && !isActive(btn)) {
1638 buttHandlers.onDeactivation.apply(this, [btn]);
1639 }
1640 }
1641
1642 if (!isTemporary) btn.lastActivationState = isActive(btn);
1643 this.lastChangedElement = btn;
1644 },
1645 //Служебная функция, добавляет группу полей в группКонтейнер. Здесь реально не на что смотреть, проходите.
1646 addGroup: function addGroup(gc
1647 /* groupContainer */
1648 ) {
1649 var group = document.createElement('div'),
1650 task = this;
1651 group.className = isGroupInfinite(gc) ? 'group infiniteChild' : 'group';
1652 group.innerHTML = fieldsPropsArray[gc.dataset.codepropindex];
1653 gc.appendChild(group);
1654 this.initFieldsHere(group); //На случай вложенных групп
1655
1656 Array.from(gc.querySelectorAll('.groupContainer')).forEach(function (gcInner) {
1657 var c = gc.closest('.groupContainer');
1658
1659 if (c && c === gc) {
1660 return;
1661 }
1662
1663 task.addGroup(gcInner);
1664 });
1665 },
1666 //Функция валидации всех ШВшных инпутов и текстарей. Если вам нужна какая-то непредусмотренная валидация - смело дополняйте в отведённом месте.
1667 validateInput: function validateInput(input) {
1668 var checksPassed;
1669
1670 if (input.closest('.alien')) {
1671 input.classList.add('skipMe');
1672 return;
1673 }
1674
1675 var task = this,
1676 root = this.getDOMElement(),
1677 parent = input.closest('.group') || input.closest('.answerBlock');
1678 var isValidationPassed = false,
1679 //validationPassed - поле прошло валидацию, validationRejected - не прошло.
1680 props = null;
1681
1682 if (input.dataset.props) {
1683 props = fieldsPropsArray[input.dataset.props];
1684 }
1685
1686 if (!input.value) {
1687 input.classList.add('clear');
1688 input.classList.remove('validationPassed');
1689 input.classList.remove('validationRejected');
1690
1691 if (isOptional(props)) {
1692 input.classList.add('skipMe');
1693 } //this.turnOffErrorClassOnParentElementTagBlock(input);
1694
1695
1696 return false;
1697 } else {
1698 input.classList.remove('skipMe');
1699 input.classList.remove('clear');
1700 } //Определяемся с валидацией - либо используем описанную у самого поля (если есть), либо по общим правилам
1701
1702
1703 var eHandlers = task.elementEventHandlers[input.dataset.name];
1704
1705 if (eHandlers && eHandlers.validation) {
1706 isValidationPassed = eHandlers.validation.apply(this, [input]);
1707 } else {
1708 switch (input.dataset.type) {
1709 case 'date':
1710 if (isDate(input.value, props && props.format ? props.format.toUpperCase() : 'DD-MM-YYYY')) {
1711 isValidationPassed = true;
1712 }
1713
1714 if (props) {
1715 if (isValidationPassed && (props.aheadOf || props.behindOf)) {
1716 checksPassed = true;
1717 var aheadOfWhat = null,
1718 behindOfWhat = null;
1719
1720 if (props.aheadOf) {
1721 aheadOfWhat = parent.querySelector('.fieldName_' + props.aheadOf + ' .validationPassed');
1722 }
1723
1724 if (props.behindOf) {
1725 behindOfWhat = parent.querySelector('.fieldName_' + props.behindOf + ' .validationPassed');
1726 }
1727
1728 if (props.aheadOf && aheadOfWhat) {
1729 //Надо сравнить обе даты, приведя каждую из них к удобному виду
1730 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');
1731 }
1732
1733 if (checksPassed && props.behindOf && behindOfWhat) {
1734 //Надо сравнить обе даты, приведя каждую из них к удобному виду
1735 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');
1736 }
1737
1738 isValidationPassed = checksPassed;
1739 }
1740 }
1741
1742 break;
1743
1744 case 'text':
1745 case 'textarea':
1746 checksPassed = true;
1747
1748 if (props) {
1749 if (props.flags) {
1750 if (props.flags.includes('cyrillic') && !/^[\u0430-\u044f\s-"]*$/i.test(input.value)) {
1751 checksPassed = false;
1752 }
1753
1754 if (props.flags.includes('firstCapital') && !/^([\u0410-\u042F][\u0430-\u044f]+\s?)*$/.test(input.value)) {
1755 checksPassed = false;
1756 }
1757 }
1758
1759 if (props.min && input.value.length < props.min) {
1760 checksPassed = false;
1761 }
1762
1763 if (props.max && input.value.length > props.max) {
1764 checksPassed = false;
1765 }
1766 }
1767
1768 isValidationPassed = checksPassed;
1769 break;
1770
1771 case 'link':
1772 if (isLink(input.value)) {
1773 isValidationPassed = true;
1774 }
1775
1776 if (props) {
1777 //В свойствах поля задан конкретный список сервисов, поэтому валидируем ссылку на принадлежность к каждому из них.
1778 if (isValidationPassed && props.services) {
1779 var pass;
1780
1781 if (typeof props.services === 'string') {
1782 props.services = [props.services];
1783 }
1784
1785 var _iterator19 = _createForOfIteratorHelper(props.services),
1786 _step19;
1787
1788 try {
1789 for (_iterator19.s(); !(_step19 = _iterator19.n()).done;) {
1790 var service = _step19.value;
1791
1792 if (isServiceLink(input.value, service.toLowerCase())) {
1793 pass = true;
1794 break;
1795 }
1796 }
1797 } catch (err) {
1798 _iterator19.e(err);
1799 } finally {
1800 _iterator19.f();
1801 }
1802
1803 if (!pass) {
1804 isValidationPassed = false;
1805 }
1806 }
1807
1808 if (isValidationPassed && props.domains) {
1809 var _pass;
1810
1811 if (typeof props.domains === 'string') {
1812 props.domains = [props.domains];
1813 }
1814
1815 for (var _i2 = 0, _Array$from = Array.from(props.domains); _i2 < _Array$from.length; _i2++) {
1816 var domain = _Array$from[_i2];
1817
1818 if (new URL(input.value).host.includes(domain)) {
1819 _pass = true;
1820 break;
1821 }
1822 }
1823
1824 if (!_pass) {
1825 isValidationPassed = false;
1826 }
1827 }
1828 }
1829
1830 break;
1831
1832 case 'email':
1833 if (isEmail(input.value)) {
1834 isValidationPassed = true;
1835 }
1836
1837 break;
1838
1839 case 'integer':
1840 checksPassed = isInteger(input.value);
1841
1842 if (props) {
1843 if (checksPassed && props.min && parseInt(input.value) < props.min) {
1844 checksPassed = false;
1845 }
1846
1847 if (checksPassed && props.max && parseInt(input.value) > props.max) {
1848 checksPassed = false;
1849 }
1850 }
1851
1852 isValidationPassed = checksPassed;
1853 break;
1854
1855 case 'float':
1856 checksPassed = false;
1857 checksPassed = isInteger(input.value) || isFloat(input.value);
1858
1859 if (props) {
1860 if (checksPassed && props.min && parseFloat(input.value) < props.min) {
1861 checksPassed = false;
1862 }
1863
1864 if (checksPassed && props.max && parseFloat(input.value) > props.max) {
1865 checksPassed = false;
1866 }
1867 }
1868
1869 isValidationPassed = checksPassed;
1870 break;
1871 } //Проверка на соответствие глобальным лимитам
1872
1873
1874 if (isValidationPassed) {
1875 if (input.dataset.type === 'textarea' && window.textareaCharLimit && input.value.length > window.textareaCharLimit || input.dataset.type !== 'textarea' && window.inputCharLimit && input.value.length > window.inputCharLimit) {
1876 isValidationPassed = false;
1877 }
1878 }
1879 } //Проверка на уникальность при наличии нужного флага
1880
1881
1882 if (input.value && isValidationPassed && props && props.flags && props.flags.includes('unique')) {
1883 var allInputsVals = [];
1884 Array.from(root.querySelectorAll('input')).forEach(function (i) {
1885 if (!i.value || i === input) {
1886 return;
1887 }
1888
1889 var iValue;
1890
1891 if (i.dataset.type === 'link') {
1892 iValue = skipProtocolParametersAndOtherStuff(i.value);
1893 } else {
1894 iValue = i.value;
1895 }
1896
1897 allInputsVals.push(iValue);
1898 });
1899 var iValue;
1900
1901 if (input.dataset.type === 'link') {
1902 iValue = skipProtocolParametersAndOtherStuff(input.value);
1903 } else {
1904 iValue = input.value;
1905 }
1906
1907 if (allInputsVals.includes(iValue) || props && props.uniqueAdditional && Array.isArray(props.uniqueAdditional) && props.uniqueAdditional.includes(iValue)) {
1908 isValidationPassed = false;
1909 }
1910 } //Место для кастомного кода
1911
1912
1913 if (!isValidationPassed) {
1914 input.classList.remove('validationPassed');
1915 input.classList.add('validationRejected');
1916 } else {
1917 input.classList.add('validationPassed');
1918 input.classList.remove('validationRejected');
1919 this.turnOffErrorClassOnParentElementTagBlock(input);
1920 }
1921
1922 return true;
1923 },
1924 //Я не знаю, кто написал эту функцию. Когда я стал лид-асессором, она уже была написана кем-то (Денисом Раскостовым?) и все её использовали в своих шаблонах. А мы чё, хуже?)))
1925 addError: function addError(message, field, errors) {
1926 errors || (errors = {
1927 task_id: this.getOptions().task.id,
1928 errors: {}
1929 });
1930 errors.errors[field] = {
1931 message: message
1932 };
1933 return errors;
1934 },
1935 //Аналог вышеописанной функции, но вешает на конкретные DOM-элементы ШВшную плашку с ошибкой (а не на Хэндлбарсный контрол). Используйте на здоровье при кастомной валидации!
1936 addErrorToField: function addErrorToField(field, msg, errors) {
1937 var root = this.getDOMElement(),
1938 task = this;
1939 var wrappedMSG = "<div class=\"errorMsgBlockInner\">".concat(msg, "</div>");
1940
1941 if (!field.classList.contains('element')) {
1942 field = field.closest('.element');
1943 }
1944
1945 if (!field.dataset.errorMsgNumber) {
1946 field.dataset.errorMsgNumber = this.errorMSGs.length;
1947 var newMSG = document.createElement('div');
1948 newMSG.className = 'errorMsgBlock';
1949 newMSG.innerHTML = wrappedMSG;
1950 this.errorMSGs.push(field.insertBefore(newMSG, field.firstElementChild));
1951 } else {
1952 this.errorMSGs[field.dataset.errorMsgNumber].innerHTML = wrappedMSG;
1953 }
1954
1955 this.turnOnErrorClassOnParentElementTagBlock(field);
1956 return this.addError(msg, 'Fallout 76', errors);
1957 },
1958 //Всё понятно из названия, а если нет - то из содержания уж точно. Вам, наверное, не понадобится.
1959 turnOnErrorClassOnParentElementTagBlock: function turnOnErrorClassOnParentElementTagBlock(someElement) {
1960 var parentContainer = someElement.closest('.element');
1961
1962 if (parentContainer) {
1963 parentContainer.classList.add('withSomeError');
1964 }
1965
1966 makeSureThisDivFitsOnScreen(parentContainer.querySelector('.errorMsgBlock'));
1967 },
1968 //Всё понятно из названия, а если нет - то из содержания уж точно. Может вам понадобиться, если где-то надо спрятать плашку с ошибкой, выведенной addErrorToField().
1969 turnOffErrorClassOnParentElementTagBlock: function turnOffErrorClassOnParentElementTagBlock(someElement) {
1970 var parentContainer = someElement.closest('.element');
1971
1972 if (parentContainer) {
1973 parentContainer.classList.remove('withSomeError');
1974 }
1975 },
1976 //Функция отвечает за обработку параметра convert в спекМаппере
1977 convertSpecField: function convertSpecField(res, convert, func) {
1978 var task = this;
1979
1980 if (typeof convert == "string") {
1981 convert = [convert];
1982 }
1983
1984 convert.forEach(function (conv) {
1985 switch (conv) {
1986 case "strToBool":
1987 res = !!(res === true || res && ['true', 'yes', 'yep', 'sure', 'да'].includes(res.toLowerCase()));
1988 break;
1989
1990 case "invert":
1991 res = !res;
1992 break;
1993
1994 case "infiniteToStringArray":
1995 if (res) {
1996 var result = [];
1997
1998 var _iterator20 = _createForOfIteratorHelper(res),
1999 _step20;
2000
2001 try {
2002 for (_iterator20.s(); !(_step20 = _iterator20.n()).done;) {
2003 var line = _step20.value;
2004
2005 for (var key in line) {
2006 result.push(line[key]);
2007 }
2008 }
2009 } catch (err) {
2010 _iterator20.e(err);
2011 } finally {
2012 _iterator20.f();
2013 }
2014
2015 res = result;
2016 } else {
2017 res = null;
2018 }
2019
2020 break;
2021
2022 case "function":
2023 if (!func) {
2024 ce('specmapper_conversion_no_function', 'type: ' + conv);
2025 } else {
2026 res = func.apply(task, [res]);
2027 }
2028
2029 break;
2030
2031 default:
2032 ce('specmapper_conversion_type_not_found', 'type: ' + conv);
2033 }
2034 });
2035 return res;
2036 },
2037 //Тот самый спекМаппер, о котором вам рассказывали на экскурсии по Звёздному Крейсеру "Галактика". И нет, я не сайлон. Наверное...
2038 mapSpecField: function mapSpecField(mapperObj, solution) {
2039 var root = this.getDOMElement(),
2040 task = this,
2041 res;
2042
2043 if (!mapperObj.function && mapperObj.f) {
2044 mapperObj.function = mapperObj.f;
2045 }
2046
2047 if (!mapperObj.convert && mapperObj.function) {
2048 mapperObj.convert = ['function'];
2049 }
2050
2051 gettingRes: {
2052 if (mapperObj.from) {
2053 if (Array.isArray(mapperObj.from)) {
2054 var _iterator21 = _createForOfIteratorHelper(mapperObj.from),
2055 _step21;
2056
2057 try {
2058 for (_iterator21.s(); !(_step21 = _iterator21.n()).done;) {
2059 var name = _step21.value;
2060
2061 if (this.output[name] || this.output[name] === false) {
2062 res = this.output[name];
2063 break gettingRes;
2064 }
2065 }
2066 } catch (err) {
2067 _iterator21.e(err);
2068 } finally {
2069 _iterator21.f();
2070 }
2071 } else {
2072 if (this.output[mapperObj.from] || this.output[mapperObj.from] === false) {
2073 res = this.output[mapperObj.from];
2074 break gettingRes;
2075 }
2076 }
2077 }
2078
2079 if (mapperObj.fromT) {
2080 if (Array.isArray(mapperObj.fromT)) {
2081 var _iterator22 = _createForOfIteratorHelper(mapperObj.fromT),
2082 _step22;
2083
2084 try {
2085 for (_iterator22.s(); !(_step22 = _iterator22.n()).done;) {
2086 var _name = _step22.value;
2087
2088 if (solution[_name] || solution[_name] === false) {
2089 res = solution[_name];
2090 break gettingRes;
2091 }
2092 }
2093 } catch (err) {
2094 _iterator22.e(err);
2095 } finally {
2096 _iterator22.f();
2097 }
2098 } else {
2099 if (solution[mapperObj.fromT] || solution[mapperObj.fromT] === false) {
2100 res = solution[mapperObj.fromT];
2101 break gettingRes;
2102 }
2103 }
2104 }
2105
2106 if (!mapperObj.from && !mapperObj.fromT) {
2107 res = this.output;
2108 }
2109 }
2110
2111 if (mapperObj.convert) {
2112 res = this.convertSpecField(res, mapperObj.convert, mapperObj.function);
2113 }
2114
2115 return res;
2116 },
2117 //Серьёзно, вам нужен комментарий для этой функции?
2118 validate: function validate(solution) {
2119 var root = this.getDOMElement(),
2120 task = this,
2121 input = this.getTask().input_values;
2122
2123 if (printSolution) {
2124 console.log('Таск:', root.dataset.number);
2125 console.log('Солюшка до валидации:', Object.assign({}, solution.output_values));
2126 }
2127
2128 var errors, output; //Валидация и сбор солюшки по ШВшным элементам
2129
2130 if (!this.dontGatherSolution) {
2131 var eAndS = this.gatherErrorsAndSolution();
2132 errors = eAndS.errors;
2133 output = eAndS.solution;
2134 } else {
2135 output = this.theOneSolution;
2136 }
2137
2138 this.output = output; //СпекМаппер
2139
2140 var specFields = 0,
2141 res = false;
2142
2143 if (useSpecMapper) {
2144 res = {}; //Если в HTML есть скрытое поле для загрузки файлов - его надо обязательно смаппить в выходное поле files
2145
2146 if (root.querySelector('.element.__fileUploader')) {
2147 specMapper.files = {
2148 fromT: "files"
2149 };
2150 }
2151
2152 for (var outField in specMapper) {
2153 specFields++;
2154 var value = this.mapSpecField(specMapper[outField], solution.output_values);
2155
2156 if (value !== undefined) {
2157 res[outField] = value;
2158 }
2159 }
2160
2161 if (usingOneRawData) {
2162 var tord = $.extend(true, {}, this.theOneSolution);
2163 tord.__theOneCustomStorage.__actionLog = []; //Экономим трафик и место
2164
2165 tord.__theOneCustomStorage.__timingsStorage = {}; //Оно там не нужно
2166
2167 delete tord.undefined; //Были прецеденты)))
2168
2169 res.__theOneRawData = JSON.stringify(tord);
2170
2171 if (printSolution) {
2172 console.log('__theOneRawData:', JSON.parse(res['__theOneRawData']));
2173 }
2174 }
2175 } //Валидация
2176
2177
2178 if (window.validation) {
2179 errors = validation.apply(this, [res, output, solution, errors, root]);
2180 } //Завершения валидэйта
2181
2182
2183 solution.output_values = res ? res : {
2184 "result": output
2185 };
2186 errors = errors || TolokaHandlebarsTask.prototype.validate.apply(this, solution);
2187
2188 if (errors && window.noValidationErrors) {
2189 window.noValidationErrors = false;
2190 }
2191
2192 if (printSolution) {
2193 console.log('Солюшка ШВ:', $.extend(true, {}, this.output), ' Полная: ', $.extend(true, {}, this.theOneSolution));
2194 console.log('Итоговая солюшка:', solution.output_values);
2195 console.log('Ошибки:', errors);
2196 }
2197
2198 return errors;
2199 },
2200 //Функция для ШВшной валидации и сбора солюшки во всём таске. Если beQuiet - не ругается на ошибки и не собирает их, а просто собирает валидную солюшку.
2201 //whereToGather - либо селектор, в котором всё это делать, либо элемент с уникальным в рамках таска className
2202 gatherErrorsAndSolution: function gatherErrorsAndSolution(beQuiet, whereToGather) {
2203 var root = this.getDOMElement(),
2204 task = this,
2205 selectorPrefix,
2206 output = {},
2207 errors = null,
2208 validationGroupIndex = -1;
2209 selectorPrefix = whereToGather ? typeof whereToGather == 'string' ? whereToGather : function () {
2210 var s = '';
2211
2212 var _iterator23 = _createForOfIteratorHelper(whereToGather.classList),
2213 _step23;
2214
2215 try {
2216 for (_iterator23.s(); !(_step23 = _iterator23.n()).done;) {
2217 var item = _step23.value;
2218 s += '.' + item;
2219 }
2220 } catch (err) {
2221 _iterator23.e(err);
2222 } finally {
2223 _iterator23.f();
2224 }
2225
2226 return s + ' ';
2227 }() : '.answerBlock '; //Добавляем и валидируем все обычные поля
2228
2229 var valResult = this.validateAndGrabFieldsInHere(selectorPrefix, errors, beQuiet, true);
2230 output = valResult.value;
2231 errors = valResult.errors;
2232 /* Теперь у нам предстоит 2 прохода по бесконечным группам - сначала мы все нумеруем, а потом собираем солюшку. */
2233 //Индексируем группы для оптимизации дальнейшего алгоритма и отладки
2234
2235 Array.from(root.querySelectorAll(selectorPrefix + '.groupContainer:not(.excluded)')).forEach(function (gc) {
2236 var groups = gc.querySelectorAll(':scope>.group');
2237
2238 for (var i = 0; i < groups.length; i++) {
2239 validationGroupIndex++;
2240
2241 if (groups[i].dataset.validationGroupIndex) {
2242 groups[i].classList.remove('validationGroupIndex' + groups[i].dataset.validationGroupIndex);
2243 }
2244
2245 groups[i].dataset.validationGroupIndex = validationGroupIndex;
2246 groups[i].classList.add('validationGroupIndex' + validationGroupIndex);
2247 groups[i].dataset.numberInContainer = i;
2248 }
2249 }); //Добавляем и валидируем группКонтейнеры погруппно каждый
2250
2251 Array.from(root.querySelectorAll(selectorPrefix + '.groupContainer:not(.excluded)')).forEach(function (gc) {
2252 var groups = gc.querySelectorAll(':scope>.group');
2253
2254 for (var i = 0; i < groups.length; i++) {
2255 //Если это какая-то "левая", неШВшная группа
2256 if (groups[i].parentElement !== gc) {
2257 continue;
2258 } //Проверки для "бесконечных" групп
2259
2260
2261 if (isGroupInfinite(groups[i].parentElement)) {
2262 //Если группа пустая, но при этом есть другие - пропускаем (для "бесконечных" групп)
2263 groups[i].classList.remove('infiniteSkipped');
2264
2265 if (i > 0 && !groups[i].classList.contains('hasSomething') && !groups[i].querySelector('.group.hasSomething')) {
2266 groups[i].classList.add('infiniteSkipped');
2267 continue;
2268 } else if (i === 0 && !groups[i].classList.contains('hasSomething') && groups[i].closest('.group.infiniteSkipped')) {
2269 continue;
2270 }
2271 }
2272
2273 var _valResult = task.validateAndGrabFieldsInHere(selectorPrefix + ".groupContainer .group.validationGroupIndex".concat(groups[i].dataset.validationGroupIndex, ">div:not(.groupContainer)"), errors, beQuiet); //Если valResult непустой - записываем его в выходную спеку
2274
2275
2276 if (_valResult.isNotEmpty) {
2277 if (isGroupInfinite(gc)) {
2278 var gcChain = [{
2279 name: gc.dataset.name,
2280 number: i
2281 }];
2282 var parentInfiniteChildGroup = gc.closest('.group.infiniteChild');
2283
2284 if (parentInfiniteChildGroup) {
2285 //Если эта бесконечная группа вложена как минимум в какую-то другую
2286 //Идём вверх по родителям и создаём последовательность имён и номеров групп, чтобы получить как-бы адрес текущей группы в солюшке
2287 do {
2288 gcChain.push({
2289 name: parentInfiniteChildGroup.parentElement.dataset.name,
2290 number: parseInt(parentInfiniteChildGroup.dataset.numberInContainer)
2291 });
2292 parentInfiniteChildGroup = parentInfiniteChildGroup.parentElement.closest('.group.infiniteChild');
2293 } while (parentInfiniteChildGroup); //Теперь перемещаем курсор снаружи вглубь солюшки по составленной цепочке попутно создавая нужные ключи, если их нет
2294
2295
2296 var cursor = output;
2297
2298 for (var n = gcChain.length - 1; n >= 0; n--) {
2299 var gcName = gcChain[n].name,
2300 gcNumber = gcChain[n].number;
2301
2302 if (!cursor[gcName]) {
2303 cursor[gcName] = [];
2304 }
2305
2306 if (!cursor[gcName][gcNumber]) {
2307 cursor[gcName][gcNumber] = {};
2308 }
2309
2310 cursor = cursor[gcName][gcNumber];
2311 } //На данный момент наш курсор находится в финальной точке и указывает на конечный объект в солюшке для текущей группы.
2312 //Остаётся лишь заполнить его собранной солюшкой
2313
2314
2315 for (var val in _valResult.value) {
2316 cursor[val] = _valResult.value[val];
2317 }
2318 } else {
2319 //Если текущая бесконечная группа у нас не содержится внутри другой бесконечной группы - всё намного проще
2320 if (!output[gc.dataset.name]) {
2321 output[gc.dataset.name] = [];
2322 }
2323
2324 if (!output[gc.dataset.name][i]) {
2325 output[gc.dataset.name][i] = {};
2326 }
2327
2328 for (var _val in _valResult.value) {
2329 output[gc.dataset.name][i][_val] = _valResult.value[_val];
2330 }
2331 }
2332 } else {
2333 for (var _val2 in _valResult.value) {
2334 output[_val2 + (i > 0 ? i : '')] = _valResult.value[_val2];
2335 }
2336 }
2337 }
2338
2339 errors = _valResult.errors;
2340 }
2341 }); //Проверяем заполненность всех radioGroup
2342
2343 Array.from(root.querySelectorAll(selectorPrefix + '.radioGroup:not(.optional)')).forEach(function (radioGroup) {
2344 if (!beQuiet && !radioGroup.querySelector('.btn.active') && !radioGroup.closest('.excluded') && !radioGroup.closest('.window:not(.opened)')) {
2345 var group = radioGroup.closest('.group');
2346
2347 if (group) {
2348 var gc = group.parentElement;
2349
2350 if (gc && group && group.closest('.infiniteSkipped')) {
2351 return;
2352 }
2353
2354 if (gc && group && isGroupInfinite(gc) && !group.classList.contains('hasSomething') && gc.querySelector('.group.hasSomething')) {
2355 return;
2356 }
2357 }
2358
2359 errors = task.addErrorToField(radioGroup, 'Пожалуйста, выберите соответствующую кнопку!', errors);
2360 }
2361 }); //Проверяем заполненность всех checkboxGroup
2362
2363 Array.from(root.querySelectorAll(selectorPrefix + '.checkboxGroup:not(.optional)')).forEach(function (checkboxGroup) {
2364 if (!beQuiet && !checkboxGroup.querySelector('.btn.active') && !checkboxGroup.closest('.excluded') && !checkboxGroup.closest('.window:not(.opened)')) {
2365 var group = checkboxGroup.closest('.group');
2366
2367 if (group) {
2368 var gc = group.parentElement;
2369
2370 if (gc && group && group.closest('.infiniteSkipped')) {
2371 return;
2372 }
2373
2374 if (gc && group && isGroupInfinite(gc) && !group.classList.contains('hasSomething') && gc.querySelector('.group.hasSomething')) {
2375 return;
2376 }
2377 }
2378
2379 errors = task.addErrorToField(checkboxGroup, 'Пожалуйста, выберите соответствующую кнопку!', errors);
2380 }
2381 }); //Проверяем заполненность всех селектов
2382
2383 Array.from(root.querySelectorAll(selectorPrefix + '.select:not(.optional)')).forEach(function (select) {
2384 if (!beQuiet && !select.dataset.selection && !select.closest('.excluded') && !select.closest('.window:not(.opened)')) {
2385 var group = select.closest('.group');
2386
2387 if (group) {
2388 var gc = group.parentElement;
2389
2390 if (gc && group && group.closest('.infiniteSkipped')) {
2391 return;
2392 }
2393
2394 if (gc && group && isGroupInfinite(gc) && !group.classList.contains('hasSomething') && gc.querySelector('.group.hasSomething')) {
2395 return;
2396 }
2397 }
2398
2399 errors = task.addErrorToField(select, 'Пожалуйста, выберите элемент выпадающего списка!', errors);
2400 }
2401 });
2402 return {
2403 "errors": errors,
2404 "solution": output
2405 };
2406 },
2407 //Производит ШВшную валидацию и сбор ШВшной солюшки (далее по тексту - просто "Валидация") в указанном элементе.
2408 validateAndGrabFieldsInHere: function validateAndGrabFieldsInHere(selector, errors, beQuiet, notInGroup) {
2409 var task = this,
2410 root = this.getDOMElement(),
2411 result = {
2412 "value": {},
2413 "errors": errors,
2414 "isNotEmpty": false
2415 }; //Добавляем все инпуты
2416
2417 Array.from(root.querySelectorAll(selector + ' input, ' + selector + ' textarea')).forEach(function (field) {
2418 if (notInGroup && field.closest('.groupContainer')) {
2419 return result;
2420 }
2421
2422 if (field.closest('.excluded') || field.closest('.alien') || field.closest('.window:not(.opened)')) {
2423 return result;
2424 } //Дополнительная проверка для инпутов вложенных полей
2425
2426
2427 task.validateInput(field);
2428
2429 if (field.classList.contains('skipMe')) {
2430 return;
2431 }
2432
2433 if (!field.classList.contains('validationPassed')) {
2434 if (!beQuiet) {
2435 result.errors = task.addErrorToField(field, 'Проверьте, пожалуйста, корректность заполнения этого поля.', result.errors);
2436 }
2437 } else {
2438 result.value[field.dataset.solutionname || field.dataset.name] = field.value;
2439 result.isNotEmpty = true;
2440 }
2441 }); //Добавляем все нажатые кнопки
2442
2443 Array.from(root.querySelectorAll(selector + ' .btn.active')).forEach(function (btn) {
2444 if (notInGroup && btn.closest('.groupContainer')) {
2445 return result;
2446 }
2447
2448 if (btn.closest('.excluded') || btn.closest('.alien') || btn.closest('.window:not(.opened)')) {
2449 return result;
2450 }
2451
2452 var groupSolutionName = btn.dataset.groupsolutionname || btn.dataset.radiogroup || btn.dataset.select || btn.dataset.checkboxgroup;
2453
2454 if (btn.dataset.radiogroup) {
2455 //Эта кнопка - часть массива радиобаттонов
2456 result.value[groupSolutionName] = btn.dataset.name;
2457 } else if (btn.dataset.select) {
2458 //Эта кнопка - часть селекта
2459 result.value[groupSolutionName] = btn.dataset.name;
2460 } else if (btn.dataset.checkboxgroup) {
2461 //Эта кнопка - часть массива чекбоксов (от обычных чекбоксов они отличаются сбором данных в массив)
2462 if (!result.value[groupSolutionName]) {
2463 result.value[groupSolutionName] = [];
2464 }
2465
2466 result.value[groupSolutionName].push(btn.dataset.name);
2467 } else {
2468 result.value[btn.dataset.name] = true;
2469 }
2470
2471 result.isNotEmpty = true;
2472 }); //Добавляем выключенные кнопки
2473
2474 if (typeof buttonsCanBeFalse === "undefined" || buttonsCanBeFalse) {
2475 Array.from(root.querySelectorAll(selector + '.buttonGroup .btn:not(.active)')).forEach(function (btn) {
2476 if (notInGroup && btn.closest('.groupContainer')) {
2477 return result;
2478 }
2479
2480 if (btn.closest('.excluded') || btn.closest('.alien') || btn.closest('.window:not(.opened)')) {
2481 return result;
2482 }
2483
2484 var groupSolutionName = btn.dataset.groupsolutionname || btn.dataset.radiogroup || btn.dataset.select || btn.dataset.checkboxgroup;
2485 result.value[btn.dataset.name] = false;
2486 result.isNotEmpty = true;
2487 });
2488 } //Валидируем ШВшные загрузчики файлов
2489
2490
2491 var _iterator24 = _createForOfIteratorHelper(root.querySelectorAll(selector + ' .element.fileField')),
2492 _step24;
2493
2494 try {
2495 for (_iterator24.s(); !(_step24 = _iterator24.n()).done;) {
2496 var fileField = _step24.value;
2497
2498 if (notInGroup && fileField.closest('.groupContainer')) {
2499 continue;
2500 }
2501
2502 if (fileField.closest('.excluded') || fileField.closest('.alien') || fileField.closest('.window:not(.opened)')) {
2503 continue;
2504 } //Валидация
2505
2506
2507 result.errors = fileField.validate(result.errors, beQuiet); //Сбор солюшки
2508
2509 if (fileField.classList.contains('validationPassed')) {
2510 var files = [];
2511 var fileLines = fileField.querySelectorAll('.fcFileLine');
2512
2513 var _iterator25 = _createForOfIteratorHelper(fileLines),
2514 _step25;
2515
2516 try {
2517 for (_iterator25.s(); !(_step25 = _iterator25.n()).done;) {
2518 var line = _step25.value;
2519
2520 if (line.dataset.attachmentId) {
2521 files.push(line.dataset.attachmentId);
2522 }
2523 }
2524 } catch (err) {
2525 _iterator25.e(err);
2526 } finally {
2527 _iterator25.f();
2528 }
2529
2530 if (files.length) {
2531 result.value[fileField.dataset.name] = files;
2532 }
2533 }
2534 }
2535 } catch (err) {
2536 _iterator24.e(err);
2537 } finally {
2538 _iterator24.f();
2539 }
2540
2541 return result;
2542 },
2543 //Инициализирует поле для загрузки файла
2544 initFileField: function initFileField(field) {
2545 var _this5 = this;
2546
2547 var task = this,
2548 root = this.getDOMElement(),
2549 fContainer = field.querySelector('.fContainer'),
2550 fTitle = field.querySelector('.stepTitle'),
2551 fileUploader = root.querySelector('.__fileUploader'),
2552 fieldName = field.dataset.name;
2553
2554 if (!fileUploader) {
2555 ce('Вы добавили в спеку поле типа file, оно отрендерилось, но вы не добавили для него стандартный Толоковский загручик файлов. Читайте доку.', field);
2556 return;
2557 }
2558
2559 var fuFiles = fileUploader.querySelector('.field_file__files');
2560
2561 if (!fuFiles) {
2562 ce('Очень странно, у загрузчика файлов не найден div .field_file__files...', fileUploader);
2563 return;
2564 }
2565
2566 var props;
2567
2568 if (field.dataset.props) {
2569 props = fieldsPropsArray[field.dataset.props];
2570 } //Раз у нас в шаблоне есть скрытый загрузчик файлов - надо повесить на него обсервер, если ещё не повесили
2571
2572
2573 if (!this._fileUploader) {
2574 this._fileUploader = {};
2575 }
2576
2577 if (!this._fileUploader.observer) {
2578 this._fileUploader.uploadedFilesDivs = [];
2579 this._fileUploader.observer = new MutationObserver(function (mutations) {
2580 var filesDivs = fuFiles.querySelectorAll('.field_file__files__file');
2581 var uploadingFiles = {}; //Пробегаемся по всем дивам с файлами и загоняем имена в объект
2582
2583 var nameOfLastFile;
2584
2585 var _iterator26 = _createForOfIteratorHelper(filesDivs),
2586 _step26;
2587
2588 try {
2589 for (_iterator26.s(); !(_step26 = _iterator26.n()).done;) {
2590 var fDiv = _step26.value;
2591 var fileName = fDiv.querySelector('.file__name').innerHTML;
2592 var fileDeleteButt = fDiv.querySelector('.file__delete');
2593 var attachmentId = fileDeleteButt.dataset.attachmentId;
2594
2595 if (attachmentId) {
2596 uploadingFiles[attachmentId] = {
2597 fileName: fileName
2598 };
2599
2600 if (_this5.getStorageItem('__uploadingFiles')[attachmentId]) {
2601 if (_this5.getStorageItem('__uploadingFiles')[attachmentId].fileLine) {
2602 uploadingFiles[attachmentId].fileLine = _this5.getStorageItem('__uploadingFiles')[attachmentId].fileLine;
2603 }
2604
2605 if (_this5.getStorageItem('__uploadingFiles')[attachmentId].fileField) {
2606 uploadingFiles[attachmentId].fileField = _this5.getStorageItem('__uploadingFiles')[attachmentId].fileField;
2607 }
2608
2609 if (_this5.getStorageItem('__uploadingFiles')[attachmentId].fieldName) {
2610 uploadingFiles[attachmentId].fieldName = _this5.getStorageItem('__uploadingFiles')[attachmentId].fieldName;
2611 }
2612 }
2613 }
2614
2615 nameOfLastFile = fileName;
2616 } //Если добавили файл
2617
2618 } catch (err) {
2619 _iterator26.e(err);
2620 } finally {
2621 _iterator26.f();
2622 }
2623
2624 if (filesDivs.length > _this5._fileUploader.uploadedFilesDivs.length) {
2625 if (_this5._fileUploader.someFileUploading) {
2626 _this5._fileUploader.someFileUploading(nameOfLastFile);
2627 }
2628 } else //Если удалили файл
2629 if (filesDivs.length < _this5._fileUploader.uploadedFilesDivs.length) {
2630 //Сначала надо определить, какой именно
2631 var deletedFileName;
2632
2633 for (var f in uploadingFiles) {
2634 var weGotIt = void 0;
2635
2636 for (var _f in _this5.getStorageItem('__uploadingFiles')) {
2637 if (f === _f) {
2638 weGotIt = true;
2639 }
2640 }
2641
2642 if (!weGotIt) {
2643 if (_this5._fileUploader.someFileRemoved) {
2644 _this5._fileUploader.someFileRemoved(f);
2645 }
2646
2647 break;
2648 }
2649 }
2650 } else //Если количество не изменилось - значит какой-то файл закончил загружаться
2651 {
2652 if (_this5._fileUploader.uploadComplete) {
2653 _this5._fileUploader.uploadComplete(filesDivs);
2654 }
2655 }
2656
2657 _this5.setStorageItem('__uploadingFiles', uploadingFiles);
2658
2659 _this5._fileUploader.uploadedFilesDivs = filesDivs;
2660 });
2661
2662 this._fileUploader.observer.observe(fuFiles, {
2663 attributes: false,
2664 childList: true,
2665 characterData: false
2666 });
2667 } //Добавляет к полю загрузчик файла
2668
2669
2670 field.addUploader = function () {
2671 function openFileDialog() {
2672 fileUploader.querySelector('.field_file__upload').click();
2673 }
2674
2675 var waitTillTheFileFinishUploading = function waitTillTheFileFinishUploading() {
2676 return new Promise(function (resolve, reject) {
2677 _this5._fileUploader.uploadComplete = function (filesDivs) {
2678 resolve(filesDivs);
2679 };
2680 });
2681 };
2682
2683 var fuButtons = {
2684 type: "actionButtonGroup",
2685 flags: ["unfocusable"],
2686 buttons: [{
2687 iconClass: "uploadButtIcon",
2688 caption: "Загрузить",
2689 onClick: function onClick() {
2690 var _this6 = this;
2691
2692 if (this.isReadOnly || this.interfaceBlocked) {
2693 return;
2694 }
2695
2696 if (this._fileUploader.someFileUploadingNow) {
2697 return;
2698 }
2699
2700 openFileDialog(); //Переопределяем функцию, которая будет вызвана после появления нового файла в загрузчике
2701
2702 this._fileUploader.someFileUploading = function (fileName) {
2703 if ( //Проверка расширения файла
2704 props && props.extensions && !new RegExp("\\.(".concat(function () {
2705 var s = '';
2706
2707 var _iterator27 = _createForOfIteratorHelper(props.extensions),
2708 _step27;
2709
2710 try {
2711 for (_iterator27.s(); !(_step27 = _iterator27.n()).done;) {
2712 var e = _step27.value;
2713 s += '|' + e;
2714 }
2715 } catch (err) {
2716 _iterator27.e(err);
2717 } finally {
2718 _iterator27.f();
2719 }
2720
2721 return s.substr(1);
2722 }(), ")$"), 'ig').test(fileName) || //Проверка имени файла
2723 function () {
2724 var uf = _this6.getStorageItem('__uploadingFiles');
2725
2726 for (var attachmentId in uf) {
2727 if (uf[attachmentId].fileName === fileName) {
2728 _this6.windows._fileWithSameNameAlreadyUploadedMessageWindow.open();
2729
2730 return true;
2731 }
2732 }
2733 }()) {
2734 //Отменяем загрузку файла
2735 var thisFileDeleteButt = fileUploader.querySelector('.field_file__files__file_processing .file__delete');
2736 thisFileDeleteButt.click();
2737 return;
2738 }
2739
2740 _this6._fileUploader.someFileUploadingNow = true;
2741 var fileLine = _this6._fileUploader.uploadingFileLine = field.addFileLine(fileName);
2742 waitTillTheFileFinishUploading().then(function (filesDivs) {
2743 _this6._fileUploader.uploadingFileLine.uploadingComplete(filesDivs);
2744 });
2745 };
2746 }
2747 }]
2748 };
2749 task.getJSON([fuButtons]);
2750 var fUploader = document.createElement('div');
2751 fUploader.className = "fUploader";
2752 fUploader.innerHTML = "\n <div class=\"fuButtons\">".concat(handleFieldAndGetHTML(fuButtons), "</div>\n ");
2753 fTitle.appendChild(fUploader);
2754 task.initFieldsHere(fUploader);
2755 };
2756
2757 field.addUploader();
2758
2759 field.addFileLine = function (fileName, attachmentId) {
2760 var flButtons = {
2761 type: "actionButtonGroup",
2762 flags: ["unfocusable"],
2763 buttons: [{
2764 iconClass: "thrashButtIcon",
2765 caption: "Удалить",
2766 onClick: function onClick() {
2767 if (this.isReadOnly || this.interfaceBlocked) {
2768 return;
2769 }
2770
2771 fileLine.remove();
2772 }
2773 }]
2774 };
2775 task.getJSON([flButtons]);
2776 var fileLine = document.createElement('div');
2777
2778 fileLine.remove = function () {
2779 //Перебираем все файлы в загрузчке и удаляем нужный
2780 var _iterator28 = _createForOfIteratorHelper(_this5._fileUploader.uploadedFilesDivs),
2781 _step28;
2782
2783 try {
2784 for (_iterator28.s(); !(_step28 = _iterator28.n()).done;) {
2785 var fDiv = _step28.value;
2786 var thisFileName = fDiv.querySelector('.file__name').innerHTML;
2787 var thisFileDeleteButt = fDiv.querySelector('.file__delete');
2788 var thisFileAttachmentId = thisFileDeleteButt.dataset.attachmentId;
2789
2790 if (thisFileName === fileName && (!thisFileAttachmentId || attachmentId === thisFileAttachmentId)) {
2791 thisFileDeleteButt.click();
2792 break;
2793 }
2794 }
2795 } catch (err) {
2796 _iterator28.e(err);
2797 } finally {
2798 _iterator28.f();
2799 }
2800
2801 fileObj.fileLine.classList.remove('uploading');
2802 fContainer.classList.remove('somethingIsUploading');
2803 _this5._fileUploader.someFileUploadingNow = false; //Удаляем строчку ШВшного загрузчика с данным файлом
2804
2805 fContainer.removeChild(fileObj.fileLine);
2806
2807 _this5.turnOffErrorClassOnParentElementTagBlock(field);
2808
2809 field.validate({}, true);
2810 };
2811
2812 fileLine.className = "fcFileLine";
2813
2814 if (!attachmentId) {
2815 fileLine.className += " uploading";
2816 }
2817
2818 fileLine.innerHTML = "\n <div class=\"fcFileButtons\">".concat(handleFieldAndGetHTML(flButtons), "</div>\n <div class=\"fcFileName\">").concat(fileName, "</div>\n ");
2819 var fileObj = {
2820 fileName: fileName,
2821 fileLine: fileLine,
2822 fileField: field,
2823 fieldName: fieldName
2824 };
2825 fContainer.appendChild(fileLine);
2826 task.initFieldsHere(fileLine);
2827
2828 if (!attachmentId) {
2829 fileLine.uploadingComplete = function (filesDivs) {
2830 fileLine.classList.remove('uploading');
2831 fContainer.classList.remove('somethingIsUploading');
2832 _this5._fileUploader.someFileUploadingNow = false; //Получаем и прописываем attachmentId загруженного файла
2833
2834 if (filesDivs) {
2835 var _iterator29 = _createForOfIteratorHelper(filesDivs),
2836 _step29;
2837
2838 try {
2839 for (_iterator29.s(); !(_step29 = _iterator29.n()).done;) {
2840 var fDiv = _step29.value;
2841 var fName = fDiv.querySelector('.file__name').innerHTML;
2842
2843 if (fName !== fileName) {
2844 continue;
2845 }
2846
2847 var fileDeleteButt = fDiv.querySelector('.file__delete');
2848 attachmentId = fileDeleteButt.dataset.attachmentId;
2849 break;
2850 }
2851 } catch (err) {
2852 _iterator29.e(err);
2853 } finally {
2854 _iterator29.f();
2855 }
2856
2857 fileLine.dataset.attachmentId = attachmentId;
2858 _this5.getStorageItem('__uploadingFiles')[attachmentId] = fileObj;
2859
2860 _this5.turnOffErrorClassOnParentElementTagBlock(field);
2861
2862 field.validate({}, true);
2863
2864 _this5.checksAfterValidation(field);
2865
2866 _this5.onSolutionChange();
2867 }
2868 };
2869 } else {
2870 fileLine.dataset.attachmentId = attachmentId;
2871 }
2872
2873 fContainer.classList.add('somethingIsUploading');
2874 return fileLine;
2875 };
2876
2877 field.clear = function () {
2878 fContainer.innerHTML = '';
2879 };
2880
2881 field.validate = function (errors, beQuiet) {
2882 var fileLines = field.querySelectorAll('.fcFileLine');
2883 var minFiles = props && props.min ? parseInt(props.min) : false;
2884 var maxFiles = props && props.max ? parseInt(props.max) : false; //Валидация
2885
2886 var validationPassed = true;
2887
2888 if (!beQuiet && !fileLines.length && !field.classList.contains('optional')) {
2889 if (!minFiles && !maxFiles) {
2890 validationPassed = false;
2891 errors = task.addErrorToField(field, 'Загрузите, пожалуйста, хотя бы один файл!', errors);
2892 } else {
2893 validationPassed = false;
2894 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);
2895 }
2896 } else if (minFiles && !beQuiet && fileLines.length < minFiles) {
2897 validationPassed = false;
2898 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);
2899 } else if (maxFiles && !beQuiet && fileLines.length > maxFiles) {
2900 validationPassed = false;
2901 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);
2902 }
2903
2904 field.classList.remove('validationRejected');
2905 field.classList.remove('validationPassed');
2906 field.classList.remove('clear');
2907 field.classList.remove('skipMe');
2908
2909 if (fileLines.length > 0) {
2910 if (validationPassed) {
2911 field.classList.add('validationPassed');
2912 } else {
2913 field.classList.add('validationRejected');
2914 }
2915 } else {
2916 field.classList.add('clear');
2917
2918 if (field.classList.contains('optional')) {
2919 field.classList.add('skipMe');
2920 }
2921 }
2922
2923 return errors;
2924 }; //Если поле не виртуальное - отображаем все его загруженные файлы
2925
2926
2927 if (!field.classList.contains('virtual') && (!field.closest('.groupContainer ') || !isGroupInfinite(field.closest('.groupContainer')))) {
2928 var fileDeleteButtons = fuFiles.querySelectorAll('.field_file__files__file .file__delete');
2929
2930 if (fileDeleteButtons && fileDeleteButtons.length > 0) {
2931 this._fileUploader.uploadedFilesDivs = fuFiles.querySelectorAll('.field_file__files__file'); //Отрисовываем в контроле загруженные в нём файлы, которые есть в скрытом загрузчике
2932
2933 var _iterator30 = _createForOfIteratorHelper(fileDeleteButtons),
2934 _step30;
2935
2936 try {
2937 for (_iterator30.s(); !(_step30 = _iterator30.n()).done;) {
2938 var fileDeleteButt = _step30.value;
2939 var attachmentId = fileDeleteButt.dataset.attachmentId;
2940 var item = this.getStorageItem('__uploadingFiles')[attachmentId];
2941
2942 if (item) {
2943 item.fileName = fileDeleteButt.parentElement.querySelector('.file__name').innerHTML;
2944
2945 if (item.fieldName === fieldName) {
2946 item.fileField = field;
2947 item.fileLine = field.addFileLine(item.fileName, attachmentId);
2948 }
2949 }
2950 }
2951 } catch (err) {
2952 _iterator30.e(err);
2953 } finally {
2954 _iterator30.f();
2955 }
2956 }
2957
2958 field.validate({}, true);
2959 }
2960
2961 field.validate({}, true);
2962 field.classList.add('initialized');
2963 },
2964 //Очищает список загруженных файлов (не удаляя их) у ШВшного поля загрузки файлов
2965 clearFileField: function clearFileField(name) {
2966 var field = this.getDOMElement().querySelector('.element.fileField.fieldName_' + name);
2967
2968 if (!field) {
2969 ce('Не найдено поле загрузки файлов для его очистки', name);
2970 return;
2971 }
2972
2973 field.clear();
2974 },
2975 //Открывает окно жалобщика
2976 submitComplaint: function submitComplaint() {
2977 if (!useComplainter) {
2978 return;
2979 }
2980
2981 var assignmentInfo = this.getAssignment().getOptions().assignment;
2982 window.Complaints.default.open({
2983 inputValues: this.getTask().input_values,
2984 assignment: {
2985 assignmentId: assignmentInfo.id,
2986 poolId: assignmentInfo.poolId,
2987 taskSuiteId: assignmentInfo.taskSuiteId
2988 }
2989 }, this.getAssignment().getId());
2990 },
2991 //Заново рендерит указанный контрол с новой спекой и сохранением солюшки
2992 replaceField: function replaceField(name, json) {
2993 var root = this.getDOMElement(),
2994 field = root.querySelector('.element.' + name);
2995
2996 if (!field) {
2997 return;
2998 }
2999
3000 this.loadSolution(this.theOneSolution);
3001 },
3002 //Записывает в солюшку все поля переданного объекта
3003 writeObjectToSolution: function writeObjectToSolution(object, postfix, solution) {
3004 if (!solution) {
3005 solution = this.theOneSolution || {};
3006 }
3007
3008 if (!object) {
3009 return solution;
3010 }
3011
3012 for (var item in object) {
3013 solution[item + postfix] = object[item];
3014 }
3015
3016 return solution;
3017 },
3018 //Заново отрисовывает указанный контрол с сохранением солюшки
3019 redrawField: function redrawField(name, group) {
3020 var root = this.getDOMElement(),
3021 field = root.querySelector('.element.' + name);
3022
3023 if (!field) {
3024 return;
3025 }
3026
3027 var fieldParent = field.parentElement;
3028 var fieldWasFocused = field.classList.contains('focused');
3029 var specField = this.getSpecField(name, group);
3030
3031 if (!specField) {
3032 return;
3033 }
3034
3035 field.outerHTML = handleFieldAndGetHTML.apply(this, [specField]);
3036 this.initFieldsHere(fieldParent);
3037 this.loadSolution(this.theOneSolution, fieldParent);
3038
3039 if (fieldWasFocused) {
3040 var newField = root.querySelector('.element.' + name);
3041 newField.click();
3042 }
3043 },
3044 redrawFields: function redrawFields(fieldsName) {
3045 var root = this.getDOMElement(),
3046 container = root.querySelector('.theOneFieldsContainer.fieldsName_' + fieldsName),
3047 spec = this.theOneSpec.spec[fieldsName];
3048
3049 if (!container) {
3050 ce("В redrawFields передано несуществующее имя theOneFieldsContainer'a", fieldsName);
3051 return;
3052 }
3053
3054 if (!spec) {
3055 ce("В redrawFields передано несуществующее имя спеки theOneSpec", fieldsName);
3056 return;
3057 }
3058
3059 var _iterator31 = _createForOfIteratorHelper(root.querySelectorAll('.theOneFieldsContainer.fieldsName_' + fieldsName)),
3060 _step31;
3061
3062 try {
3063 for (_iterator31.s(); !(_step31 = _iterator31.n()).done;) {
3064 var _container = _step31.value;
3065 var p = _container.parentElement;
3066 _container.outerHTML = getFieldsHTML.apply(this, [fieldsName]);
3067 _container = p.querySelector('.theOneFieldsContainer');
3068 this.initFieldsHere(_container);
3069 this.loadSolution(this.theOneSolution, _container);
3070 }
3071 } catch (err) {
3072 _iterator31.e(err);
3073 } finally {
3074 _iterator31.f();
3075 }
3076 },
3077 //Возвращает ссылку на конкретное поле в спеке Всевластия
3078 getSpecField: function getSpecField(name, group) {
3079 if (!this.theOneSpec.specByName[name]) {
3080 return;
3081 }
3082
3083 var field = group ? this.theOneSpec.specByName[name].getElementByName(group).groupFields.getElementByName(name) : this.theOneSpec.specByName[name].getElementByName(name);
3084 return field;
3085 },
3086 //Устанавливает/заменяет массив полей в спеке Всевластия
3087 setSpecFields: function setSpecFields(fieldsName, value) {
3088 this.theOneSpec.spec[fieldsName] = this.getJSON(value);
3089 this.getTask().input_values[fieldsName] = this.theOneSpec.spec[fieldsName];
3090 },
3091 includeGroup: function includeGroup(name) {
3092 var root = this.getDOMElement(),
3093 groupDiv = root.querySelector('.groupContainer.fieldName_' + name);
3094
3095 if (!groupDiv) {
3096 return;
3097 }
3098
3099 groupDiv.classList.remove('excluded');
3100 },
3101 excludeGroup: function excludeGroup(name) {
3102 var root = this.getDOMElement(),
3103 groupDiv = root.querySelector('.groupContainer.fieldName_' + name);
3104
3105 if (!groupDiv) {
3106 return;
3107 }
3108
3109 groupDiv.classList.add('excluded');
3110 },
3111 focusNextTask: function focusNextTask() {
3112 this.getAssignment().getTaskSuite().focusNextTask();
3113 },
3114 focusPreviousTask: function focusPreviousTask() {
3115 this.getAssignment().getTaskSuite().focusPreviousTask();
3116 },
3117
3118 /* Работа с видами (view) */
3119 //Прячет указанный view
3120 hideView: function hideView(name) {
3121 var root = this.getDOMElement(),
3122 view = root.querySelector('.view.' + name);
3123
3124 if (!view) {
3125 return;
3126 }
3127
3128 view.classList.add('hidden');
3129 },
3130 //Показывает указанный view
3131 showView: function showView(name) {
3132 var root = this.getDOMElement(),
3133 view = root.querySelector('.view.' + name);
3134
3135 if (!view) {
3136 return;
3137 }
3138
3139 view.classList.remove('hidden'); //Обновлянем все просмотрщики с канвасами
3140
3141 var _iterator32 = _createForOfIteratorHelper(view.querySelectorAll('.imageField .imageBlock canvas')),
3142 _step32;
3143
3144 try {
3145 for (_iterator32.s(); !(_step32 = _iterator32.n()).done;) {
3146 var c = _step32.value;
3147
3148 if (c.initCanvas) {
3149 c.initCanvas();
3150 }
3151 }
3152 } catch (err) {
3153 _iterator32.e(err);
3154 } finally {
3155 _iterator32.f();
3156 }
3157 },
3158 //Исключает из сбора солюшки указанный view
3159 excludeView: function excludeView(name) {
3160 var root = this.getDOMElement(),
3161 view = root.querySelector('.view.' + name);
3162
3163 if (!view) {
3164 return;
3165 }
3166
3167 view.classList.add('excluded');
3168 },
3169 //Включает в сбор солюшки указанный view
3170 includeView: function includeView(name) {
3171 var root = this.getDOMElement(),
3172 view = root.querySelector('.view.' + name);
3173
3174 if (!view) {
3175 return;
3176 }
3177
3178 view.classList.remove('excluded');
3179 },
3180 //Прячет все view
3181 hideAllViews: function hideAllViews(className) {
3182 var root = this.getDOMElement();
3183
3184 if (!className) {
3185 className = 'view';
3186 }
3187
3188 var _iterator33 = _createForOfIteratorHelper(root.querySelectorAll('.view.' + className)),
3189 _step33;
3190
3191 try {
3192 for (_iterator33.s(); !(_step33 = _iterator33.n()).done;) {
3193 var view = _step33.value;
3194 view.classList.add('hidden');
3195 }
3196 } catch (err) {
3197 _iterator33.e(err);
3198 } finally {
3199 _iterator33.f();
3200 }
3201 },
3202 //Выключает сбор солюшки во всех view
3203 excludeAllViews: function excludeAllViews(className) {
3204 var root = this.getDOMElement();
3205
3206 if (!className) {
3207 className = 'view';
3208 }
3209
3210 var _iterator34 = _createForOfIteratorHelper(root.querySelectorAll('.view.' + className)),
3211 _step34;
3212
3213 try {
3214 for (_iterator34.s(); !(_step34 = _iterator34.n()).done;) {
3215 var view = _step34.value;
3216 view.classList.add('excluded');
3217 }
3218 } catch (err) {
3219 _iterator34.e(err);
3220 } finally {
3221 _iterator34.f();
3222 }
3223 },
3224 //Показывает все view
3225 showAllViews: function showAllViews(className) {
3226 var root = this.getDOMElement();
3227
3228 if (!className) {
3229 className = 'view';
3230 }
3231
3232 var _iterator35 = _createForOfIteratorHelper(root.querySelectorAll('.view.' + className)),
3233 _step35;
3234
3235 try {
3236 for (_iterator35.s(); !(_step35 = _iterator35.n()).done;) {
3237 var view = _step35.value;
3238 view.classList.remove('hidden'); //Обновлянем все просмотрщики с канвасами
3239
3240 var _iterator36 = _createForOfIteratorHelper(view.querySelectorAll('.imageField .imageBlock canvas')),
3241 _step36;
3242
3243 try {
3244 for (_iterator36.s(); !(_step36 = _iterator36.n()).done;) {
3245 var c = _step36.value;
3246
3247 if (c.initCanvas) {
3248 c.initCanvas();
3249 }
3250 }
3251 } catch (err) {
3252 _iterator36.e(err);
3253 } finally {
3254 _iterator36.f();
3255 }
3256 }
3257 } catch (err) {
3258 _iterator35.e(err);
3259 } finally {
3260 _iterator35.f();
3261 }
3262 },
3263 //Включает сбор солюшки во всех view
3264 includeAllViews: function includeAllViews(className) {
3265 var root = this.getDOMElement();
3266
3267 if (!className) {
3268 className = 'view';
3269 }
3270
3271 var _iterator37 = _createForOfIteratorHelper(root.querySelectorAll('.view.' + className)),
3272 _step37;
3273
3274 try {
3275 for (_iterator37.s(); !(_step37 = _iterator37.n()).done;) {
3276 var view = _step37.value;
3277 view.classList.remove('excluded');
3278 }
3279 } catch (err) {
3280 _iterator37.e(err);
3281 } finally {
3282 _iterator37.f();
3283 }
3284 },
3285 //Показывает только указыннй view (остальные прячет)
3286 showOnlyThisView: function showOnlyThisView(name, className) {
3287 var root = this.getDOMElement();
3288 this.hideAllViews(className);
3289 this.showView(name);
3290 },
3291 //Включает сбор солюшки только в указанном view
3292 includeOnlyThisView: function includeOnlyThisView(name, className) {
3293 var root = this.getDOMElement();
3294 this.excludeAllViews(className);
3295 this.includeView(name);
3296 },
3297 //Компилирует HTML+Handlebars в контексте текущего таска
3298 compileHandlebars: function compileHandlebars(code, optionalContext) {
3299 var context = optionalContext || this.handlebarsContext || this.getTask().input_values;
3300 return Handlebars.compile(code)(context);
3301 },
3302 //Сохраняет данные в ШВ-шное локальное хранилище
3303 setStorageItem: function setStorageItem(key, value) {
3304 this.theOneSolution.__theOneCustomStorage[key] = value;
3305 this.theOneSolutionUpdated = true;
3306 },
3307 //Получает данные из ШВ-шного локального хранилища
3308 getStorageItem: function getStorageItem(key) {
3309 if (this.theOneSolution.__theOneCustomStorage && this.theOneSolution.__theOneCustomStorage[key]) {
3310 return this.theOneSolution.__theOneCustomStorage[key];
3311 }
3312 },
3313 //Раньше в шаблоне использовался иконочный шрифт material icons. Функция ищет все старые и новые иконки в коде и меняет их на свгшки.
3314 insertIcons: function insertIcons(where) {
3315 var root = where ? where : this.getDOMElement(),
3316 c = 0;
3317 Array.from(root.querySelectorAll('i.material-icons:not(.processed)')).forEach(function (i) {
3318 c++;
3319 i.dataset.icon = i.innerHTML;
3320 i.innerHTML = '';
3321 i.classList.add('ffIcon');
3322 });
3323 Array.from(root.querySelectorAll('.ffIcon:not(.processed)')).forEach(function (i) {
3324 var icon = svgIcons[i.dataset.icon];
3325
3326 if (!icon) {
3327 return;
3328 }
3329
3330 var html = '<svg focusable="false" xmlns="http://www.w3.org/2000/svg" ' + icon.attributes + '>';
3331
3332 if (icon.translucentPath) {
3333 html += '<path fill="currentColor" d="' + icon.translucentPath + '" opacity="0.4"></path>';
3334 }
3335
3336 if (icon.path) {
3337 html += '<path fill="currentColor" d="' + icon.path + '"></path>';
3338 }
3339
3340 i.innerHTML = html + '</svg>';
3341 i.classList.add('processed');
3342 });
3343 },
3344
3345 /* Логирование действий */
3346 getTimeStamp: function getTimeStamp() {
3347 var date = new Date(),
3348 curTime = date.getTime(),
3349 ts = this.getStorageItem('__timingsStorage');
3350 var timestamp = ts.total + (curTime - ts.focused);
3351 return timestamp;
3352 },
3353 //Обеспечивает задержку в логировании, потому что если кликнуть по элементу несфокусированного таска, то сначала сработает события клика по элементу, а только потом сфокусируется таск.
3354 logAction: function logAction(action, additional) {
3355 var _this7 = this;
3356
3357 if (!actionLogger || !this.rendered) {
3358 return;
3359 }
3360
3361 setTimeout(function () {
3362 _this7.logActionNow(action, additional);
3363 }, 60);
3364 },
3365 logActionNow: function logActionNow(action, additional) {
3366 var logObj = {
3367 time: this.getTimeStamp(),
3368 action: action
3369 },
3370 ts = this.getStorageItem('__timingsStorage');
3371
3372 if (additional) {
3373 logObj.additional = additional;
3374 }
3375
3376 this.getStorageItem('__actionLog').push(logObj);
3377 },
3378 onFocus: function onFocus() {
3379 if (!actionLogger || !this.rendered) {
3380 return;
3381 }
3382
3383 var date = new Date(),
3384 curTime = date.getTime(),
3385 ts = this.getStorageItem('__timingsStorage');
3386
3387 if (!ts.started) {
3388 ts.started = curTime;
3389 }
3390
3391 ts.focused = curTime;
3392 this.logActionNow('focus');
3393 },
3394 onBlur: function onBlur() {
3395 if (!actionLogger || !this.rendered) {
3396 return;
3397 }
3398
3399 var date = new Date(),
3400 curTime = date.getTime(),
3401 ts = this.getStorageItem('__timingsStorage');
3402
3403 if (!ts.focused) {
3404 return;
3405 }
3406
3407 ts.blurred = curTime;
3408 this.logActionNow('blur');
3409 ts.total += curTime - ts.focused;
3410 }
3411};
3412/*
3413*
3414* Методы класса TaskSuite
3415*
3416*/
3417
3418window.theOneTaskSuitePrototype = {
3419 onRender: function onRender() {
3420 var root = this.getDOMElement(),
3421 _this = this;
3422
3423 this.initSuite();
3424 document.ready.then(function () {//Кастомные штуки
3425 }).catch(function (err) {
3426 return console.error(err);
3427 }); //Кастомные штуки
3428 },
3429 //Инициализирует интерфейс Шаблона Всевластия
3430 initSuite: function initSuite() {
3431 var root = this.getDOMElement(),
3432 _this = this,
3433 tasks = root.querySelectorAll('.task'),
3434 isMobile = window.thisIsMobileDevice || window.screen.width <= 768;
3435
3436 this.tasks = tasks;
3437 window.readyToKill = false;
3438 window.curTask = 4815162342;
3439 cssNoClassSettings.forEach(function (settingName) {
3440 if (!eval(settingName)) {
3441 root.classList.add('no_' + settingName);
3442 }
3443 });
3444 cssClassSettings.forEach(function (settingName) {
3445 if (eval(settingName)) {
3446 root.classList.add(settingName);
3447 }
3448 }); //Установка флагов режима только для чтения и просмотра выполненных заданий
3449
3450 if (this.getWorkspaceOptions().isReadOnly) {
3451 this.isReadOnly = true;
3452 }
3453
3454 if (this.getWorkspaceOptions().isReviewMode) {
3455 this.isReviewMode = true;
3456 } //Инициализация поиска по глоссарию
3457
3458
3459 if (useGlossarySearch) {
3460 $.when($.getScript(jQueryUILibSrc), $.getScript(glossarySearchLib), $.Deferred(function (deferred) {
3461 $(deferred.resolve);
3462 })).done(function () {
3463 $('<link/>', {
3464 rel: 'stylesheet',
3465 type: 'text/css',
3466 href: jQueryUICssSrc
3467 }).appendTo('head');
3468
3469 var _iterator38 = _createForOfIteratorHelper(tasks),
3470 _step38;
3471
3472 try {
3473 for (_iterator38.s(); !(_step38 = _iterator38.n()).done;) {
3474 var task = _step38.value;
3475 $(task.querySelector('.btnGlossarySearch .glossarySearchForm')).glossSearch('add', {
3476 source: 'ajax_to_S3',
3477 url: glossarySearchSource
3478 });
3479 task.querySelector('.btnGlossarySearch a').addEventListener('click', function () {
3480 this.parentElement.classList.toggle('active');
3481 });
3482 }
3483 } catch (err) {
3484 _iterator38.e(err);
3485 } finally {
3486 _iterator38.f();
3487 }
3488 });
3489 } //Инициализация Жалобщика
3490
3491
3492 if (useComplainter) {
3493 var _iterator39 = _createForOfIteratorHelper(tasks),
3494 _step39;
3495
3496 try {
3497 var _loop4 = function _loop4() {
3498 var task = _step39.value;
3499 task.querySelector('.btnComplainter a').addEventListener('click', function () {
3500 _this.getTasks()[task.dataset.number].submitComplaint();
3501 });
3502 };
3503
3504 for (_iterator39.s(); !(_step39 = _iterator39.n()).done;) {
3505 _loop4();
3506 }
3507 } catch (err) {
3508 _iterator39.e(err);
3509 } finally {
3510 _iterator39.f();
3511 }
3512 } //Формируем tabList у переключалки тасков
3513 //Формируем HTML для выпадашек - таскопереключалок
3514
3515
3516 var tabListHTML = '',
3517 i = 0;
3518 Array.from(tasks).forEach(function (task) {
3519 if (!task.querySelector('.tabList')) {
3520 return;
3521 } //Это чтобы таски не перепутались, если где-то отсутствует таблист
3522
3523
3524 i++;
3525 var name = task.querySelector('.taskName');
3526 name = name ? name.innerHTML : 'Задание ' + i;
3527 tabListHTML += ' <div class="tabListItem" data-number="' + (i - 1) + '">' + '<div class="tabListItemNumber">' + i + '.</div>' + '<div class="tabListItemName">' + name + '</div>' + '</div>';
3528 }); //И прописываем его в переключалки тасков
3529
3530 i = 0;
3531 Array.from(tasks).forEach(function (task) {
3532 var tabList = task.querySelector('.tabList');
3533
3534 if (tabList) {
3535 tabList.innerHTML = '<div>' + tabListHTML + '</div>'; //Обрабатываем элементы выпадашки
3536
3537 var j = 0;
3538 Array.from(tabList.querySelectorAll('.tabListItem')).forEach(function (tlItem) {
3539 var curItemNumber = j;
3540
3541 if (i === j) {
3542 tlItem.classList.add('selected');
3543 } else tlItem.addEventListener('click', function (e) {
3544 e.preventDefault();
3545 e.stopPropagation();
3546 var n = parseInt(this.dataset.number);
3547
3548 if (!verticalSuite) {
3549 window.scrollingNow = true;
3550 root.style.left = -100 * n + 'vw';
3551 setTimeout(function () {
3552 _this.focusTask(n, false);
3553 }, 50);
3554 } else {
3555 window.scrollingNow = true;
3556 setTimeout(function () {
3557 _this.focusTask(n, true);
3558
3559 $(root).animate({
3560 scrollTop: tasks[n].offsetTop
3561 }, 200);
3562 }, 50);
3563 }
3564 });
3565
3566 j++;
3567 }); //После отрисовки страницы прописываем таблисту правильную высоту при ховере, чтобы анимация работала максимально красиво
3568
3569 document.ready.then(function () {
3570 setTimeout(function () {
3571 document.styleSheets[0].insertRule('.tabScroller:hover .tabList {max-height: ' + tabList.offsetHeight + 'px;}');
3572 document.styleSheets[0].insertRule('.tabList {max-height: 0;}');
3573 }, 0);
3574 }).catch(function (err) {
3575 return console.error(err);
3576 });
3577 i++;
3578 }
3579 }); //Переключение между тасками свайпом
3580
3581 if (isMobile) {
3582 Array.from(tasks).forEach(function (task) {
3583 var swipe = {};
3584
3585 function mbTouchStart(e) {
3586 swipe.startX = e.touches[0].clientX;
3587 swipe.startY = e.touches[0].clientY;
3588 }
3589
3590 function mbTouchEnd(e) {
3591 swipe.endX = e.changedTouches[0].clientX;
3592 swipe.endY = e.changedTouches[0].clientY;
3593
3594 if (!swipe.startX || !swipe.startY || !swipe.endX || !swipe.endY) {
3595 return;
3596 }
3597
3598 swipe.diffX = Math.abs(swipe.startX - swipe.endX);
3599 swipe.diffY = Math.abs(swipe.startY - swipe.endY);
3600
3601 if (swipe.diffX > window.screen.width / 2.5 && swipe.diffX > swipe.diffY * 2.5) {
3602 //Надо бы свайпнуть
3603 if (swipe.startX < swipe.endX) {
3604 task.querySelector('.leftScroll').click();
3605 } else {
3606 task.querySelector('.rightScroll').click();
3607 }
3608 }
3609 }
3610
3611 task.addEventListener('touchstart', mbTouchStart, false);
3612 task.addEventListener('touchend', mbTouchEnd, false);
3613 });
3614 }
3615
3616 this.initTheOneHotkeys();
3617 document.addEventListener('keydown', function (event) {
3618 if (document.activeElement && document.activeElement.closest('.noHotkeys')) {
3619 return;
3620 }
3621
3622 if (window.onSuiteKeydown) {
3623 if (onSuiteKeydown.apply(_this, [event.code, root, event])) {
3624 return;
3625 }
3626 }
3627
3628 if (_this.focusedElement && _this.focusedElement.onKey) {
3629 _this.focusedElement.onKey(event.code, event);
3630 } //Удерживание кнопки не ставит вердикт, а показывает подсказку (совместно с onKey и onkeyup)
3631
3632
3633 if (!event.repeat) {
3634 _this.keydownTime = Date.now();
3635 var key = event.key.toUpperCase();
3636
3637 if (elementButtonsHotkeysOrderString.includes(key)) {
3638 _this.keyHoldTimeout = setTimeout(function () {
3639 if (_this.focusedElement && _this.focusedElement.buttons && _this.focusedElement.closest('.task_focused')) {
3640 var btn = _this.focusedElement.buttons[elementButtonsHotkeysOrderString.indexOf(key)];
3641
3642 if (!btn) {
3643 return;
3644 }
3645
3646 var popup = btn.parentElement.querySelector('.popUpTitle');
3647
3648 if (!popup) {
3649 return;
3650 }
3651
3652 _this.openedPopup = popup;
3653 popup.parentElement.classList.add('openedByKey');
3654 }
3655 }, timeToOpenHintByKey);
3656 }
3657 }
3658
3659 if (event.code === "Tab") {
3660 event.preventDefault();
3661 }
3662
3663 if (!hotkeysEnabled) {
3664 return;
3665 }
3666
3667 if (event.code === "Escape" && document.activeElement && document.activeElement.closest('.element') && (document.activeElement.tagName === 'INPUT' || document.activeElement.tagName === 'TEXTAREA')) {
3668 document.activeElement.blur();
3669 }
3670 /*if (event.keyCode!=9 && document.activeElement && document.activeElement.closest('.element') && document.activeElement.tagName=='TEXTAREA' && document.activeElement.value.length>0) {
3671 _this.focusElement(parseInt(document.activeElement.closest('.element').dataset.number));
3672 return;
3673 }*/
3674
3675
3676 var tf = root.querySelector('.task_focused');
3677
3678 if (!tf) {
3679 return false;
3680 }
3681
3682 if (event.code === "Tab") {
3683 //TAB
3684 _this.focusNextElement();
3685 }
3686 });
3687 document.addEventListener('keyup', function (event) {
3688 //Удерживание кнопки не ставит вердикт, а показывает подсказку (совместно с onKey и onkeydown)
3689 clearTimeout(_this.keyHoldTimeout);
3690
3691 if (_this.openedPopup) {
3692 _this.openedPopup.parentElement.classList.remove('openedByKey');
3693
3694 _this.openedPopup = false;
3695 }
3696 }); //Проставляем номера и количество тасков в листалках и биндим действия на кнопки
3697
3698 root.style.left = '0';
3699
3700 var _loop5 = function _loop5(_i3) {
3701 tasks[_i3].querySelector('.curTab').innerHTML = _i3 + 1;
3702 tasks[_i3].querySelector('.tabsCount').innerHTML = tasks.length;
3703 tasks[_i3].dataset.number = _i3.toString();
3704 tasks[_i3].querySelector('.leftScroll').i = _i3;
3705 tasks[_i3].querySelector('.rightScroll').i = _i3; //Переключение между тасками
3706
3707 if (!verticalSuite) {
3708 //Горизонтальное расположение тасков в сьюте (с листалкой)
3709 tasks[_i3].querySelector('.leftScroll').addEventListener('click', function () {
3710 if (this.i > 0 && !window.scrollingNow) {
3711 window.scrollingNow = true;
3712 root.style.left = parseInt(root.style.left) + 100 + 'vw';
3713 setTimeout(function () {
3714 _this.focusTask(_i3 - 1, false);
3715 }, 50);
3716 }
3717 });
3718
3719 tasks[_i3].querySelector('.rightScroll').addEventListener('click', function () {
3720 if (this.i < tasks.length - 1 && !window.scrollingNow) {
3721 window.scrollingNow = true;
3722 root.style.left = parseInt(root.style.left) - 100 + 'vw';
3723 setTimeout(function () {
3724 _this.focusTask(_i3 + 1, false);
3725 }, 50);
3726 }
3727 });
3728 } else {
3729 //Традиционное, вертикальное расположение тасков в сьюте
3730 tasks[_i3].querySelector('.leftScroll').addEventListener('click', function () {
3731 if (window.curTask > 0) {
3732 window.scrollingNow = true;
3733 setTimeout(function () {
3734 _this.focusTask(window.curTask - 1, true);
3735 }, 50);
3736 }
3737 });
3738
3739 tasks[_i3].querySelector('.rightScroll').addEventListener('click', function () {
3740 if (window.curTask < tasks.length - 1) {
3741 window.scrollingNow = true;
3742 setTimeout(function () {
3743 _this.focusTask(window.curTask + 1, true);
3744 }, 50);
3745 }
3746 });
3747 } //Сворачивалка/разворачивалка блоков
3748
3749
3750 if (taskMinimizer) {
3751 Array.from(tasks[_i3].querySelectorAll('.block.minimizable .blockTitle:not(.tabScroller):not(.searchField):not(.darkButt)')).forEach(function (btn) {
3752 if (!isMobile) {
3753 btn.addEventListener('click', function () {
3754 if (this.parentElement.classList.contains('minimized')) {
3755 this.parentElement.classList.remove('minimized');
3756 } else {
3757 this.parentElement.classList.add('minimized');
3758 }
3759 });
3760 } else {
3761 btn.closest('.block').classList.remove('minimizable');
3762 }
3763 });
3764 }
3765 };
3766
3767 for (var _i3 = 0; _i3 < tasks.length; _i3++) {
3768 _loop5(_i3);
3769 } //Включаем светлую тему для джедаев
3770
3771
3772 if (this.storage && this.storage.getItem('funfrogLightSide') === false) {
3773 var _iterator40 = _createForOfIteratorHelper(root.querySelectorAll('.task .btnDarkSide a')),
3774 _step40;
3775
3776 try {
3777 for (_iterator40.s(); !(_step40 = _iterator40.n()).done;) {
3778 var switchButton = _step40.value;
3779 switchButton.setAttribute('title', localize('lightSide'));
3780 root.classList.add('darkSide');
3781 }
3782 } catch (err) {
3783 _iterator40.e(err);
3784 } finally {
3785 _iterator40.f();
3786 }
3787 } //Going full Anakin and back
3788
3789
3790 var _iterator41 = _createForOfIteratorHelper(root.querySelectorAll('.task .btnDarkSide a')),
3791 _step41;
3792
3793 try {
3794 for (_iterator41.s(); !(_step41 = _iterator41.n()).done;) {
3795 var _switchButton = _step41.value;
3796
3797 _switchButton.addEventListener("click", function () {
3798 if (root.classList.contains('darkSide')) {
3799 var _iterator46 = _createForOfIteratorHelper(root.querySelectorAll('.task .btnDarkSide a')),
3800 _step46;
3801
3802 try {
3803 for (_iterator46.s(); !(_step46 = _iterator46.n()).done;) {
3804 var _switchButton2 = _step46.value;
3805
3806 _switchButton2.setAttribute('title', localize('darkSide'));
3807 }
3808 } catch (err) {
3809 _iterator46.e(err);
3810 } finally {
3811 _iterator46.f();
3812 }
3813
3814 root.classList.remove('darkSide');
3815
3816 if (_this.storage) {
3817 _this.storage.setItem('funfrogLightSide', 'true', new Date().getTime() + 30000000);
3818 }
3819 } else {
3820 var _iterator47 = _createForOfIteratorHelper(root.querySelectorAll('.task .btnDarkSide a')),
3821 _step47;
3822
3823 try {
3824 for (_iterator47.s(); !(_step47 = _iterator47.n()).done;) {
3825 var _switchButton3 = _step47.value;
3826
3827 _switchButton3.setAttribute('title', localize('lightSide'));
3828 }
3829 } catch (err) {
3830 _iterator47.e(err);
3831 } finally {
3832 _iterator47.f();
3833 }
3834
3835 root.classList.add('darkSide');
3836
3837 if (_this.storage) {
3838 _this.storage.setItem('funfrogLightSide', 'false', new Date().getTime() + 30000000);
3839 }
3840 }
3841 });
3842 } //Пытаемся выпытать у браузера сохранённую солюшку, если она была
3843 //this.getSolutionsFromStorage(); Перенесено в loadStuffFromTheOneLibrary()
3844 //Периодически сохраняем всю солюшку в локальное хранилище, чтобы потом, если что, можно было восстановить
3845
3846 } catch (err) {
3847 _iterator41.e(err);
3848 } finally {
3849 _iterator41.f();
3850 }
3851
3852 if (_this.storage) {
3853 var options = _this.getOptions(),
3854 assId = options.assignment._options.assignment.id,
3855 wOptions = options.workspaceOptions;
3856
3857 if (assId && !wOptions.isReadOnly && !wOptions.isReviewMode) {
3858 setInterval(function () {
3859 var solutions = [];
3860 var solutionsUpdated = false;
3861
3862 _this.getTasks().forEach(function (task) {
3863 if (task.theOneSolutionUpdated) {
3864 solutionsUpdated = true;
3865 task.theOneSolutionUpdated = false;
3866 }
3867
3868 solutions.push(task.theOneSolution);
3869 });
3870
3871 if (!solutionsUpdated) {
3872 return;
3873 }
3874
3875 if (printSolution) {
3876 console.log('Сохранили солюшку:', JSON.parse(JSON.stringify(solutions)));
3877 }
3878
3879 _this.storage.setItem('funfrogSavedSolutions_' + assId, JSON.stringify(solutions));
3880 }, 10000);
3881 }
3882 } //Биндим действия на все helpContainer'ы
3883
3884
3885 var _iterator42 = _createForOfIteratorHelper(root.querySelectorAll('.helpContainer:not(.initialized)')),
3886 _step42;
3887
3888 try {
3889 for (_iterator42.s(); !(_step42 = _iterator42.n()).done;) {
3890 var q = _step42.value;
3891 initHelpContainer(q);
3892 }
3893 } catch (err) {
3894 _iterator42.e(err);
3895 } finally {
3896 _iterator42.f();
3897 }
3898
3899 document.ready.then(function () {
3900 //Обработка миспринтов во всех тасках (если они там есть)
3901 if (misprintsContainers && Array.isArray(misprintsContainers)) {
3902 var _iterator43 = _createForOfIteratorHelper(tasks),
3903 _step43;
3904
3905 try {
3906 for (_iterator43.s(); !(_step43 = _iterator43.n()).done;) {
3907 var task = _step43.value;
3908 var misprintsDiv = task.querySelector('.stringifiedMisprints');
3909
3910 if (!misprintsDiv) {
3911 continue;
3912 }
3913
3914 var misprints = JSON.parse(misprintsDiv.innerHTML);
3915
3916 if (!misprints || !Array.isArray(misprints)) {
3917 continue;
3918 }
3919
3920 var mString = '';
3921
3922 var _iterator44 = _createForOfIteratorHelper(misprints),
3923 _step44;
3924
3925 try {
3926 for (_iterator44.s(); !(_step44 = _iterator44.n()).done;) {
3927 var misprint = _step44.value;
3928 mString += '|(\\s+|^)\\S?' + misprint.replace(/(ёшь|умя|ами|ому|емя|ого|ими|ишь|ёте|ите|еми|ёт|ий|ам|ые|ый|ие|am|ая|ас|ax|ит|ем|её|ей|их|ию|ею|ex|ух|ут|ёх|ум|cm|им|ую|шь|ою|ми|оё|ое|ой|ов|мя|ом|ы|и|й|о|е|ъ|у|э|а|я|м|ю|ь)$/i, '') + '\\S*';
3929 }
3930 } catch (err) {
3931 _iterator44.e(err);
3932 } finally {
3933 _iterator44.f();
3934 }
3935
3936 var regExp = new RegExp('(' + mString.substr(1) + ')', 'gi');
3937
3938 var _iterator45 = _createForOfIteratorHelper(misprintsContainers),
3939 _step45;
3940
3941 try {
3942 for (_iterator45.s(); !(_step45 = _iterator45.n()).done;) {
3943 var selector = _step45.value;
3944 var div = task.querySelector(selector);
3945
3946 if (!div) {
3947 continue;
3948 }
3949
3950 var text = div.innerText;
3951 div.innerHTML = text.replace(regExp, '<span class="misprinted">$1</span>');
3952 }
3953 } catch (err) {
3954 _iterator45.e(err);
3955 } finally {
3956 _iterator45.f();
3957 }
3958 }
3959 } catch (err) {
3960 _iterator43.e(err);
3961 } finally {
3962 _iterator43.f();
3963 }
3964 } //Завершение инициализации
3965
3966
3967 window.readyToKill = true;
3968
3969 _this.focusTask(0);
3970
3971 if (!_this.getTasks()[0].getStorageItem('__timingsStorage').started) {
3972 _this.getTasks()[0].onFocus();
3973 } //Устанавливаем фокус на первый элемент
3974
3975 /*if (hotkeysEnabled) {
3976 const elementToFocus = root.querySelector('.focusMeOnStart');
3977 if (elementToFocus) {
3978 _this.focusElement(elementToFocus.dataset.number);
3979 } else {
3980 _this.focusNextElement();
3981 }
3982 }*/
3983
3984
3985 if (window.onSuiteReady) {
3986 onSuiteReady.apply(_this, [root]);
3987 }
3988 }).catch(function (err) {
3989 return console.error(err);
3990 });
3991 },
3992 //Инициализация ШВшных горячих клавиш (ТуДу: перенести в таск)
3993 initTheOneHotkeys: function initTheOneHotkeys() {
3994 var _this = this,
3995 root = this.getDOMElement();
3996
3997 if (hotkeysEnabled) {
3998 this.elements = root.querySelectorAll('.element:not(.unfocusable)');
3999
4000 for (var i = 0; i < _this.elements.length; i++) {
4001 this.elements[i].dataset.number = i.toString();
4002
4003 if (!this.elements[i].classList.contains('hotKeysBinded')) {
4004 this.elements[i].addEventListener('click', function (e) {
4005 if (!readyToKill || !e.isTrusted) {
4006 return;
4007 }
4008
4009 _this.focusElement(parseInt(this.dataset.number));
4010 });
4011 this.elements[i].classList.add('hotKeysBinded');
4012 } //Прорисовываем кнопкам соответствующие горячие клавиши
4013
4014
4015 var buttons = this.elements[i].querySelectorAll('.btn');
4016
4017 for (var j = 0; j < buttons.length; j++) {
4018 var div = buttons[j].querySelector('.hotkeyHint');
4019
4020 if (!div) {
4021 div = document.createElement('div');
4022 div.className = 'hotkeyHint';
4023 div.innerHTML = elementButtonsHotkeysOrderString[j];
4024 buttons[j].appendChild(div);
4025 } else {
4026 div.innerHTML = elementButtonsHotkeysOrderString[j];
4027 }
4028 }
4029 }
4030 }
4031 },
4032 getSolutionsFromStorage: function getSolutionsFromStorage() {
4033 var options = this.getOptions(),
4034 assId = options.assignment._options.assignment.id,
4035 tasks = this.getTasks();
4036
4037 if (assId) {
4038 var sols = this.storage.getItem('funfrogSavedSolutions_' + assId);
4039
4040 if (sols) {
4041 var _tasks = this.getTasks();
4042
4043 for (var i in _tasks) {
4044 _tasks[i].theOneSolution = sols[i];
4045 }
4046
4047 if (printSolution) {
4048 console.log('Солюшка из локального хранилища:', JSON.parse(JSON.stringify(sols)));
4049 }
4050
4051 return true;
4052 }
4053 }
4054 },
4055 maybeScrollToThisElement: function maybeScrollToThisElement(element, forceTop) {
4056 //Прокручивает страницу к элементу, если он находится за пределами экрана
4057 var root = this.getDOMElement(),
4058 what2scroll = verticalSuite ? root : fullScreenMode ? root.querySelector('.task_focused .block.main>.blockContent') : root.querySelector('.task_focused'),
4059 topMargin = fullScreenMode ? 40 : 10;
4060
4061 if (!forceTop && element.getBoundingClientRect().bottom + 10 > document.documentElement.clientHeight) {
4062 //Надо подкрутить страницу
4063 $(what2scroll).animate({
4064 scrollTop: what2scroll.scrollTop + element.getBoundingClientRect().bottom - document.documentElement.clientHeight + 10
4065 }, 200);
4066 } else if (forceTop || element.getBoundingClientRect().top - topMargin < 0) {
4067 //Надо подкрутить страницу
4068 $(what2scroll).animate({
4069 scrollTop: what2scroll.scrollTop + element.getBoundingClientRect().top - topMargin
4070 }, 200);
4071 }
4072 },
4073 focusNextElement: function focusNextElement(dontScroll) {
4074 //Пытается перевести фокус на следующий элемент
4075 if (window.thisIsMobileDevice) {
4076 return;
4077 }
4078
4079 var root = this.getDOMElement();
4080
4081 var _this = this;
4082
4083 var n = _this.focusedElement && _this.focusedElement.closest('.task_focused') ? //Если есть элемент с фокусом в этом таске
4084 parseInt(_this.focusedElement.dataset.number) + 1 : //Берём следующий
4085 parseInt(root.querySelector('.task_focused .element:not(.unfocusable)') ? root.querySelector('.task_focused .element:not(.unfocusable)').dataset.number : null); //Или берём первый в этом таске
4086
4087 if (!n && n !== 0) {
4088 return;
4089 }
4090
4091 var nothing = false,
4092 nStart = n; //Запоминаем текущий номер элемента, а то вдруг по кругу пройдём: чтоб не зациклиться.
4093
4094 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')) {
4095 //Если текущий элемент спрятан или не в этом таске
4096 n++;
4097
4098 if (n >= _this.elements.length) {
4099 n = root.querySelector('.task_focused .element:not(.unfocusable)').dataset.number;
4100 }
4101
4102 if (n === nStart) {
4103 nothing = true;
4104 break;
4105 }
4106 }
4107
4108 if (!nothing) {
4109 _this.focusElement(n, dontScroll);
4110 }
4111 },
4112 focusPreviousElement: function focusPreviousElement() {
4113 //Пытается перевести фокус на предыдущий элемент
4114 if (window.thisIsMobileDevice) {
4115 return;
4116 }
4117
4118 var root = this.getDOMElement();
4119
4120 var _this = this;
4121
4122 var n = _this.focusedElement && _this.focusedElement.closest('.task_focused') ? //Если есть элемент с фокусом в этом таске
4123 parseInt(_this.focusedElement.dataset.number) - 1 : //Берём предыдущий
4124 _this.elements.length - 1; //Или берём тупо самый последний
4125
4126 var nothing = false,
4127 nStart = n; //Запоминаем текущий номер элемента, а то вдруг по кругу пройдём: чтоб не зациклиться.
4128
4129 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')) {
4130 //Если текущий элемент спрятан или не в этом таске
4131 n--;
4132
4133 if (n < 0) {
4134 n = _this.elements.length - 1;
4135 }
4136
4137 if (n === nStart) {
4138 nothing = true;
4139 break;
4140 }
4141 }
4142
4143 if (!nothing) {
4144 _this.focusElement(n);
4145 }
4146 },
4147 focusElement: function focusElement(n, dontScroll) {
4148 if (window.thisIsMobileDevice) {
4149 return;
4150 }
4151
4152 var root = this.getDOMElement();
4153
4154 var _this = this;
4155
4156 n = parseInt(n);
4157 document.activeElement.blur();
4158 Array.from(root.querySelectorAll('.element.focused')).forEach(function (ef) {
4159 ef.classList.remove('focused');
4160 });
4161 this.focusedElement = _this.elements[n];
4162 this.focusedElement.classList.add('focused'); //На потестить
4163
4164 if (!dontScroll) {
4165 this.maybeScrollToThisElement(this.focusedElement);
4166 }
4167
4168 if (this.isReadOnly || this.interfaceBlocked) {
4169 return;
4170 }
4171
4172 if (_this.focusedElement.querySelector('input')) {
4173 setTimeout(function () {
4174 var input = _this.focusedElement.querySelector('input');
4175
4176 if (input) {
4177 input.focus();
4178 }
4179 }, 100);
4180 } else if (_this.focusedElement.querySelector('textarea')) {
4181 setTimeout(function () {
4182 _this.focusedElement.querySelector('textarea').focus();
4183 }, 100);
4184 } else if (!_this.focusedElement.buttons) {
4185 _this.focusedElement.buttons = _this.focusedElement.querySelectorAll('.btn');
4186 }
4187 },
4188 focusTask: function focusTask(i) {
4189 var _this = this;
4190
4191 var n = parseInt(i);
4192
4193 if (!verticalSuite) {
4194 if (!this.tasks) {
4195 return;
4196 }
4197
4198 Array.from(this.getDOMElement().querySelectorAll('.task_focused')).forEach(function (task) {
4199 task.classList.remove('task_focused');
4200 });
4201 this.tasks[n].classList.add('task_focused');
4202 } else {
4203 TolokaHandlebarsTaskSuite.prototype.focusTask.apply(this, arguments);
4204 }
4205
4206 if (window.curTask === n) {
4207 return;
4208 }
4209
4210 window.curTask = n; //Обновлянем все просмотрщики с канвасами
4211
4212 var _iterator48 = _createForOfIteratorHelper(_this.tasks[window.curTask].querySelectorAll('.imageField .imageBlock canvas')),
4213 _step48;
4214
4215 try {
4216 for (_iterator48.s(); !(_step48 = _iterator48.n()).done;) {
4217 var c = _step48.value;
4218
4219 if (c.initCanvas) {
4220 c.initCanvas();
4221 }
4222 }
4223 } catch (err) {
4224 _iterator48.e(err);
4225 } finally {
4226 _iterator48.f();
4227 }
4228
4229 setTimeout(function () {
4230 window.scrollingNow = false;
4231
4232 var elementToFocus = _this.tasks[window.curTask].querySelector('.focusMeOnStart');
4233
4234 if (!elementToFocus && _this.focusedElement && _this.focusedElement.closest('.task_focused')) {
4235 elementToFocus = _this.focusedElement;
4236 }
4237
4238 if (elementToFocus) {
4239 _this.focusElement(elementToFocus.dataset.number, true);
4240 } else {
4241 _this.focusNextElement(true);
4242 }
4243
4244 _this.maybeScrollToThisElement(_this.tasks[window.curTask], true);
4245 }, 250);
4246 },
4247 focusNextTask: function focusNextTask() {
4248 if (this.getDOMElement().querySelector('.window.opened')) {
4249 return;
4250 }
4251
4252 this.getDOMElement().querySelector('.task_focused .rightScroll').click();
4253 },
4254 focusPreviousTask: function focusPreviousTask() {
4255 if (this.getDOMElement().querySelector('.window.opened')) {
4256 return;
4257 }
4258
4259 this.getDOMElement().querySelector('.task_focused .leftScroll').click();
4260 },
4261 initHotkeys: function initHotkeys() {
4262 this.hotkey.reset();
4263 this.hotkey.on('arrow-left', this.focusPreviousTask, this);
4264 this.hotkey.on('arrow-down', this.focusNextTask, this); //Почему-то это arrow-right
4265
4266 if (!hotkeysEnabled) {
4267 this.hotkey.on('arrow-up', this.focusPreviousTask, this);
4268 this.hotkey.on('arrow-right', this.focusNextTask, this); //Почему-то это arrow-down
4269 } else {
4270 this.hotkey.on('arrow-up', this.focusPreviousElement, this);
4271 this.hotkey.on('arrow-right', this.focusNextElement, this); //Почему-то это arrow-down
4272
4273 this.hotkey.on('key', this.onKey, this);
4274 }
4275 },
4276 onKey: function onKey(key) {
4277 if (Date.now() - this.keydownTime > timeToOpenHintByKey) {
4278 return;
4279 }
4280
4281 if (this.focusedElement && this.focusedElement.buttons && this.focusedElement.closest('.task_focused') && elementButtonsHotkeysOrderString.includes(key)) {
4282 var btn = this.focusedElement.buttons[elementButtonsHotkeysOrderString.indexOf(key)];
4283
4284 if (btn) {
4285 if (btn.classList.contains('linkButton')) {
4286 btn.querySelector('a').click();
4287 } else {
4288 btn.click();
4289 }
4290 }
4291 }
4292 },
4293 validate: function validate() {
4294 var root = this.getDOMElement(),
4295 _this = this;
4296
4297 if (typeof beforeTaskSuiteValidation !== "undefined") {
4298 var _result = beforeTaskSuiteValidation.apply(this, [root]);
4299
4300 if (_result) {
4301 return true;
4302 }
4303 }
4304
4305 window.noValidationErrors = true; //Перевод фокуса на активный таск (он может быть не первым, если это задание - обучение, и были указаны неправильные ответы)
4306
4307 window.hintScrollerCounter = 0;
4308 var tf = root.querySelector('.task_focused');
4309
4310 if (!tf) {
4311 this.focusTask(0);
4312 tf = root.querySelector('.task_focused');
4313 }
4314
4315 this.currentFocusedTaskNumber = tf.dataset.number || 0;
4316 clearInterval(window.hintScrollTimer);
4317 window.hintScrollTimer = setInterval(function () {
4318 var curTask = root.querySelector('.task_focused');
4319
4320 if (curTask) {
4321 if (!verticalSuite) {
4322 if (curTask.dataset.number !== _this.currentFocusedTaskNumber) {
4323 root.style.left = curTask.dataset.number * -100 + 'vw';
4324 }
4325 } else {
4326 window.scrollingNow = true;
4327 window.curTask = parseInt(curTask.dataset.number);
4328 setTimeout(function () {
4329 $(root).animate({
4330 scrollTop: curTask.offsetTop
4331 }, 200);
4332 setTimeout(function () {
4333 window.scrollingNow = false;
4334 }, 250);
4335 }, 50);
4336 }
4337 }
4338
4339 window.hintScrollerCounter++;
4340
4341 if (window.hintScrollerCounter > 1 || curTask && _this.currentFocusedTaskNumber !== curTask.dataset.number) {
4342 clearInterval(window.hintScrollTimer);
4343 }
4344 }, 1000);
4345 var result = TolokaHandlebarsTaskSuite.prototype.validate.apply(this, arguments);
4346
4347 if (printSolution) {
4348 console.log('Итоговая солюшка тасксьюта: ', arguments[0]);
4349 }
4350
4351 return result;
4352 }
4353};
4354/*
4355*
4356* Хелперы Handlebars
4357*
4358*/
4359
4360window.theOneHandlebarsHelpers = {
4361 //Рисует поля для выбора ответов
4362 drawFields: function drawFields(fields) {
4363 var context = this; //Логика такая - перебираем все поля во входном JSON и отрисовываем их. Вешать на них валидацию будем в onRender каждого таска
4364
4365 return getFieldsHTML(fields, context);
4366 },
4367 urlDecode: function urlDecode(url) {
4368 return decodeURIComponent(url.replace(/\+/g, ' '));
4369 },
4370 urlEncode: function urlEncode(url) {
4371 return encodeURIComponent(url);
4372 },
4373 urlRemoveSpacesAndEncode: function urlRemoveSpacesAndEncode(url) {
4374 return encodeURIComponent(url.replace(/\s+/g, ''));
4375 },
4376 //Выводит строки для описания входного задания - возможно, стоит удалить?
4377 drawInputStrings: function drawInputStrings(inputStrings, placeholders) {
4378 var out = '';
4379 Array.from(inputStrings).forEach(function (inputString) {
4380 //У нас могут быть либо обычные плейсхолдеры - %%имя%%, либо со ссылкой - %%имя||ссылка%%. Меняем их тут на соответствующий код.
4381 var s = inputString.text.replace(/\$\$.*?\$\$/g, function (str) {
4382 var placeholder = str.match(/[$|][^$|]+[$|]/g);
4383
4384 if (placeholder[1]) {
4385 return '<a href="' + placeholders[placeholder[1].match(/[$|]([^$|]+)[$|]/)[1]] + '" target="_blank">' + placeholders[placeholder[0].match(/[$|]([^$|]+)[$|]/)[1]] + '</a>';
4386 } else if (placeholder[0]) {
4387 return placeholders[placeholder[0].match(/[$|]([^$|]+)[$|]/)[1]];
4388 }
4389 });
4390 out += '<div class="stepTitle">' + s + '</div>';
4391 });
4392 return out;
4393 },
4394 //Помогает передавать объекты как аргументы хелперов
4395 o: function o(_ref) {
4396 var hash = _ref.hash;
4397 return hash;
4398 },
4399 //Устанавливает словарь локализации window.l10n
4400 setLocalization: function setLocalization(l10n) {
4401 window.handlebarsContext = this;
4402 window.currentRenderingTask.handlebarsContext = this;
4403 window.l10n = l10n;
4404 },
4405 //Локализует строку в соответствии с window.l10n
4406 l: function l(t) {
4407 return localize(t);
4408 },
4409 setTaskName: function setTaskName(name) {
4410 window.handlebarsContext = this;
4411 window.currentRenderingTask.handlebarsContext = this;
4412 name = name ? name.length < 60 ? name : name.substr(0, 57) + '...' : 'Задание';
4413 var taskNameHTML = "<div class='taskName'>".concat(name, "</div>");
4414 return taskNameHTML;
4415 },
4416 ploosAdeen: function ploosAdeen(someNumb) {
4417 return someNumb + 1;
4418 },
4419 shortName: function shortName(name) {
4420 return name ? name.length < 60 ? name : name.substr(0, 57) + '...' : 'Задание';
4421 },
4422 e: function e(options) {
4423 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>");
4424 },
4425 a: function a(options) {
4426 if (!options.hash.title) {
4427 options.hash.title = options.hash.href;
4428 }
4429
4430 if (!options.hash.name) {
4431 options.hash.name = '';
4432 }
4433
4434 if (!options.hash.class) {
4435 options.hash.class = '';
4436 }
4437
4438 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>");
4439 },
4440 window: function window(options) {
4441 return "<div class=\"window ".concat(options.hash.name, " ").concat(options.hash.class, "\">").concat(options.fn(this), "</div>");
4442 },
4443 v: function v(options) {
4444 return "<div class=\"view ".concat(options.hash.name, " ").concat(options.hash.class, "\" data-name=\"").concat(options.hash.name, "\">").concat(options.fn(this), "</div>");
4445 },
4446 flexBox: function flexBox(options) {
4447 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>");
4448 },
4449 spoiler: function spoiler(options) {
4450 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({
4451 icon: 'angle_down',
4452 caption: options.hash.title
4453 }), "\n </div>\n <div class='spoilerContainer'>\n <div class='spoilerContent'>\n ").concat(options.fn(this), "\n </div>\n </div>\n </div>");
4454 },
4455 iLine: function iLine(options) {
4456 if (!options.hash.name) {
4457 options.hash.name = '';
4458 }
4459
4460 if (!options.hash.title) {
4461 options.hash.title = '';
4462 }
4463
4464 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>";
4465 },
4466 iLines: function iLines(options) {
4467 if (!options.hash.name) {
4468 options.hash.name = '';
4469 }
4470
4471 if (!options.hash.title) {
4472 options.hash.title = '';
4473 }
4474
4475 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>";
4476 },
4477 baloon: function baloon(options) {
4478 var opacity = options.hash.opacity ? options.hash.opacity : 20;
4479 var bdOpacity = Math.min(255, parseInt(opacity, 16) + 40).toString(16);
4480 options.hash.color = '#' + options.hash.color;
4481 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>";
4482 },
4483 block: function block(options) {
4484 var blockButtons = '';
4485
4486 if (!options.hash.class) {
4487 options.hash.class = '';
4488 }
4489
4490 if (taskMinimizer && !options.hash.class.includes('nonMinimizable') && (!fullScreenMode || fullScreenMode && !options.hash.class.includes('main'))) {
4491 options.hash.class += ' minimizable';
4492 }
4493
4494 if (options.hash.class.includes('main')) {
4495 if (useComplainter) {
4496 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>");
4497 }
4498
4499 if (useGlossarySearch) {
4500 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>");
4501 }
4502
4503 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>");
4504
4505 if (window.customTitleElements) {
4506 var _iterator49 = _createForOfIteratorHelper(customTitleElements),
4507 _step49;
4508
4509 try {
4510 for (_iterator49.s(); !(_step49 = _iterator49.n()).done;) {
4511 var e = _step49.value;
4512 blockButtons += "\n <div class='blockTitle custom ".concat(e.name, " ").concat(e.class ? e.class : '', "'>\n ").concat(Handlebars.compile(e.content)(this), "\n </div>");
4513 }
4514 } catch (err) {
4515 _iterator49.e(err);
4516 } finally {
4517 _iterator49.f();
4518 }
4519 }
4520
4521 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 ";
4522 }
4523
4524 var title = '';
4525
4526 if (options.hash.title) {
4527 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>");
4528 } // свернуть блок, если блоку назначен класс minimized
4529
4530
4531 var minimized = '';
4532
4533 if (options.hash.class.includes('minimized')) {
4534 minimized = 'minimized';
4535 options.hash.class = options.hash.class.replace('minimized', '');
4536 } //Подсказка
4537
4538
4539 var help = '';
4540
4541 if (options.hash.help) {
4542 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>");
4543 }
4544
4545 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>");
4546 },
4547 //Рисует баннер для шаблонов Директа
4548 drawBanner: function drawBanner(banner) {
4549 if (!banner) {
4550 return;
4551 }
4552
4553 var b = banner.data[0];
4554
4555 if (!b) {
4556 return;
4557 }
4558
4559 var title = b.title + (b.title_extension ? ' - ' + b.title_extension : '');
4560 var domain = /https?:\/\/([^/]+)/.exec(b.parametrized_href)[1];
4561 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>';
4562 return out;
4563 },
4564 //Рисует дополнительные данные баннера для шаблонов Директа
4565 drawBannerInfo: function drawBannerInfo(banner) {
4566 if (!banner) {
4567 return;
4568 }
4569
4570 var b = banner.data[0];
4571 var m = banner.meta;
4572
4573 if (!b || !m) {
4574 return;
4575 }
4576
4577 var infoLines = '';
4578
4579 function addInfoLine(title, lineClass, value, valueType, additional) {
4580 var v;
4581
4582 if (!valueType) {
4583 valueType = 'text';
4584 }
4585
4586 switch (valueType) {
4587 case 'link':
4588 v = '<a target="_blank" href="' + additional + '" rel="noreferrer">' + value + '</a>';
4589 break;
4590
4591 case 'somethingOther':
4592 break;
4593
4594 default:
4595 v = value;
4596 }
4597
4598 var line = '<div class="bannerInfoLine ' + lineClass + '">' + '<div class="bilTitle">' + title + '</div>' + '<div class="bilValue ' + valueType + '">' + v + '</div>' + '</div>';
4599 infoLines += line;
4600 }
4601
4602 function arrayOrStringToString(something) {
4603 if (Array.isArray(something)) {
4604 var list = '';
4605
4606 var _iterator50 = _createForOfIteratorHelper(something),
4607 _step50;
4608
4609 try {
4610 for (_iterator50.s(); !(_step50 = _iterator50.n()).done;) {
4611 var item = _step50.value;
4612 list += ', ' + item;
4613 }
4614 } catch (err) {
4615 _iterator50.e(err);
4616 } finally {
4617 _iterator50.f();
4618 }
4619
4620 list = list.substr(2);
4621 return list;
4622 } else if (something) {
4623 return something;
4624 }
4625 } //Регионы показа
4626
4627
4628 if (b.geo_country) {
4629 addInfoLine('Регионы показа:', 'geoCountry', arrayOrStringToString(b.geo_country));
4630 } //ГеоТекст
4631 //if (b.geo_text) {addInfoLine('Регион подробно:', 'geoText', b.geo_text);}
4632 //Языки
4633
4634
4635 if (b.lang) {
4636 addInfoLine('Языки:', 'lang', arrayOrStringToString(b.lang));
4637 } //Логин
4638
4639
4640 if (b.login) {
4641 addInfoLine('Логин:', 'login', b.login);
4642 } //Просмотр баннера
4643
4644
4645 if (m.banner_id) {
4646 addInfoLine('Просмотр баннера:', 'bannerLink', m.banner_id, 'link', 'https://direct-mod.yandex-team.ru/history/banner/?bid=' + m.banner_id);
4647 } //Просмотр кампании
4648
4649
4650 if (m.campaign_id) {
4651 addInfoLine('Просмотр кампании:', 'campaignLink', m.campaign_id, 'link', 'https://direct-mod.yandex-team.ru/campaign/show/?cid=' + m.campaign_id);
4652 }
4653
4654 var out = '<div class="element alien unfocusable bannerInfoElement">' + '<div class="bannerInfoContainer">' + '<div class="bannerInfo">' + infoLines + '</div>' + '</div>' + '</div>';
4655 return out;
4656 },
4657 misprints: function misprints(_misprints) {
4658 if (!_misprints) {
4659 return;
4660 }
4661
4662 var stringifiedMisprints = '<div class="stringifiedMisprints" style="display: none;">' + JSON.stringify(_misprints) + '</div>';
4663 return stringifiedMisprints;
4664 }
4665};
4666/*
4667*
4668* Всякие функции
4669*
4670*/
4671
4672window.dumpObjectKeysToDocumentBody = function (obj) {
4673 document.querySelector('body').innerHTML += '<br><br>';
4674
4675 for (var k in obj) {
4676 document.querySelector('body').innerHTML += k + '<br>';
4677 }
4678};
4679
4680window.loadStuffFromTheOneLibrary = function (assignment) {
4681 //Вытаскиваем настройки шаблона из объекта
4682 for (var s in window.theOneSettings) {
4683 window[s] = window.theOneSettings[s];
4684 } //Обогащаем таск и тасксьют методами из библиотеки и накидываем поверх кастомных методов, ежели таковые имеются
4685
4686
4687 var taskSuite = assignment.getTaskSuite();
4688 injectMethods(theOneAssignmentMethods, assignment, window.customAssignmentMethods);
4689 injectMethods(theOneTaskSuitePrototype, taskSuite, window.customTaskSuiteMethods);
4690
4691 var _iterator51 = _createForOfIteratorHelper(taskSuite.getTasks()),
4692 _step51;
4693
4694 try {
4695 for (_iterator51.s(); !(_step51 = _iterator51.n()).done;) {
4696 var task = _step51.value;
4697 injectMethods(theOneTaskPrototype, task, window.customTaskMethods);
4698 }
4699 } catch (err) {
4700 _iterator51.e(err);
4701 } finally {
4702 _iterator51.f();
4703 }
4704
4705 if (!window.helpersRegisteredAndCssApplied) {
4706 //Регистрируем библиотечные хелперы Handlebars
4707 for (var name in theOneHandlebarsHelpers) {
4708 Handlebars.registerHelper(name, theOneHandlebarsHelpers[name]);
4709 } //Вставляем CSS
4710
4711
4712 applyTheOneCSS();
4713 window.helpersRegisteredAndCssApplied = true;
4714 } //То, что раньше было в onResume()
4715
4716
4717 window.lang = assignment.getWorkspaceOptions().language || 'ru'; //Инициализация жалобщика
4718
4719 if (useComplainter) {
4720 $.getScript(complainterLibSrc, function (data, textStatus, jqxhr) {
4721 var options = assignment.getWorkspaceOptions();
4722 var namespace = complainterNamespace;
4723 window.Complaints.default.initialize({
4724 api: {
4725 url: '',
4726 origin: options.origin,
4727 proxyName: complainterProxyName,
4728 assignmentId: assignment.getId()
4729 },
4730 language: options.language,
4731 namespace: namespace,
4732 ajax: function ajax(ajaxOptions) {
4733 !ajaxOptions.type && (ajaxOptions.type = ajaxOptions.method);
4734 return assignment.getSandboxChannel().request('proxy', ajaxOptions);
4735 },
4736 custom_causes: complainterCauses,
4737 custom_translations: complainterTranslations
4738 });
4739 });
4740 } //Пытаемся выпытать у браузера сохранённую солюшку, если она была
4741
4742
4743 assignment.getTaskSuite().getSolutionsFromStorage();
4744};
4745
4746window.injectMethods = function (from, to, customMethods) {
4747 for (var m in from) {
4748 to[m] = from[m];
4749 }
4750
4751 if (customMethods) {
4752 for (var _m in customMethods) {
4753 if (to[_m]) {
4754 to['_' + _m] = to[_m];
4755 }
4756
4757 to[_m] = customMethods[_m];
4758 }
4759 }
4760};
4761
4762window.applyTheOneCSS = function () {
4763 var style = document.createElement('style');
4764 style.setAttribute('type', 'text/css');
4765 style.innerHTML = theOneCSS;
4766 document.querySelector('head').appendChild(style);
4767};
4768
4769window.initHelpContainer = function (q) {
4770 var popUpTitle = q.querySelector('.popUpTitle');
4771 popUpTitle.innerHTML = "<div class=\"popUpTitleInner\">".concat(popUpTitle.innerHTML, "</div>");
4772 q.addEventListener('click', function () {
4773 if (this.classList.contains('triggered')) {
4774 this.classList.remove('triggered');
4775 } else {
4776 this.classList.add('triggered');
4777 makeSureThisDivFitsOnScreen(q.querySelector('.popUpTitle'));
4778 }
4779 });
4780 q.classList.add('initialized');
4781}; //Проверяет, помещается ли указанный контейнер на экране, и если нет - сдвигает его и масштабирует
4782
4783
4784window.makeSureThisDivFitsOnScreen = function (div) {
4785 div.setAttribute('style', '');
4786 var style = getComputedStyle(div),
4787 margin = 20,
4788 //Ровно на столько пикселей от края экрана будут отодвигаться подсказки
4789 task = fullScreenMode ? div.closest('.block.main') || div.closest('.windowContent') || div.closest('.task') : div.closest('.windowContent') || div.closest('.task');
4790 var verticalProperty,
4791 n = 0;
4792
4793 do {
4794 if (n++ > 20) {
4795 break;
4796 }
4797
4798 var divRect = div.getBoundingClientRect(),
4799 taskRect = task.getBoundingClientRect(),
4800 divEnd = void 0; //console.log(divRect);
4801 //console.log(taskRect);
4802 //Ширина блока должна быть меньше ширины таска
4803
4804 if (divRect.width > taskRect.width - 2 * margin) {
4805 div.style.maxWidth = taskRect.width - 2 * margin + "px";
4806 divRect = div.getBoundingClientRect();
4807 taskRect = task.getBoundingClientRect();
4808 } //Высота блока должна быть меньше высоты таска
4809
4810
4811 if (divRect.height > taskRect.height - 2 * margin) {
4812 div.style.maxHeight = taskRect.height - 2 * margin + "px";
4813 divRect = div.getBoundingClientRect();
4814 taskRect = task.getBoundingClientRect();
4815 } //Выход за правую границу
4816
4817
4818 divEnd = divRect.right + margin;
4819
4820 if (divEnd > taskRect.right - 20) {
4821 div.style.left = "-" + (divEnd - taskRect.right).toString() + "px";
4822 divRect = div.getBoundingClientRect();
4823 taskRect = task.getBoundingClientRect();
4824 } //Выход за нижнюю границу
4825
4826
4827 divEnd = divRect.bottom + margin;
4828
4829 if (!div.classList.contains('errorMsgBlock') && divEnd > taskRect.bottom + 10) {
4830 div.style.top = "-" + (divEnd - taskRect.bottom - parseInt(style.top)).toString() + "px";
4831 divRect = div.getBoundingClientRect();
4832 taskRect = task.getBoundingClientRect();
4833 } //Выход за левую границу
4834
4835
4836 divEnd = divRect.left - margin;
4837
4838 if (divEnd < -10) {
4839 div.style.left = margin + "px";
4840 divRect = div.getBoundingClientRect();
4841 taskRect = task.getBoundingClientRect();
4842 } //Выход за верхнюю границу
4843
4844
4845 divEnd = divRect.top - margin;
4846
4847 if (!div.classList.contains('errorMsgBlock') && divEnd < taskRect.top - 10) {
4848 if (div.classList.contains('errorMsgBlock')) {
4849 div.style.maxHeight = divRect.height + divEnd - taskRect.top + "px";
4850 } else {
4851 div.style.top = "-" + (divEnd - taskRect.top - parseInt(style.top)).toString() + "px";
4852 }
4853
4854 divRect = div.getBoundingClientRect();
4855 taskRect = task.getBoundingClientRect();
4856 }
4857 } while (n < 3);
4858};
4859
4860window.initImageField = function (imageField) {
4861 /*
4862 Тут 2 канваса, внешний и внутренний.
4863 img - внутренний канвас с картинкой pic
4864 canvas - внешний канвас.
4865 */
4866 var imageSource = imageField.querySelector('.imageSource'),
4867 imageOrigLink = imageField.querySelector('.imageOriginalLink'),
4868 imageBlock = imageField.querySelector('.imageBlock'),
4869 imageConsole = imageField.querySelector('.imageConsole'),
4870 noCanvasMode = imageField.classList.contains('noCanvas'),
4871 imgRatio,
4872 canvasRatio,
4873 imgD,
4874 leftButtsJSON;
4875 var leftButts = imageField.querySelector('.imageLeftButts'),
4876 rightButts = imageField.querySelector('.imageRightButts');
4877
4878 if (!imageSource) {
4879 ce('imageField_no_imageSource_div', imageField.className);
4880 return;
4881 }
4882
4883 imageSource = imageSource.innerHTML;
4884
4885 if (!imageSource) {
4886 ce('imageField_no_imageSource', imageField.className);
4887 return;
4888 }
4889
4890 if (imageOrigLink) {
4891 imageOrigLink = imageOrigLink.innerHTML;
4892 }
4893
4894 if (noCanvasMode) {
4895 var img = document.createElement('img');
4896 img.className = 'justAnImage';
4897 img.setAttribute('src', imageSource);
4898 imageBlock.appendChild(img);
4899 leftButtsJSON = {
4900 type: "actionButtonGroup",
4901 class: "imageFieldRightControls",
4902 flags: ["unfocusable"],
4903 buttons: [{
4904 iconClass: "expandButtIcon",
4905 hint: "Развернуть на весь экран.\nДвойной клик по изображению делает то же самое."
4906 }]
4907 };
4908 leftButts.innerHTML = handleFieldAndGetHTML(leftButtsJSON);
4909 var btnA;
4910 btnA = leftButts.querySelector('.expandButtIcon a');
4911
4912 if (btnA) {
4913 var expandStuff = function expandStuff(e) {
4914 e.preventDefault();
4915 imageField.classList.toggle('expanded');
4916
4917 var btn = _btnA.closest('.btn');
4918
4919 if (imageField.classList.contains('expanded')) {
4920 btn.classList.add('selected');
4921 } else {
4922 btn.classList.remove('selected');
4923 }
4924 };
4925
4926 var _btnA = btnA;
4927 btnA.addEventListener('click', expandStuff);
4928 imageBlock.addEventListener('dblclick', expandStuff);
4929 }
4930 } else {
4931 var initCanvas = function initCanvas() {
4932 ctx.canvas.width = canvas.offsetWidth;
4933 ctx.canvas.height = canvas.offsetHeight;
4934 ctx.canvas.halfWidth = canvas.offsetWidth / 2;
4935 ctx.canvas.halfHeight = canvas.offsetHeight / 2;
4936 canvas.scale =
4937 /* canvas.scale ? canvas.scale :*/
4938 Math.min(canvas.width / _img.width, canvas.height / _img.height);
4939 canvas.minScale =
4940 /*canvas.minScale ? canvas.minScale : */
4941 canvas.scale;
4942 _img.rotation = _img.rotation ? _img.rotation : 0;
4943 _img.left = _img.width / -2;
4944 _img.top = _img.height / -2;
4945 onScaleChange();
4946 ctx.setTransform(canvas.scale, 0, 0, canvas.scale, canvas.halfWidth, canvas.halfHeight);
4947 ctx.drawImage(_img, _img.left, _img.top);
4948 imgRatio = _img.width / _img.height;
4949 canvasRatio = canvas.width / canvas.height;
4950
4951 if (imgRatio < canvasRatio) {
4952 imgD = 1;
4953 } else {
4954 imgD = -1;
4955 }
4956
4957 if (imgRatio < 1) {
4958 imgD *= -1;
4959 }
4960 };
4961
4962 var disableButts = function disableButts() {
4963 Array.from(leftButts.querySelectorAll('.btn.selected:not(.adjustSolidButtIcon):not(.expandButtIcon)')).forEach(function (btn) {
4964 return btn.classList.remove('selected');
4965 });
4966 ['freeRotationMode', 'freeHueMode', 'freeContrastMode', 'freeBrightnessMode', 'freeSaturationMode'].forEach(function (name) {
4967 return _img[name] = false;
4968 });
4969 };
4970
4971 var clrscr = function clrscr() {
4972 ctx.clearRect(canvas.left, canvas.top, canvas.scaledWidth, canvas.scaledHeight);
4973 };
4974
4975 var imgClrscr = function imgClrscr() {
4976 imgCtx.clearRect(-_img.halfWidth - 10, -_img.halfHeight - 10, _img.width + 20, _img.height + 20);
4977 };
4978
4979 var imgLog = function imgLog(text) {
4980 imageConsole.style.opacity = "0.8";
4981 imageConsole.innerHTML = text;
4982 clearTimeout(imageConsole.timeout);
4983 imageConsole.timeout = setTimeout(function () {
4984 imageConsole.style.opacity = "0";
4985 }, 2000);
4986 };
4987
4988 var applyFilters = function applyFilters(f) {
4989 if (!f) {
4990 f = _img.filters;
4991 }
4992
4993 imgCtx.filter = "invert(".concat(f.invert, "%) hue-rotate(").concat(f.hue, "deg) contrast(").concat(f.contrast, "%) brightness(").concat(f.brightness, "%) saturate(").concat(f.saturation, "%)");
4994 imgCtx.drawImage(pic, pic.left, pic.top);
4995 ctx.drawImage(_img, _img.left, _img.top);
4996 };
4997
4998 var rotatePic = function rotatePic() {
4999 clrscr();
5000 imgClrscr();
5001 var angle = _img.rotation * 180 % 180;
5002 angle = angle > 90 ? 90 - (angle - 90) : angle;
5003 var alpha = Math.atan2(_img.height, _img.width); // Угол диагонали
5004
5005 var alpha_1 = alpha + angle * Math.PI / 180; // Новый угол первой диагонали
5006
5007 var alpha_2 = alpha_1 + Math.PI - 2 * alpha; // Новый угол второй диагонали
5008
5009 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
5010
5011 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
5012
5013 _img.scale = 1 / Math.max(W_new / _img.width, H_new / _img.height);
5014
5015 if (canvas.scale > canvas.minScale || imgD < 0) {
5016 canvas.scale = canvas.savedScale / _img.scale;
5017 }
5018
5019 imgCtx.setTransform(_img.scale, 0, 0, _img.scale, _img.halfWidth, _img.halfHeight);
5020 imgCtx.rotate(_img.rotation * Math.PI);
5021 imgCtx.drawImage(pic, pic.left, pic.top);
5022 onScaleChange();
5023 positionCorrector();
5024 ctx.setTransform(canvas.scale, 0, 0, canvas.scale, canvas.halfWidth, canvas.halfHeight);
5025 clrscr();
5026 ctx.drawImage(_img, _img.left, _img.top);
5027 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"));
5028 };
5029
5030 var imgDrag = function imgDrag(e) {
5031 canvas.midnightMover = true;
5032 imageField.classList.add('dragging');
5033 };
5034
5035 var imgMove = function imgMove(e) {
5036 if (!canvas.midnightMover) {
5037 return;
5038 }
5039
5040 _img.left += e.movementX / canvas.scale;
5041 _img.top += e.movementY / canvas.scale;
5042 positionCorrector();
5043 clrscr();
5044 ctx.drawImage(_img, _img.left, _img.top);
5045 };
5046
5047 var imgDrop = function imgDrop() {
5048 canvas.midnightMover = false;
5049 imageField.classList.remove('dragging');
5050 };
5051
5052 var onScaleChange = function onScaleChange() {
5053 canvas.left = -canvas.halfWidth / canvas.scale;
5054 canvas.top = -canvas.halfHeight / canvas.scale;
5055 canvas.scaledWidth = canvas.width / canvas.scale;
5056 canvas.scaledHeight = canvas.height / canvas.scale;
5057 canvas.right = -canvas.left;
5058 canvas.bottom = -canvas.top;
5059 onPositionChange();
5060 };
5061
5062 var onPositionChange = function onPositionChange() {
5063 _img.right = _img.left + _img.width;
5064 _img.bottom = _img.top + _img.height;
5065 };
5066
5067 var positionCorrector = function positionCorrector() {
5068 onPositionChange();
5069
5070 if (_img.width > canvas.scaledWidth) {
5071 if (_img.right > canvas.right && _img.left > canvas.left) {
5072 _img.left = canvas.left;
5073 }
5074
5075 if (_img.left < canvas.left && _img.right < canvas.right) {
5076 _img.left = canvas.right - _img.width;
5077 }
5078 } else {
5079 if (_img.left < canvas.left) {
5080 _img.left = canvas.left;
5081 }
5082
5083 if (_img.right > canvas.right) {
5084 _img.left = canvas.right - _img.width;
5085 }
5086 }
5087
5088 if (_img.height > canvas.scaledHeight) {
5089 if (_img.bottom > canvas.bottom && _img.top > canvas.top) {
5090 _img.top = canvas.top;
5091 }
5092
5093 if (_img.top < canvas.top && _img.bottom < canvas.bottom) {
5094 _img.top = canvas.bottom - _img.height;
5095 }
5096 } else {
5097 if (_img.top < canvas.top) {
5098 _img.top = canvas.top;
5099 }
5100
5101 if (_img.bottom > canvas.bottom) {
5102 _img.top = canvas.bottom - _img.height;
5103 }
5104 }
5105
5106 onPositionChange();
5107 clrscr();
5108 ctx.drawImage(_img, _img.left, _img.top);
5109 };
5110
5111 var imgWheel = function imgWheel(e) {
5112 if (!imageField.classList.contains('focused')) {
5113 return;
5114 }
5115
5116 e.preventDefault();
5117
5118 if (_img.freeRotationMode) {
5119 //Свободное вращение
5120 if (e.deltaY > 0) {
5121 _img.rotation < 2 ? _img.rotation += imageViewerRotationStep : _img.rotation = imageViewerRotationStep;
5122 } else {
5123 _img.rotation > 0 ? _img.rotation -= imageViewerRotationStep : _img.rotation = 2 - imageViewerRotationStep;
5124 }
5125
5126 rotatePic();
5127 } else if (_img.freeHueMode) {
5128 //Свободный оттенок
5129 if (e.deltaY < 0) {
5130 _img.filters.hue <= 358 ? _img.filters.hue += 2 : _img.filters.hue = 2;
5131 } else {
5132 _img.filters.hue >= 2 ? _img.filters.hue -= 2 : _img.filters.hue = 358;
5133 }
5134
5135 imgLog("\u041E\u0442\u0442\u0435\u043D\u043E\u043A \u043F\u043E\u0432\u0451\u0440\u043D\u0443\u0442 \u043D\u0430 ".concat(_img.filters.hue, "\xB0"));
5136 applyFilters();
5137 } else if (_img.freeContrastMode) {
5138 //Свободная контрастность
5139 if (e.deltaY < 0) {
5140 _img.filters.contrast <= 199 ? _img.filters.contrast += 1 : _img.filters.contrast = 200;
5141 } else {
5142 _img.filters.contrast >= 1 ? _img.filters.contrast -= 1 : _img.filters.contrast = 0;
5143 }
5144
5145 imgLog("\u041A\u043E\u043D\u0442\u0440\u0430\u0441\u0442\u043D\u043E\u0441\u0442\u044C ".concat(_img.filters.contrast, "%"));
5146 applyFilters();
5147 } else if (_img.freeBrightnessMode) {
5148 //Свободная яркость
5149 if (e.deltaY < 0) {
5150 _img.filters.brightness <= 199 ? _img.filters.brightness += 1 : _img.filters.brightness = 200;
5151 } else {
5152 _img.filters.brightness >= 1 ? _img.filters.brightness -= 1 : _img.filters.brightness = 0;
5153 }
5154
5155 imgLog("\u042F\u0440\u043A\u043E\u0441\u0442\u044C ".concat(_img.filters.brightness, "%"));
5156 applyFilters();
5157 } else if (_img.freeSaturationMode) {
5158 //Свободная насыщенность
5159 if (e.deltaY < 0) {
5160 _img.filters.saturation <= 199 ? _img.filters.saturation += 1 : _img.filters.saturation = 200;
5161 } else {
5162 _img.filters.saturation >= 1 ? _img.filters.saturation -= 1 : _img.filters.saturation = 0;
5163 }
5164
5165 imgLog("\u041D\u0430\u0441\u044B\u0449\u0435\u043D\u043D\u043E\u0441\u0442\u044C ".concat(_img.filters.saturation, "%"));
5166 applyFilters();
5167 } else {
5168 var oldScale = canvas.scale;
5169
5170 if (e.deltaY > 0) {
5171 canvas.scale -= imageViewerScalingStep + canvas.scale / 10;
5172
5173 if (canvas.scale < canvas.minScale) {
5174 canvas.scale = canvas.minScale;
5175 }
5176 } else {
5177 canvas.scale += imageViewerScalingStep + canvas.scale / 10;
5178
5179 if (canvas.scale > 4) {
5180 canvas.scale = 4;
5181 }
5182 }
5183
5184 onScaleChange();
5185 positionCorrector();
5186 ctx.setTransform(canvas.scale, 0, 0, canvas.scale, canvas.halfWidth, canvas.halfHeight);
5187 clrscr();
5188 ctx.drawImage(_img, _img.left, _img.top);
5189 }
5190 };
5191
5192 var canvas = imageBlock.querySelector('canvas');
5193 var ctx = canvas.getContext("2d"); //Внутренний канвас с картинкой
5194
5195 var _img = document.createElement('canvas');
5196
5197 var imgCtx = _img.getContext("2d");
5198
5199 ctx.imageSmoothingEnabled = false;
5200 ctx.imageSmoothingEnabled = false;
5201 var pic = new Image();
5202 pic.src = imageSource;
5203 imgLog('Загружаю изображение...');
5204
5205 pic.onload = function () {
5206 imgLog('Изображение загружено!');
5207 var picMaxD = Math.max(pic.width, pic.height);
5208 var picDiagonal = Math.ceil(Math.sqrt(Math.pow(pic.width, 2) + Math.pow(pic.height, 2)));
5209 _img.width = pic.width;
5210 _img.height = pic.height;
5211 _img.halfWidth = _img.width / 2;
5212 _img.halfHeight = _img.height / 2;
5213 _img.scale = 1;
5214 pic.left = 0 - pic.width / 2;
5215 pic.top = 0 - pic.height / 2;
5216 imgCtx.setTransform(_img.scale, 0, 0, _img.scale, _img.halfWidth, _img.halfHeight);
5217 imgCtx.drawImage(pic, pic.left, pic.top);
5218 initCanvas();
5219 canvas.initCanvas = initCanvas;
5220 window.addEventListener('resize', initCanvas);
5221 var btnA;
5222 btnA = leftButts.querySelector('.expandButtIcon a');
5223
5224 if (btnA) {
5225 var _expandStuff = function _expandStuff(e) {
5226 e.preventDefault();
5227
5228 if (!imageField.classList.contains('expanded')) {}
5229
5230 var btn = _btnA2.closest('.btn');
5231
5232 if (!imageField.classList.contains('expanded')) {
5233 imageBlock.widthBeforeExpanding = getComputedStyle(imageBlock).width;
5234 imageBlock.heightBeforeExpanding = getComputedStyle(imageBlock).height;
5235 imageField.classList.add('expanded');
5236 btn.classList.add('selected');
5237 initCanvas();
5238 } else {
5239 btn.classList.remove('selected');
5240 imageBlock.style.width = imageBlock.widthBeforeExpanding;
5241 imageBlock.style.height = imageBlock.heightBeforeExpanding;
5242 imageField.classList.remove('expanded');
5243 initCanvas();
5244 setTimeout(function () {
5245 imageBlock.style.width = 'initial';
5246 imageBlock.style.height = 'initial';
5247 }, 100);
5248 }
5249 };
5250
5251 var _btnA2 = btnA;
5252 btnA.addEventListener('click', _expandStuff);
5253 imageBlock.addEventListener('dblclick', _expandStuff);
5254 } //Поворот влево на 90 градусов
5255
5256
5257 btnA = leftButts.querySelector('.undoButtIcon a');
5258
5259 if (btnA) {
5260 btnA.addEventListener('click', function (e) {
5261 e.preventDefault();
5262 _img.rotation = Math.ceil(_img.rotation * 2) / 2;
5263 _img.rotation > 0 ? _img.rotation -= 0.5 : _img.rotation = 1.5;
5264 canvas.savedScale = canvas.scale * _img.scale;
5265 rotatePic();
5266 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"));
5267 });
5268 } //Поворот вправо на 90 градусов
5269
5270
5271 btnA = leftButts.querySelector('.redoButtIcon a');
5272
5273 if (btnA) {
5274 btnA.addEventListener('click', function (e) {
5275 e.preventDefault();
5276 _img.rotation = Math.floor(_img.rotation * 2) / 2;
5277 _img.rotation < 2 ? _img.rotation += 0.5 : _img.rotation = 0.5;
5278 canvas.savedScale = canvas.scale * _img.scale;
5279 rotatePic();
5280 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"));
5281 });
5282 } //Кнопка свободного вращения
5283
5284
5285 var lastClickedTime;
5286 btnA = leftButts.querySelector('.syncButtIcon a');
5287
5288 if (btnA) {
5289 btnA.addEventListener('click', function (e) {
5290 e.preventDefault();
5291 var btn = this.closest('.btn');
5292
5293 if (!btn.classList.contains('selected')) {
5294 disableButts();
5295 btn.classList.add('selected');
5296 canvas.savedScale = canvas.scale * _img.scale;
5297 _img.freeRotationMode = true;
5298 } else {
5299 disableButts();
5300 } //Проверка на двойной клик
5301
5302
5303 if (new Date().getTime() <= lastClickedTime + imageViewerDoubleClickTime) {
5304 //Это был двойной клик, значит надо сбросить значение данного свойства
5305 _img.rotation = 0;
5306 rotatePic();
5307 btn.classList.remove('selected');
5308 _img.freeRotationMode = false;
5309 } //Запоминаем время включения для отслеживания двойного клика
5310
5311
5312 lastClickedTime = new Date().getTime();
5313 });
5314 } //Инверсия изображения
5315
5316
5317 btnA = leftButts.querySelector('.adjustSolidButtIcon a');
5318
5319 if (btnA) {
5320 btnA.addEventListener('click', function (e) {
5321 e.preventDefault();
5322 var btn = this.closest('.btn');
5323
5324 if (!btn.classList.contains('selected')) {
5325 btn.classList.add('selected');
5326 _img.filters.invert = 100;
5327 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");
5328 } else {
5329 btn.classList.remove('selected');
5330 _img.filters.invert = 0;
5331 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");
5332 }
5333
5334 applyFilters();
5335 });
5336 } //Оттенок изображения
5337
5338
5339 btnA = leftButts.querySelector('.swatchBookIcon a');
5340
5341 if (btnA) {
5342 btnA.addEventListener('click', function (e) {
5343 e.preventDefault();
5344 var btn = this.closest('.btn');
5345
5346 if (!btn.classList.contains('selected')) {
5347 disableButts();
5348 btn.classList.add('selected');
5349 _img.freeHueMode = true;
5350 } else {
5351 disableButts();
5352 } //Проверка на двойной клик
5353
5354
5355 if (new Date().getTime() <= lastClickedTime + imageViewerDoubleClickTime) {
5356 //Это был двойной клик, значит надо сбросить значение данного свойства
5357 btn.classList.remove('selected');
5358 _img.freeHueMode = false;
5359 _img.filters.hue = 0;
5360 applyFilters();
5361 imgLog("\u041E\u0442\u0442\u0435\u043D\u043E\u043A \u0432\u043E\u0441\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D");
5362 } //Запоминаем время включения для отслеживания двойного клика
5363
5364
5365 lastClickedTime = new Date().getTime();
5366 });
5367 } //Контрастность изображения
5368
5369
5370 btnA = leftButts.querySelector('.adjustButtIcon a');
5371
5372 if (btnA) {
5373 btnA.addEventListener('click', function (e) {
5374 e.preventDefault();
5375 var btn = this.closest('.btn');
5376
5377 if (!btn.classList.contains('selected')) {
5378 disableButts();
5379 btn.classList.add('selected');
5380 _img.freeContrastMode = true;
5381 } else {
5382 disableButts();
5383 } //Проверка на двойной клик
5384
5385
5386 if (new Date().getTime() <= lastClickedTime + imageViewerDoubleClickTime) {
5387 //Это был двойной клик, значит надо сбросить значение данного свойства
5388 btn.classList.remove('selected');
5389 _img.freeContrastMode = false;
5390 _img.filters.contrast = 100;
5391 applyFilters();
5392 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");
5393 } //Запоминаем время включения для отслеживания двойного клика
5394
5395
5396 lastClickedTime = new Date().getTime();
5397 });
5398 } //Яркость изображения
5399
5400
5401 btnA = leftButts.querySelector('.sunButtIcon a');
5402
5403 if (btnA) {
5404 btnA.addEventListener('click', function (e) {
5405 e.preventDefault();
5406 var btn = this.closest('.btn');
5407
5408 if (!btn.classList.contains('selected')) {
5409 disableButts();
5410 btn.classList.add('selected');
5411 _img.freeBrightnessMode = true;
5412 } else {
5413 disableButts();
5414 } //Проверка на двойной клик
5415
5416
5417 if (new Date().getTime() <= lastClickedTime + imageViewerDoubleClickTime) {
5418 //Это был двойной клик, значит надо сбросить значение данного свойства
5419 btn.classList.remove('selected');
5420 _img.freeBrightnessMode = false;
5421 _img.filters.brightness = 100;
5422 applyFilters();
5423 imgLog("\u042F\u0440\u043A\u043E\u0441\u0442\u044C \u0432\u043E\u0441\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D\u0430");
5424 } //Запоминаем время включения для отслеживания двойного клика
5425
5426
5427 lastClickedTime = new Date().getTime();
5428 });
5429 } //Насыщенность изображения
5430
5431
5432 btnA = leftButts.querySelector('.paletteButtIcon a');
5433
5434 if (btnA) {
5435 btnA.addEventListener('click', function (e) {
5436 e.preventDefault();
5437 var btn = this.closest('.btn');
5438
5439 if (!btn.classList.contains('selected')) {
5440 disableButts();
5441 btn.classList.add('selected');
5442 _img.freeSaturationMode = true;
5443 } else {
5444 disableButts();
5445 } //Проверка на двойной клик
5446
5447
5448 if (new Date().getTime() <= lastClickedTime + imageViewerDoubleClickTime) {
5449 //Это был двойной клик, значит надо сбросить значение данного свойства
5450 btn.classList.remove('selected');
5451 _img.freeSaturationMode = false;
5452 _img.filters.saturation = 100;
5453 applyFilters();
5454 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");
5455 } //Запоминаем время включения для отслеживания двойного клика
5456
5457
5458 lastClickedTime = new Date().getTime();
5459 });
5460 } //Стирание фильтров
5461
5462
5463 btnA = leftButts.querySelector('.eraserButtIcon a');
5464
5465 if (btnA) {
5466 btnA.addEventListener('click', function (e) {
5467 e.preventDefault();
5468 disableButts();
5469 _img.filters = {
5470 invert: 0,
5471 hue: 0,
5472 contrast: 100,
5473 brightness: 100,
5474 saturation: 100
5475 };
5476 applyFilters();
5477 imgLog("\u0412\u0441\u0435 \u0444\u0438\u043B\u044C\u0442\u0440\u044B \u0441\u0442\u0451\u0440\u0442\u044B");
5478 });
5479 }
5480 };
5481
5482 canvas.addEventListener('mousedown', imgDrag);
5483 canvas.addEventListener('mousemove', imgMove);
5484 canvas.addEventListener('mouseup', imgDrop);
5485 canvas.addEventListener('mouseleave', imgDrop);
5486 canvas.addEventListener('wheel', imgWheel);
5487 _img.filters = {
5488 invert: 0,
5489 hue: 0,
5490 contrast: 100,
5491 brightness: 100,
5492 saturation: 100
5493 };
5494 leftButtsJSON = {
5495 type: "actionButtonGroup",
5496 class: "imageFieldRightControls",
5497 flags: ["unfocusable"],
5498 buttons: [{
5499 iconClass: "expandButtIcon",
5500 hint: "Развернуть на весь экран.\nДвойной клик по изображению делает то же самое."
5501 }, {
5502 iconClass: "undoButtIcon",
5503 hint: "Повернуть изображение против часовой стрелки."
5504 }, {
5505 iconClass: "redoButtIcon",
5506 hint: "Повернуть изображение по часовой стрелке."
5507 }, {
5508 iconClass: "syncButtIcon",
5509 hint: "Режим свободного вращения колёсиком.\nДвойная активация сбрасывает поворот."
5510 }]
5511 };
5512
5513 if (!imageField.classList.contains('noFilters')) {
5514 leftButtsJSON.buttons.push({
5515 iconClass: "adjustSolidButtIcon",
5516 hint: "Инвертировать изображение"
5517 }, {
5518 iconClass: "swatchBookIcon",
5519 hint: "Оттенок изображения.\nДвойная активация сбрасывает фильтр."
5520 }, {
5521 iconClass: "adjustButtIcon",
5522 hint: "Контрастность изображения.\nДвойная активация сбрасывает фильтр."
5523 }, {
5524 iconClass: "sunButtIcon",
5525 hint: "Яркость изображения.\nДвойная активация сбрасывает фильтр."
5526 }, {
5527 iconClass: "paletteButtIcon",
5528 hint: "Насыщенность изображения.\nДвойная активация сбрасывает фильтр."
5529 }, {
5530 iconClass: "eraserButtIcon",
5531 hint: "Стереть все фильтры."
5532 });
5533 }
5534
5535 leftButts.innerHTML = handleFieldAndGetHTML(leftButtsJSON);
5536 }
5537
5538 var rightButtsJSON = {
5539 type: "linkButtonGroup",
5540 class: "imageFieldRightControls",
5541 flags: ["unfocusable"],
5542 buttons: [{
5543 link: imageOrigLink ? imageOrigLink : imageSource,
5544 hint: "Открыть изображение в новом окне.",
5545 caption: "Просто ссылка"
5546 }]
5547 };
5548
5549 if (!imageField.classList.contains('noSearchButtons')) {
5550 rightButtsJSON.buttons.push({
5551 type: "yandexSearchByPic",
5552 hint: "Искать похожие картинки в Яндексе.",
5553 query: imageOrigLink ? imageOrigLink : imageSource
5554 });
5555 rightButtsJSON.buttons.push({
5556 type: "googleSearchByPic",
5557 hint: "Искать похожие картинки в другом поисковике.",
5558 query: imageOrigLink ? imageOrigLink : imageSource
5559 });
5560 }
5561
5562 rightButts.innerHTML = handleFieldAndGetHTML(rightButtsJSON);
5563};
5564
5565window.isInteger = function (s) {
5566 return /^\d+$/.test(s);
5567};
5568
5569window.isFloat = function (s) {
5570 return /^\d+(\.\d+)?$/.test(s);
5571};
5572
5573window.isEmail = function (s) {
5574 return /^([a-z\u00a1-\uffff0-9_\-.])+@([a-z\u00a1-\uffff0-9_\-.])+\.([a-z\u00a1-\uffff]{2,6})$/i.test(s);
5575};
5576
5577window.isIP = function (s) {
5578 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);
5579};
5580
5581window.isTelNumber = function (n) {
5582 return /^(\s*)?(\+)?([- _():=+]?\d[- _():=+]?){10,14}(\s*)?$/.test(n);
5583};
5584
5585window.isLink = function (zelda) {
5586 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://';
5587};
5588
5589window.isServiceLink = function (zelda, hyrule) {
5590 switch (hyrule) {
5591 case 'vkontakte':
5592 return /^http(s)?:\/\/(www\.)?vk\.com\/.*$/.test(zelda);
5593
5594 case 'youtube':
5595 return /^http(s)?:\/\/(www\.)?youtu(be)?\.(com)|(be)\/.*$/.test(zelda);
5596
5597 case 'yadisk':
5598 return /^http(s)?:\/\/(www\.)?yadi\.sk\/.*$/.test(zelda);
5599
5600 case 'telegram':
5601 return /^http(s)?:\/\/(www\.)?t\.me\/.*$/.test(zelda);
5602
5603 case 'instagram':
5604 return /^http(s)?:\/\/(www\.)?instagram\.com\/.*$/.test(zelda);
5605
5606 case 'facebook':
5607 return /^http(s)?:\/\/(www\.)?facebook\.com\/.*$/.test(zelda);
5608
5609 case 'wikipedia':
5610 return /^http(s)?:\/\/(www\.)?((ru\.)|(en\.)|(by\.)|(simple\.))?wikipedia\.org\/.*$/.test(zelda);
5611
5612 case 'kinopoisk':
5613 return /^http(s)?:\/\/(www\.)?kinopoisk\.ru\/.*$/.test(zelda);
5614
5615 case 'yamusic':
5616 return /^https:\/\/music.yandex.ru\/album\/\d+\/track\/\d+/.test(zelda);
5617 }
5618};
5619
5620window.isYaDiskLink = function (zelda) {
5621 return zelda.substr(0, 15) === 'http://yadi.sk/' || zelda.substr(0, 16) === 'https://yadi.sk/';
5622};
5623
5624window.isDate = function (text, format) {
5625 switch (format) {
5626 case 'YYYY-MM-DD':
5627 return /^([012]\d{3})-(0[1-9]|1[012])-(0[1-9]|[12]\d|3[01])$/.test(text);
5628
5629 case 'DD-MM-YYYY':
5630 return /^(0[1-9]|[12]\d|3[01])-(0[1-9]|1[012])-([012]\d{3})$/.test(text);
5631
5632 case 'MM-DD-YYYY':
5633 return /^(0[1-9]|1[012])-(0[1-9]|[12]\d|3[01])-([012]\d{3})$/.test(text);
5634
5635 case 'YYYY.MM.DD':
5636 return /^([012]\d{3})\.(0[1-9]|1[012])\.(0[1-9]|[12]\d|3[01])$/.test(text);
5637
5638 case 'DD.MM.YYYY':
5639 return /^(0[1-9]|[12]\d|3[01])\.(0[1-9]|1[012])\.([012]\d{3})$/.test(text);
5640
5641 case 'MM.DD.YYYY':
5642 return /^(0[1-9]|1[012])\.(0[1-9]|[12]\d|3[01])\.([012]\d{3})$/.test(text);
5643 }
5644
5645 return false;
5646};
5647
5648window.skipProtocolParametersAndOtherStuff = function (s) {
5649 if (!s) {
5650 return s;
5651 }
5652
5653 return s.replace(/^(?:https?:\/\/)?(?:www\.)?/i, "").replace(/(?:\?.*)?(?:#.*)?$/i, "").replace(/^(youtube\.)(.*)(\/featured|\/about|\/channels|\/community|\/playlists|\/videos)$/i, '$1$2');
5654};
5655
5656window.isOptional = function (props) {
5657 //Проверяет, есть ли у поля флаг опциональности (для необязательных полей)
5658 return !!(props && props.flags && props.flags.includes('optional'));
5659};
5660
5661window.isActive = function (btn) {
5662 return btn.classList.contains('active') || btn.dataset.type === 'linkButton';
5663};
5664
5665window.isGroupInfinite = function (groupDiv) {
5666 return groupDiv.classList.contains('infinite');
5667};
5668
5669window.activateButton = function (btn) {
5670 btn.classList.add('active');
5671};
5672
5673window.deactivateButton = function (btn) {
5674 btn.classList.remove('active');
5675};
5676
5677window.invertButton = function (btn) {
5678 btn.classList.remove('clear');
5679
5680 if (isActive(btn)) {
5681 deactivateButton(btn);
5682 } else {
5683 activateButton(btn);
5684 }
5685};
5686
5687window.date2Unix = function (date, format) {
5688 var dateArr = date.split('-');
5689
5690 switch (format) {
5691 case 'YYYY-MM-DD':
5692 return new Date(dateArr[0] + '-' + dateArr[1] + '-' + dateArr[2]).getTime();
5693
5694 case 'DD-MM-YYYY':
5695 return new Date(dateArr[2] + '-' + dateArr[1] + '-' + dateArr[0]).getTime();
5696
5697 case 'MM-DD-YYYY':
5698 return new Date(dateArr[1] + '-' + dateArr[2] + '-' + dateArr[0]).getTime();
5699
5700 case 'YYYY.MM.DD':
5701 return new Date(dateArr[0] + '.' + dateArr[1] + '.' + dateArr[2]).getTime();
5702
5703 case 'DD.MM.YYYY':
5704 return new Date(dateArr[2] + '.' + dateArr[1] + '.' + dateArr[0]).getTime();
5705
5706 case 'MM.DD.YYYY':
5707 return new Date(dateArr[1] + '.' + dateArr[2] + '.' + dateArr[0]).getTime();
5708 }
5709};
5710
5711window.isFirstDateAheadOfSecond = function (date1, format1, date2, format2) {
5712 return date2Unix(date1, format1) > date2Unix(date2, format2);
5713};
5714
5715window.ce = function (error, additional) {
5716 //Выводит в консоль ошибку с определённым текстом
5717 console.error((templateErrorsJSON[error] ? templateErrorsJSON[error] : error) + (additional ? ' (' + additional + ')' : ''));
5718};
5719
5720window.cl = function () {
5721 console.log.apply(_this8, arguments);
5722};
5723
5724window.escapeHTML = function (text) {
5725 var map = {
5726 '&': '&',
5727 '<': '<',
5728 '>': '>',
5729 '"': '"',
5730 "'": '''
5731 };
5732 return text.replace(/[&<>"']/g, function (m) {
5733 return map[m];
5734 });
5735};
5736
5737window.localize = function (slug, noConsole) {
5738 if (!window.l10n) {
5739 if (!noConsole) {
5740 console.error('[Вы пытаетесть что-то перевести функцией localize, но window.l10n у вас не определён (например, вызовом Handlebars-хелпера setLocalization).]');
5741 }
5742
5743 return;
5744 }
5745
5746 if (!window.l10n[slug]) {
5747 if (!noConsole) {
5748 console.error('[В window.l10n нет строк локализации для "' + slug + '"!]');
5749 }
5750
5751 return;
5752 }
5753
5754 var localizedString = window.l10n[slug][window.lang];
5755
5756 if (!localizedString && localizedString !== '') {
5757 localizedString = window.l10n[slug].ru;
5758 }
5759
5760 if (!localizedString && localizedString !== '') {
5761 localizedString = window.l10n[slug].en;
5762 }
5763
5764 return localizedString;
5765};
5766
5767window.isDescendant = function (parent, child) {
5768 if (!parent || !child) {
5769 return false;
5770 }
5771
5772 var node = child.parentNode;
5773
5774 while (node != null) {
5775 if (node === parent) {
5776 return true;
5777 }
5778
5779 node = node.parentNode;
5780 }
5781
5782 return false;
5783};
5784
5785window.checkFilesExtensions = function (field, extensions) {
5786 var soWhat = true;
5787 Array.from(field.querySelectorAll('.file__name')).forEach(function (name) {
5788 var result = false;
5789 Array.from(extensions).forEach(function (ext) {
5790 if (new RegExp('\.' + ext + '$', 'i').test(name.innerHTML)) {
5791 result = true;
5792 }
5793 });
5794
5795 if (!result) {
5796 soWhat = false;
5797 name.classList.add('red');
5798 }
5799 });
5800 return soWhat;
5801};
5802
5803window.getInputFieldHTML = function (field, propsIndex) {
5804 var title = '';
5805
5806 if (field.hint) {
5807 title += '<div class="buttPath">Подсказка:</div>' + field.hint + '\n';
5808 }
5809
5810 if (field.properties && field.properties.examples) {
5811 title += '<div class="buttPath">Примеры:</div>' + field.properties.examples + '\n';
5812 } //Подсказка
5813
5814
5815 var help = '';
5816
5817 if (field.titleHelp) {
5818 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>");
5819 }
5820
5821 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>';
5822};
5823
5824window.getTextareaFieldHTML = function (field, propsIndex) {
5825 var title = '';
5826
5827 if (field.hint) {
5828 title += '<div class="buttPath">Подсказка:</div>' + field.hint + '\n';
5829 }
5830
5831 if (field.properties && field.properties.examples) {
5832 title += '<div class="buttPath">Примеры:</div>' + field.properties.examples + '\n';
5833 } //Подсказка
5834
5835
5836 var help = '';
5837
5838 if (field.titleHelp) {
5839 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>");
5840 }
5841
5842 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>';
5843};
5844
5845window.getSwitchButtonHTML = function (field, propsIndex) {
5846 var includesString = ' includes';
5847
5848 if (propsIndex && fieldsPropsArray[propsIndex].includes) {
5849 Array.from(fieldsPropsArray[propsIndex].includes).forEach(function (name) {
5850 includesString += ' ' + name;
5851 });
5852 } else {
5853 includesString = '';
5854 }
5855
5856 if (field.flags && field.flags.includes('selected')) {
5857 includesString += ' toBeClickedAtStart';
5858 }
5859
5860 var title = '';
5861
5862 if (field.hint) {
5863 title += '<div class="popUpTitle"><div class="buttPath">Подсказка:</div>' + field.hint + '\n</div>';
5864 includesString += ' hasHint';
5865 }
5866
5867 var additional = '';
5868
5869 if (field.help) {
5870 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>';
5871 includesString += ' hasHelp';
5872 }
5873
5874 if (!field.caption) {
5875 field.caption = field.name;
5876 }
5877
5878 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>';
5879};
5880
5881window.getLinkButtonHTML = function (field, propsIndex) {
5882 field.type = field.service;
5883
5884 if (!field.caption) {
5885 field.caption = '';
5886 }
5887
5888 var includesString = 'includes';
5889
5890 if (propsIndex && fieldsPropsArray[propsIndex].includes) {
5891 Array.from(fieldsPropsArray[propsIndex].includes).forEach(function (name) {
5892 includesString += ' ' + name;
5893 });
5894 } else {
5895 includesString = '';
5896 }
5897
5898 if (field.flags && field.flags.includes('selected')) {
5899 includesString += ' toBeClickedAtStart';
5900 }
5901
5902 if (field.flags && field.flags.includes('mustBeClicked')) {
5903 includesString += ' mustBeClicked';
5904 }
5905
5906 var title = '';
5907
5908 if (field.hint) {
5909 title += '<div class="buttPath">Подсказка:</div>' + field.hint + '\n';
5910 }
5911
5912 if (field.properties && field.properties.examples) {
5913 title += '<div class="buttPath">Примеры:</div>' + field.properties.examples + '\n';
5914 }
5915
5916 var href, iconClass, caption;
5917
5918 if (!field.type) {
5919 href = field.link;
5920 iconClass = field.iconClass ? field.iconClass : 'defaultButtIcon';
5921 caption = field.caption;
5922 } else if (linkButtonTypes[field.type]) {
5923 href = field.link ? field.link : linkButtonTypes[field.type].link;
5924 iconClass = field.iconClass ? field.iconClass : linkButtonTypes[field.type].iconClass;
5925 caption = field.caption ? field.caption : linkButtonTypes[field.type].caption;
5926
5927 if (field.query) {
5928 href += encodeURIComponent(field.query);
5929 }
5930
5931 if (linkButtonTypes[field.type].linkAfter) {
5932 href += linkButtonTypes[field.type].linkAfter;
5933 } else if (field.linkAfter) {
5934 href += field.linkAfter;
5935 }
5936 } else {
5937 ce('wrong_linkButtonGroup_button_type', 'type:' + field.type + ', caption:' + field.caption);
5938 }
5939
5940 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>';
5941};
5942
5943window.getImageViewerHTML = function (field, propsIndex) {
5944 //Подсказка
5945 var help = '';
5946
5947 if (field.titleHelp) {
5948 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>");
5949 }
5950
5951 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>';
5952 return output;
5953};
5954
5955window.getFileFieldHTML = function (field, propsIndex) {
5956 //Подсказка
5957 var help = '';
5958
5959 if (field.titleHelp) {
5960 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>");
5961 }
5962
5963 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>");
5964 return output;
5965};
5966
5967window.flagsToClassName = function (flags) {
5968 if (!flags || !Array.isArray(flags)) {
5969 return '';
5970 }
5971
5972 function maybeRename(flag) {
5973 switch (flag) {
5974 case 'block':
5975 return '';
5976
5977 case 'oldSchool':
5978 return 'asButtons';
5979
5980 case 'focused':
5981 return 'focusMeOnStart';
5982
5983 default:
5984 return flag;
5985 }
5986 }
5987
5988 var s = ' ';
5989
5990 var _iterator52 = _createForOfIteratorHelper(flags),
5991 _step52;
5992
5993 try {
5994 for (_iterator52.s(); !(_step52 = _iterator52.n()).done;) {
5995 var flag = _step52.value;
5996 s += maybeRename(flag) + ' ';
5997 }
5998 } catch (err) {
5999 _iterator52.e(err);
6000 } finally {
6001 _iterator52.f();
6002 }
6003
6004 return s;
6005}; //Функция получает на вход поле из входной спеки, обрабатывает его и возвращает его HTML код
6006
6007
6008window.handleFieldAndGetHTML = function (field, handlebarsContext) {
6009 //Если у поля не прописаны обязательные поля - забиваем
6010 if (!field.type) {
6011 return;
6012 }
6013
6014 if (!field.title) {
6015 field.title = '';
6016 }
6017
6018 if (!handlebarsContext) {
6019 if (_this8 && _this8.getTask) {
6020 handlebarsContext = _this8.getTask().input_values;
6021 } else {
6022 handlebarsContext = window.handlebarsContext;
6023 }
6024 } //Проверка полей на операторы, плейсхолдеры и т.д.
6025
6026
6027 for (var p in field) {
6028 if (typeof field[p] != "string") {
6029 continue;
6030 }
6031
6032 var split = field[p].split('|||');
6033
6034 if (split[1]) {
6035 //Похоже, что поле с оператором
6036 switch (split[0]) {
6037 case 'INPUT':
6038 //В сплит3 вместо ▼ надо будет подставить значение входного поля сплит2
6039 var input = window.currentRenderingTask.getTask().input_values;
6040
6041 if (!split[1] || !split[2]) {
6042 ce('output_placeholder_input_insufficient_parameters', 'type:' + field.type + ', title:' + field.title + ', caption:' + field.caption);
6043 break;
6044 }
6045
6046 if (!input[split[1]] || typeof input[split[1]] === "string" && input[split[1]].length === 0) {
6047 if (field.default && p !== 'default') {
6048 field[p] = field.default;
6049 } else {
6050 return '';
6051 }
6052 } else {
6053 field[p] = split[2].replace('▼', input[split[1]]).replace('▼', input[split[1]]);
6054 }
6055
6056 break;
6057
6058 case 'somethingOther':
6059 break;
6060
6061 default:
6062 ce('output_placeholder_not_found', 'type:' + field.type + ', title:' + field.title + ', caption:' + field.caption);
6063 break;
6064 }
6065 }
6066 } //Локализация [v1.18+]
6067
6068
6069 for (var _p in field) {
6070 if (_typeof(field[_p]) == 'object' && (field[_p].ru || field[_p].en)) {
6071 field[_p] = field[_p][window.lang];
6072 }
6073 } //Компилируем Handlebars в свойствах поля
6074
6075
6076 function compileStringsHere(where) {
6077 for (var _p2 in where) {
6078 if (!where[_p2]) {//continue;
6079 } else if (typeof where[_p2] === 'string') {
6080 where[_p2] = Handlebars.compile(where[_p2])(handlebarsContext);
6081 } else if (_typeof(where[_p2]) === 'object') {
6082 compileStringsHere(where[_p2]);
6083 }
6084 /*if (Array.isArray(where[p])) {
6085 for (const e in where[p]) {
6086 where[p][e] = Handlebars.compile(where[p][e])(handlebarsContext);
6087 }
6088 } else
6089 if (where[p] instanceof Object) {
6090 compileStringsHere(where[p]);
6091 }*/
6092
6093 }
6094 }
6095
6096 if (handlebarsContext) {
6097 compileStringsHere(field);
6098 } //Если у поля есть массив properties - нам надо добавить его в fieldsPropsArray и в dataset поля добавить ссылку, т.к. потом, очевидно, пригодятся
6099
6100
6101 var propsIndex = null;
6102
6103 if (field.properties) {
6104 propsIndex = fieldsPropsArray.length;
6105 fieldsPropsArray.push(field.properties);
6106 } //Определимся с типом поля, т.к. от этого будут зависеть наши дальнейшие действия
6107
6108
6109 var groupHTML, help;
6110
6111 switch (field.type) {
6112 case 'date': //Поле со вводом даты. Обычный инпут.
6113
6114 case 'text': //Поле со вводом текста. Обычный инпут.
6115
6116 case 'email': //Поле со вводом мыла. Обычный инпут.
6117
6118 case 'link': //Поле со вводом ссылки. Обычный инпут.
6119
6120 case 'integer': //Поле со вводом целого числа. Обычный инпут.
6121
6122 case 'float':
6123 //Поле со вводом дробного числа. Обычный инпут.
6124 return getInputFieldHTML(field, propsIndex);
6125
6126 case 'textarea':
6127 return getTextareaFieldHTML(field, propsIndex);
6128
6129 case 'switchButton':
6130 return getSwitchButtonHTML(field, propsIndex);
6131
6132 case 'linkButton':
6133 if (field.fieldType === 'linkButtonGroup' && !field.service && !field.caption) {
6134 return '';
6135 } //Если надо вывести кнопки только для определённых языков, например
6136
6137
6138 return getLinkButtonHTML(field, propsIndex);
6139
6140 case 'image':
6141 return getImageViewerHTML(field, propsIndex);
6142
6143 case 'file':
6144 return getFileFieldHTML(field, propsIndex);
6145
6146 case 'title':
6147 return '<div class="element unfocusable ' + (field.class ? field.class : '') + '"><div class="elementTitle"><div class="title">' + field.caption + '</div></div></div>';
6148
6149 case 'title2':
6150 return '<div class="element unfocusable ' + (field.class ? field.class : '') + '"><div class="elementTitle"><div class="stepQuestion title">' + field.caption + '</div></div></div>';
6151
6152 case 'label':
6153 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>';
6154
6155 case 'clickableLink':
6156 if (!field.caption) {
6157 field.caption = decodeURIComponent(field.link.replace(/\+/g, ' '));
6158 }
6159
6160 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>';
6161
6162 case 'radioGroup':
6163 case 'checkboxGroup':
6164 case 'buttonGroup':
6165 //Подсказка
6166 help = '';
6167
6168 if (field.titleHelp) {
6169 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>");
6170 }
6171
6172 groupHTML = field.title ? '<div class="stepTitle title">' + field.title + help + '</div>' : '';
6173 Array.from(field.buttons).forEach(function (button) {
6174 if (!field.name && field.type !== 'buttonGroup') {
6175 ce('no_field_name', 'type:' + field.type + ', title:' + field.title);
6176 return;
6177 }
6178
6179 if (!button.name) {
6180 ce('no_button_name', 'field type:' + field.type + ', field title:' + field.title + ', button caption:' + field.caption);
6181 return;
6182 }
6183
6184 button.type = 'switchButton';
6185 button[field.type.toLowerCase()] = field.name;
6186
6187 if (field.solutionName) {
6188 button['groupSolutionName'] = field.solutionName;
6189 }
6190
6191 if (!button.flags) {
6192 button.flags = [];
6193 }
6194
6195 if (field.flags && field.flags.includes('block')) {
6196 button.flags.push('block');
6197 }
6198
6199 if (!button.flags.includes('inline') && !button.flags.includes('block')) {
6200 button.flags.push('inline');
6201 }
6202
6203 groupHTML += handleFieldAndGetHTML(button);
6204 });
6205
6206 if (!field.title) {
6207 field.title = '';
6208 }
6209
6210 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>");
6211
6212 case 'select':
6213 groupHTML = '';
6214 Array.from(field.buttons).forEach(function (button) {
6215 if (!field.name && field.type !== 'buttonGroup') {
6216 ce('no_field_name', 'type:' + field.type + ', title:' + field.title);
6217 return;
6218 }
6219
6220 if (!button.name) {
6221 ce('no_button_name', 'field type:' + field.type + ', field title:' + field.title + ', button caption:' + field.caption);
6222 return;
6223 }
6224
6225 button.type = 'switchButton';
6226 button[field.type.toLowerCase()] = field.name;
6227
6228 if (field.solutionName) {
6229 button['groupSolutionName'] = field.solutionName;
6230 }
6231
6232 if (!button.flags) {
6233 button.flags = [];
6234 }
6235
6236 if (field.flags && field.flags.includes('block')) {
6237 button.flags.push('block');
6238 }
6239
6240 if (!button.flags.includes('inline') && !button.flags.includes('block')) {
6241 button.flags.push('inline');
6242 }
6243
6244 groupHTML += handleFieldAndGetHTML(button);
6245 });
6246
6247 if (!field.title) {
6248 field.title = '';
6249 }
6250
6251 var includesString,
6252 title = '';
6253
6254 if (field.hint) {
6255 title += '<div class="popUpTitle"><div class="buttPath">Подсказка:</div>' + field.hint + '\n</div>';
6256 includesString += ' hasHint';
6257 }
6258
6259 var additional = '';
6260
6261 if (field.help) {
6262 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>';
6263 includesString += ' hasHelp';
6264 }
6265
6266 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>';
6267
6268 case 'group':
6269 //Генерируем HTML всей внутрянки этой группы
6270 groupHTML = '';
6271 Array.from(field.groupFields).forEach(function (groupField) {
6272 groupHTML += handleFieldAndGetHTML(groupField);
6273 }); //Записываем её в наш массив свойств
6274
6275 propsIndex = fieldsPropsArray.length;
6276 fieldsPropsArray.push(groupHTML); //Возвращаем обёртку группы со всеми нужными параметрами. Внутрянку будем добавлять/удалять по мере необходимости
6277
6278 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>';
6279
6280 case 'actionButtonGroup':
6281 case 'linkButtonGroup':
6282 //Подсказка
6283 help = '';
6284
6285 if (field.titleHelp) {
6286 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>");
6287 }
6288
6289 groupHTML = field.title ? '<div class="stepTitle title">' + field.title + help + '</div>' : '';
6290
6291 if (!field.title) {
6292 field.title = '';
6293 }
6294
6295 if (!field.name) {
6296 field.name = '';
6297 }
6298
6299 if (!field.class) {
6300 field.class = field.name;
6301 }
6302
6303 Array.from(field.buttons).forEach(function (button) {
6304 button.service = button.type;
6305 button.type = 'linkButton';
6306 button.fieldType = field.type;
6307 button[field.type.toLowerCase()] = field.class;
6308
6309 if (field.type === 'actionButtonGroup') {
6310 button.link = '';
6311
6312 if (!button.iconClass) {
6313 button.iconClass = 'actionButtonNoIcon';
6314 }
6315 }
6316
6317 if (!button.flags) {
6318 button.flags = [];
6319 }
6320
6321 if (field.flags && field.flags.includes("block")) {
6322 button.flags.push("block");
6323 }
6324
6325 if (!button.name) {
6326 button.name = field.name;
6327 }
6328
6329 if (!button.class) {
6330 button.class = field.class;
6331 }
6332
6333 if (!button.flags.includes('inline') && !button.flags.includes('block')) {
6334 button.flags.push('inline');
6335 }
6336
6337 groupHTML += handleFieldAndGetHTML(button);
6338 });
6339 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>';
6340
6341 case 'view':
6342 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);
6343
6344 case 'null':
6345 return '';
6346 }
6347
6348 ce('wrong_field_type', 'type:' + field.type + ', title:' + field.title + ', caption:' + field.caption);
6349};
6350
6351window.getFieldsHTML = function (fields, context) {
6352 var fieldsName = '';
6353
6354 if (!context || !context[fields]) {
6355 if (_this8 && _this8.getTask) {
6356 context = _this8.getTask().input_values;
6357 } else {
6358 context = window.handlebarsContext;
6359 }
6360 }
6361
6362 if (fields && typeof fields === "string") {
6363 fieldsName = fields;
6364 fields = context[fieldsName];
6365 }
6366
6367 if (!fields || !Array.isArray(fields)) {
6368 console.log(context);
6369 ce('Что-то неправильное передано хелперу drawFields: ', [fieldsName, fields ? fields : '[пустота]']);
6370 return;
6371 }
6372
6373 var out = '';
6374 Array.from(fields).forEach(function (field) {
6375 out += handleFieldAndGetHTML.apply(this, [field, context]);
6376 });
6377
6378 if (fieldsName) {
6379 out = "<div class=\"theOneFieldsContainer fieldsName_".concat(fieldsName, "\" data-name=\"").concat(fieldsName, "\">").concat(out, "</div>");
6380 }
6381
6382 return out;
6383};
6384/* Всякие служебные переменные и JSONки */
6385
6386
6387window.specObjectNameOfTheField = [];
6388window.fieldsPropsArray = []; //Настройки шаблона, работающие через CSS (если какая-то из них false, то в класс сьюта добавится "no_[имя настройки]", например "no_taskList")
6389
6390window.cssNoClassSettings = ['taskSwitcher', 'taskCounter', 'taskList', 'taskMinimizer', 'notForDirect'];
6391window.cssClassSettings = ['fullScreenMode', 'verticalSuite']; //СВГшные иконки, доступные к использованию с классом ffIcon (в data-icon)
6392
6393window.svgIcons = {
6394 "keyboard_arrow_up": {
6395 "attributes": 'viewBox="0 0 320 512"',
6396 "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"
6397 },
6398 "keyboard_tab": {
6399 "attributes": 'viewBox="0 0 448 512"',
6400 "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"
6401 },
6402 "keyboard_backspace": {
6403 "attributes": 'viewBox="0 0 448 512"',
6404 "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"
6405 },
6406 "invert_colors": {
6407 "attributes": 'viewBox="0 0 512 512"',
6408 "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"
6409 },
6410 "open_in_new": {
6411 "attributes": 'viewBox="0 0 576 512"',
6412 "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"
6413 },
6414 "done": {
6415 "attributes": 'viewBox="0 0 512 512"',
6416 "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"
6417 },
6418 "angle_down": {
6419 "attributes": 'viewBox="0 0 320 512"',
6420 "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"
6421 },
6422 "times": {
6423 "attributes": 'viewBox="0 0 352 512"',
6424 "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"
6425 },
6426 "eye": {
6427 "attributes": 'viewBox="0 0 576 512"',
6428 "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"
6429 },
6430 "comment_smile": {
6431 "attributes": 'viewBox="0 0 512 512"',
6432 "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"
6433 },
6434 "help": {
6435 "attributes": 'viewBox="0 0 448 512"',
6436 "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",
6437 "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"
6438 },
6439 "helpRound": {
6440 "attributes": 'viewBox="0 0 512 512"',
6441 "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"
6442 }
6443}; //Возможные выводимые в консоль ошибки в шаблоне, для упрощения отладки и устранения проблем со входным JSON
6444
6445window.templateErrorsJSON = {
6446 "output_placeholder_not_found": 'Судя по всему, вы попытались использовать операторы в одном из полей JSONки - найдено сочетание "|||" Но вот имя оператора мне не понятно. Если вы ничего такого делать не собирались - пожалуйста, не используйте "|||" в JSONке.',
6447 "output_placeholder_input_insufficient_parameters": 'Недостаточно параметров у оператора в JSON. У оператора INPUT три параметра - сам оператора, имя входного поля и текст, в который вместо плейсхолдера "▼" (Alt+31) будет подставлено значение этого поля.',
6448 "wrong_field_type": 'Неверный тип у поля, вместо этого выведено "undefined"',
6449 "wrong_linkButtonGroup_button_type": 'Неверный тип у кнопки в linkButtonGroup, она не выведена.',
6450 "no_field_name": 'У поля отсутствует свойство name!',
6451 "no_button_name": 'У кнопки отсутствует свойство name!',
6452 "valuesToWrite_is_not_an_array": 'Свойство valuesToWrite не является массивом объектов!',
6453 "valuesToWrite_has_no_selector": 'У одного из объектов массива valuesToWrite отсутствует свойство "selector". Укажите его, чтобы было понятно, в свойство property элемента по какому селектору вставлять значение value.',
6454 "valuesToWrite_has_no_property": 'У одного из объектов массива valuesToWrite отсутствует свойство "property". Укажите его, чтобы было понятно, какое свойство заменять у указанного элемента с селектором selector (например, "value", если это input или textarea или "innerHTML" если это div или span).',
6455 "valuesToWrite_has_no_value": 'У одного из объектов массива valuesToWrite отсутствует свойство "value". Укажите его, чтобы было понятно, какое именно значение вставлять в свойство property элемента с селектором selector.',
6456 "valuesToWrite_target_not_found": 'Только что нажатая кнопка должна была прописать значение в свойство элемента по определённому селектору (свойство valuesToWrite во входном JSON), но нужный элемент по указанному селектору найти так и не удалось... ',
6457 "specmapper_conversion_type_not_found": 'Указанного типа конверсии в правиле specMapper`а не существует.',
6458 "specmapper_conversion_no_function": 'В specMapper`е указан тип конверсии - функция, но не указана сама функция.'
6459}; //Возможные типы linkButton и их параметры
6460
6461window.linkButtonTypes = {
6462 "yandexSearch": {
6463 iconClass: "yandexButtIcon",
6464 link: "https://yandex.ru/yandsearch?text=",
6465 caption: "Яндекс.Поиск"
6466 },
6467 "yandexPics": {
6468 iconClass: "yandexButtIcon",
6469 link: "https://yandex.ru/images/search?text=",
6470 caption: "Яндекс.Картинки"
6471 },
6472 "yandexSearchByPic": {
6473 iconClass: "yandexButtIcon",
6474 link: "https://yandex.by/images/search?url=",
6475 "linkAfter": "&rpt=imageview",
6476 caption: "Поиск по картинке"
6477 },
6478 "yandexNews": {
6479 iconClass: "yandexButtIcon",
6480 link: "https://news.yandex.ru/yandsearch?rpt=nnews2&grhow=clutop&text=",
6481 caption: "Яндекс.Новости"
6482 },
6483 "yandexVideo": {
6484 iconClass: "yandexButtIcon",
6485 link: "https://yandex.ru/video/search?text=",
6486 caption: "Яндекс.Видео"
6487 },
6488 "yandexTranslate": {
6489 iconClass: "yandexButtIcon",
6490 link: "https://translate.yandex.ru/?text=",
6491 caption: "Яндекс.Переводчик"
6492 },
6493 "googleSearch": {
6494 iconClass: "googleButtIcon",
6495 link: "https://www.google.ru/search?q=",
6496 caption: "Google.Поиск"
6497 },
6498 "googlePics": {
6499 iconClass: "googleButtIcon",
6500 link: "https://www.google.ru/search?&hl=ru&site=imghp&tbm=isch&source=hp&biw=1467&bih=682&q=",
6501 caption: "Google.Картинки"
6502 },
6503 "googleSearchByPic": {
6504 iconClass: "googleButtIcon",
6505 link: "https://www.google.by/searchbyimage?image_url=",
6506 caption: "Поиск по картинке"
6507 },
6508 "googleNews": {
6509 iconClass: "googleButtIcon",
6510 link: "https://www.google.ru/search?hl=ru&gl=ru&tbm=nws&authuser=0&q=",
6511 caption: "Google.Новости"
6512 },
6513 "googleVideo": {
6514 iconClass: "googleButtIcon",
6515 link: "https://www.google.ru/search?tbm=vid&hl=ru&source=hp&biw=&bih=&q=",
6516 caption: "Google.Видео"
6517 },
6518 "googleTranslate": {
6519 iconClass: "googleButtIcon",
6520 link: "https://translate.google.ru/#auto/ru/",
6521 caption: "Google.Переводчик"
6522 },
6523 //Украина
6524 "yandexSearchUA": {
6525 iconClass: "yandexButtIcon",
6526 link: "https://yandex.ua/search/?lr=143&msid=1464598804.64254.22885.27565&text=",
6527 caption: "Яндекс.Поиск (укр)"
6528 },
6529 "yandexVideoUA": {
6530 iconClass: "yandexButtIcon",
6531 link: "https://yandex.ua/video/search?text=",
6532 caption: "Яндекс.Видео (укр)"
6533 },
6534 "googleSearchUA": {
6535 iconClass: "googleButtIcon",
6536 link: "https://www.google.com.ua/#q=",
6537 caption: "Google.Поиск (укр)"
6538 },
6539 "googleVideoUA": {
6540 iconClass: "googleButtIcon",
6541 link: "https://www.google.com.ua/search?tbm=vid&hl=ru-UA&source=hp&biw=&bih=&q=",
6542 "linkAfter": "&tbm=vid",
6543 caption: "Google.Видео (укр)"
6544 }
6545};
6546window.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";
6547$('head').append('<meta name="referrer" content="no-referrer" />');
6548document.ready = new Promise(function (resolve) {
6549 if (document.readyState === 'complete') {
6550 resolve();
6551 } else {
6552 var onReady = function onReady() {
6553 resolve();
6554 document.removeEventListener('DOMContentLoaded', onReady, true);
6555 window.removeEventListener('load', onReady, true);
6556 };
6557
6558 document.addEventListener('DOMContentLoaded', onReady, true);
6559 window.addEventListener('load', onReady, true);
6560 }
6561});
6562
6563function extend(ParentClass, constructorFunction, prototypeHash) {
6564 constructorFunction = constructorFunction || function () {};
6565
6566 prototypeHash = prototypeHash || {};
6567
6568 if (ParentClass) {
6569 constructorFunction.prototype = Object.create(ParentClass.prototype);
6570 }
6571
6572 for (var i in prototypeHash) {
6573 constructorFunction.prototype[i] = prototypeHash[i];
6574 }
6575
6576 return constructorFunction;
6577}