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