PageSourceSearch

https://avareange.ru/company-capabilities.v1.js

js avareange.ru collected 2026-10-02 01:08:40 UTC 12,241 bytes, 128 lines download raw bytes

1// Progressive enhancement of the preserved public /future/ comparison section.
2// Keep React-owned cards in place; add status, grouping and explanatory labels
3// only once their existing containers have hydrated.
4(() => {
5  'use strict';
6  if (window.__avareangeCapabilities) return;
7  window.__avareangeCapabilities = true;
8  const data = {"reviewed":"2026-09-29","roadmapSource":"https://avareange.ru/roadmap","features":[{"id":"command","status":"released","labelRu":"Командный центр","labelEn":"Command center","captionRu":"Компания → отделы → приоритеты","captionEn":"Company → teams → priorities"},{"id":"chat","status":"released","labelRu":"LLM-анализ сообщений","labelEn":"LLM message analysis","captionRu":"Сообщение → признаки угрозы → действие","captionEn":"Message → warning signs → action"},{"id":"osint","status":"released","labelRu":"OSINT-атаки","labelEn":"OSINT-based attacks","captionRu":"Открытые данные → контекст → учебная атака","captionEn":"Public data → context → training attack"},{"id":"profile","status":"released","labelRu":"Профиль риска сотрудника","labelEn":"Employee risk profile","captionRu":"Действия сотрудника → личный профиль риска","captionEn":"Employee actions → individual risk profile"},{"id":"leak","status":"released","labelRu":"Мониторинг утечек паролей","labelEn":"Password leak monitoring","captionRu":"Учётная запись → обнаружение утечки → сигнал","captionEn":"Account → detected leak → alert"},{"id":"agent","status":"released","stage":"DONE","target":null,"task":"c49e3ccc-f1bc-4d02-baf5-e5366af8f4cb","labelRu":"ИИ-агент обучения","labelEn":"AI learning agent","captionRu":"Ошибка → персональный урок → проверка","captionEn":"Mistake → personal lesson → assessment","confirmedBy":"owner","confirmedAt":"2026-09-29"},{"id":"plugin","status":"released","stage":"DONE","target":null,"task":"57bc385d-ba27-4af1-bddd-2e4f43909e7b","labelRu":"Шаблон из реального письма","labelEn":"Template from a real email","captionRu":"Письмо → удаление угроз → учебный шаблон","captionEn":"Email → threat removal → training template","confirmedBy":"owner","confirmedAt":"2026-09-29"},{"id":"domain","status":"roadmap","stage":"PRIORITY","target":null,"task":"8d5fe9c3-86ac-4ff6-a81b-375377a77380","labelRu":"Анализ доменов в браузере","labelEn":"Browser domain analysis","captionRu":"Адрес сайта → проверка → предупреждение","captionEn":"Website address → check → warning"},{"id":"calendar","status":"roadmap","stage":"IN_PROGRESS","target":null,"task":"5ba1c3ed-1f2d-4770-a91c-f1adc28492c3","labelRu":"Календарь с ИИ","labelEn":"AI calendar","captionRu":"Цели команды → план → события в календаре","captionEn":"Team goals → plan → calendar events"}]};
9  const text = (en, ru, english) => en ? english : ru;
10  const element = (tag, className, content) => {
11    const el = document.createElement(tag);
12    el.className = className;
13    if (content) el.textContent = content;
14    return el;
15  };
16  function steps(items) {
17    const el = element('div', 'cap-steps');
18    items.forEach((item, i) => {
19      const step = element('div', 'cap-step');
20      step.append(element('span', 'cap-step-number', String(i + 1).padStart(2, '0')), element('span', '', item));
21      el.append(step);
22    });
23    return el;
24  }
25  function visual(id, en) {
26    if (id === 'osint') return steps(en ? ['Public company data', 'A familiar work situation', 'A safe training attack'] : ['Открытые данные о компании', 'Знакомая рабочая ситуация', 'Безопасная учебная атака']);
27    if (id === 'agent') return steps(en ? ['Mistake in a simulation', 'A lesson on that topic', 'Follow-up asse
27ssment'] : ['Ошибка в симуляции', 'Урок по теме ошибки', 'Повторная проверка']);
28    if (id === 'plugin') return steps(en ? ['Reported suspicious email', 'Remove malicious elements', 'Prepare a training template'] : ['Подозрительное письмо', 'Удаление опасных элементов', 'Готовый учебный шаблон']);
29    if (id === 'domain') {
30      const el = element('div', 'cap-browser');
31      el.append(element('div', 'cap-address', 'company-login.example'), element('div', 'cap-domain-warning', text(en, 'Адрес похож на подделку', 'Possible lookalike domain')), element('div', 'cap-domain-reference', text(en, 'Сравнение: company.example', 'Compare: company.example')));
32      return el;
33    }
34    if (id === 'calendar') {
35      const el = element('div', 'cap-calendar');
36      for (const [day, event] of (en ? [['MON','Simulation'],['WED','Debrief'],['FRI','Training']] : [['ПН','Симуляция'],['СР','Разбор'],['ПТ','Обучение']])) {
37        const row = element('div', 'cap-calendar-event');
38        row.append(element('span', '', day), element('strong', '', event));el.append(row);
39      }
40      return el;
41    }
42    return null;
43  }
44  function groupHeader(en, planned, count) {
45    const el = element('li', 'cap-group-header' + (planned ? ' cap-group-header--planned' : ''));
46    el.dataset.capGroup = planned ? 'roadmap' : 'released';
47    const header = element('div', 'cap-group-title');
48    header.append(element('span', 'cap-count', String(count)), element('h3', '', planned ? text(en,'В дорожной карте','On the roadmap') : text(en,'Уже доступно','Available now')));
49    el.append(header, element('p', '', planned ? text(en,'Будущие возможности. Статус и плановый срок — на каждой карточке.','Future capabilities. Each card shows its status and planned timing.') : text(en,'Функции платформы, которыми уже можно пользоваться. Доступность зависит от тарифа.','Platform capabilities available today. Availability depends on your plan.')));
50    return el;
51  }
52  function enhance(section) {
53    const en = location.pathname.startsWith('/en/');
54    const cards = [...section.querySelectorAll('.fx-bento-card')];
55    if (cards.length !== data.features.length || !cards.every(card => data.features.some(f=>f.id===card.dataset.visual))) return;
56    const list = section.querySelector('.fx-bento');
57    const ready = data.features.filter(f=>f.status==='released').length;
58    const planned = data.features.length-ready;
59    const heading = section.querySelector('#compare-title');
60    if (heading) {
61      heading.replaceChildren(document.createTextNode(text(en,`${ready} возможностей уже доступны.`,`${ready} capabilities available now.`)), element('span','cap-head-accent',text(en,`Ещё ${planned} — в дорожной карте.`,`${planned} more on the roadmap.`)));
62      heading.classList.add('cap-heading');
63    }
64    const summary = section.querySelector('.fx-exclusive-score');
65    if (summary) {
66      summary.replaceChildren();
67      const description = element('p','cap-summary',text(en,'Оценка риска, защита и обучение сотрудников: действующие функции и следующие шаги развития.','Risk assessment, protection and employee training: current capabilities and the next steps.'));
68      const link = element('a','cap-roadmap-jump',text(en,'Перейти к планам ↓','Explore planned features ↓'));
69      link.href='#capabilities-roadmap';summary.append(description,link);
70    }
71    list.prepend(groupHeader(en,false,ready));
72    const firstPlanned = cards.find(c=>data.features.find(f=>f.id===c.dataset.visual).status==='roadmap').parentElement;
73    const roadmapHeader = groupHeader(en,true,planned);roadmapHeader.id='capabilities-roadmap';
74    firstPlanned.before(roadmapHeader);
75    cards.forEach(card => {
76      const feature = data.features.find(f=>f.id===card.dataset.visual);
77      card.dataset.status=feature.status;
78      const cell = card.parentElement;
79      cell.classList.add('cap-cell');cell.dataset.capStatus=feature.status;
80      const released = feature.status==='released';
81      const status = element('div','cap-status-row');
82      const label = released ? text(en,'✓ Уже доступно','✓ Available now') : feature.stage==='PRIORITY' ? text(en,'◷ В приоритете','◷ Prioritized') : text(en,'◷ В разработке','◷ In development');
83      status.append(element('span',released?'cap-status cap-status--ready':'cap-status cap-status--
83planned',label));
84      if (!released) status.append(element('span','cap-target',feature.target ? text(en,'План: ','Target: ')+feature.target : text(en,'Срок не объявлен','Timing not announced')));
85      card.prepend(status);
86      card.querySelector('.fx-bento-roadmap')?.remove();
87      const box = card.querySelector('.fx-bento-visual');
88      const replacement = visual(feature.id,en);
89      if (replacement) box.replaceChildren(replacement);
90      const figure = element('figure','cap-figure');
91      box.before(figure);figure.append(box,element('figcaption','cap-caption',text(en,feature.captionRu,feature.captionEn)));
92      const oldTitle = card.querySelector('h3');
93      if (oldTitle) {
94        const title = element('h4','cap-card-title',text(en,feature.labelRu,feature.labelEn));oldTitle.replaceWith(title);
95      }
96      if (!released) {
97        const note = element('a','cap-roadmap-link',text(en,'Статус в дорожной карте ↗','View roadmap status ↗'));
98        note.href='/roadmap/';card.append(note);
99      }
100    });
101    const footnote = section.querySelector('.fx-footnote');
102    if (footnote) footnote.textContent=text(en,'В сравнительной таблице 42 критерия: 40 возможностей доступны, ещё 2 — в дорожной карте. Это отдельная оценка, а не число функций выше. Иллюстрации показывают сценарии работы. Плановые функции ещё не доступны; сроки могут меняться.','The comparison table covers 42 criteria: 40 available capabilities and 2 on the roadmap. This is a separate measure, not the number of capabilities above. Illustrations show example workflows. Planned features are not yet available; timing may change.');
103    // Keep the compact roadmap summary consistent with the two owner-confirmed releases.
104    const pulse=document.querySelector('.fx-pulse-head');
105    if (pulse) {
106      const readyCount=pulse.querySelector('div[data-tone="safe"] dt');
107      const workingCount=pulse.querySelector('div[data-tone="ion"] dt');
108      if (readyCount && workingCount && readyCount.textContent==='18' && workingCount.textContent==='12') {
109        readyCount.textContent='20';workingCount.textContent='10';
110        pulse.querySelector('.fx-pulse-bar [data-tone="safe"]').style.flexGrow='20';
111        pulse.querySelector('.fx-pulse-bar [data-tone="ion"]').style.flexGrow='10';
112      }
113    }
114    section.dataset.capabilitiesEnhanced='true';
115  }
116  let retry;
117  function mount() {
118    clearTimeout(retry);
119    const section=document.querySelector('#compare.fx-exclusive');
120    if (!section || section.dataset.capabilitiesEnhanced) return;
121    const cards=[...section.querySelectorAll('.fx-bento-card')];
122    if (!cards.length) return;
123    if (window.__next_f && !cards.every(card=>Object.keys(card).some(key=>key.startsWith('__reactFiber$')))) {retry=setTimeout(mount,100);return;}
124    enhance(section);
125  }
126  function start(){mount();new MutationObserver(mount).observe(document.body,{childList:true,subtree:true});}
127  if(document.readyState==='complete')start();else window.addEventListener('load',start,{once:true});
128})();

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.