PageSourceSearch

https://sinonim.org/js/test.js?304

js sinonim.org collected 2026-09-24 20:39:45 UTC 155,080 bytes, 3,788 lines download raw bytes

1// Shared PRO check for image results and image turns in chats.
2function handleWatermarkClick(event, link, onPaid) {
3  if (event) event.preventDefault();
4  if (link && link.dataset.checkingPro === '1') return false;
5  if (link) link.dataset.checkingPro = '1';
6
7  fetch('/data.php?pu&_=' + Date.now(), {
8    cache: 'no-store',
9    credentials: 'same-origin'
10  })
11    .then(function(r) {
12      if (!r.ok) throw new Error('HTTP ' + r.status);
13      return r.text();
14    })
15    .then(function(t) {
16      if (t.trim() === '1') {
17        if (typeof onPaid === 'function') return onPaid();
18        window.isPaidUser = true;
19        var refreshUrl = new URL(window.location.href);
20        refreshUrl.searchParams.set('_reload', String(Date.now()));
21        refreshUrl.hash = 'result';
22        window.location.href = refreshUrl.toString();
23        return;
24      }
25      window.location.href = '/price';
26    })
27    .catch(function() {
28      window.location.href = '/price';
29    })
30    .then(function() {
31      if (link) delete link.dataset.checkingPro;
32    });
33
34  return false;
35}
36
37// Shared by the home assistant, photo questions and chat.
38window.sinonimMath=(function(){
39  var ready=null,queue=Promise.resolve(),serial=0;
40  function escapeHtml(text){return String(text).replace(/[&<>"']/g,function(c){return {'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#039;'}[c];});}
41  function protect(text){
42    var values=[],prefix='SINONIMMATH'+Date.now()+'X'+(++serial)+'TOKEN';
43    // iOS before 16.4 cannot parse RegExp lookbehind, even inside an unused function.
44    var input=String(text||''),source='',from=0,match;
45    var parts=/(`{3,}|~{3,})[^\n]*\n[\s\S]*?\1|(`+)[^`]*?\2|\\\[[\s\S]*?\\\]|\\\([\s\S]*?\\\)|\$\$(?:[\s\S]*?[^\\])?\$\$|\$(?![\s$])(?:\\.|[^$\r\n\\])*?(?:\\[^\s\\]|[^$\s\\])\$(?!\$)/g;
46    while((match=parts.exec(input))!==null){
47      if(match[1]||match[2])continue;
48      var value=match[0],before=input.charAt(match.index-1);
49      if(before==='\\'||(value.charAt(0)==='$'&&before==='$')){
50        // Retry after the rejected opener so it cannot hide a later valid formula.
51        parts.lastIndex=match.index+1;continue;
52      }
53      var token=prefix+values.length+'END';values.push(value);
54      source+=input.slice(from,match.index)+token;from=parts.lastIndex;
55    }
56    source+=input.slice(from);
57    return {text:source,restore:function(html){values.forEach(function(value,index){html=html.split(prefix+index+'END').join('<span class="sinonim-math">'+escapeHtml(value)+'</span>');});return html;}};
58  }
59  function repairLegacy(target){
60    Array.prototype.forEach.call(target.querySelectorAll('p'),function(block){
61      if(block.querySelector('code,pre,a,mjx-container,.sinonim-math'))return;
62      var plain=document.createElement('div');
63      plain.innerHTML=block.innerHTML.replace(/<br\s*\/?\s*>/gi,'\n');
64      var match=plain.textContent.trim().match(/^\[\s*([\s\S]+?)\s*\]$/);
65      if(!match)return;
66      var formula=match[1].trim();
67      var tex=/\\[a-zA-Z]+/.test(formula);
68      var equation=/[\r\n]/.test(plain.textContent)&&/^[0-9a-zA-Z\s+\-*/=^_{}().,<>|]+$/.test(formula)&&/[=+*/^]/.test(formula)&&/\d/.test(formula);
69      if(tex||equation)block.textContent='\\[\n'+formula+'\n\\]';
70    });
71  }
72  function ensure(){
73    if(ready)return ready;
74    ready=new Promise(function(resolve,reject){
75      function loaded(){
76        var mj=window.MathJax;
77        if(!mj||!mj.typesetPromise){reject(new Error('MathJax unavailable'));return;}
78        Promise.resolve(mj.startup&&mj.startup.promise).then(function(){resolve(mj);},reject);
79      }
80      if(window.MathJax&&window.MathJax.typesetPromise){loaded();return;}
81      var script=document.querySelector('script[src*="/js/tex-svg.js"]'),created=!script;
82      if(created){
83        window.MathJax=window.MathJax||{tex:{inlineMath:[['$','$'],['\\(','\\)']],displayMath:[['$$','$$'],['\\[','\\]']]},svg:{fontCache:'global'}};
84        script=document.createElement('script');script.id='sinonimMathJax';script.async=true;script.src='/js/tex-svg.js';
85      }
86      script.addEventListener('load',loaded,{once:true});
87      script.addEventListener('error',function(){reject(new Error('MathJax load failed'));},{once:true});
88      if(created)document.head.appendChild(script);
89    }).catch(function(error){ready=null;throw error;});
90    return ready;
91  }
92  function clear(target){if(target&&window.MathJax&&window.MathJax.typesetClear)window.MathJax.typesetClear([target]);}
93  function render(target){
94    if(!target)return Promise.resolve();
95    queue=queue.then(function(){
96      if(!target.isConnected)return;
97      repairLegacy(target);
98      return ensure().then(function(mj){
99        if(!target.isConnected)return;
100        if(!document.getElementById('sinonimMathStyles')){
101          var style=document.createElement('style');style.id='sinonimMathStyles';
102          style.textContent='.paBubble,.cg-message-text,.homeAssistantMessage{min-width:0}.paBubble mjx-container[display="true"],.cg-message-text mjx-container[display="true"],.homeAssistantMessage mjx-container[display="true"]{display:block;max-width:100%;overflow-x:auto;overflow-y:hidden;padding:4px 0}.sinonim-math{max-width:100%}';
103          document.head.appendChild(style);
104        }
105        return mj.typesetPromise([target]);
106      });
107    }).catch(function(error){console.warn('Formula rendering failed:',error.message);});
108    return queue;
109  }
110  return {protect:protect,render:render,clear:clear};
111})();
112
113(function () {
114var m = navigator.userAgent.match(/Chrom(?:e|ium)\/(\d+)/);
115if (m && parseInt(m[1], 10) < 45) {
116var div = document.createElement('div');
117div.style.cssText = 'background:#fff3cd;color:#856404;border:1px solid #ffeeba;padding:10px 15px;margin:10px 0;font-size:14px;';
118div.innerHTML = 'Ваш браузер устарел. Некоторые функции сайта могут работать некорректно. Пожалуйста, обновите браузер.';
119if (document.body) {
120document.body.insertBefore(div, document.body.firstChild);
121} else {
122document.addEventListener('DOMContentLoaded', function () {
123document.body.insertBefore(div, document.body.firstChild);
124});
125}
126}
127})();
128
129var SUG_PROXY_ORIGIN = 'https://a.sinonim.org';
130
131function sugProxyLocalUrl(url) {
132var value = String(url || '');
133return value.indexOf(SUG_PROXY_ORIGIN + '/sug.php?') === 0
134? value.substring(SUG_PROXY_ORIGIN.length)
135: value;
136}
137
138function sugProxyImageFallback(img) {
139if (!img || img.getAttribute('data-sug-fallback-used') === '1') return;
140var primary = img.getAttribute('src') || img.src || '';
141var fallback = sugProxyLocalUrl(primary);
142if (fallback === primary) return;
143img.setAttribute('data-sug-fallback-used', '1');
144img.onerror = null;
145img.src = fallback;
146}
147
148function sugProxyNavigate(event, link, onNavigate) {
149if (!link) return true;
150var primary = link.href || '';
151var fallback = link.getAttribute('data-sug-fallback') || sugProxyLocalUrl(primary);
152var healthUrl = SUG_PROXY_ORIGIN + '/sug.php?img=' + encodeURIComponent('https://sinonim.org/img/x.png') + '&_health=' + Date.now();
153if (!primary || fallback === primary) {
154if (primary && typeof onNavigate === 'function') {
155if (event) event.preventDefault();
156onNavigate(primary);
157return false;
158}
159return true;
160}
161if (event) event.preventDefault();
162
163var destination = window;
164if (typeof onNavigate !== 'function' && link.target === '_blank') {
165destination = window.open(primary, '_blank') || window;
166}
167
168var finished = false;
169var healthProbe = new Image();
170var timer = setTimeout(function () {
171finish(fallback);
172}, 8000);
173
174function finish(url) {
175if (finished) return;
176finished = true;
177clearTimeout(timer);
178healthProbe.onload = null;
179healthProbe.onerror = null;
180if (typeof onNavigate === 'function') onNavigate(url);
181else destination.location.href = url;
182}
183
184healthProbe.onload = function () {
185finish(primary);
186};
187healthProbe.onerror = function () {
188finish(fallback);
189};
190healthProbe.src = healthUrl;
191return false;
192}
193
194(function () {
195function initServicesSearch() {
196var container = document.getElementById('onlineServices');
197var input = document.getElementById('servicesSearch');
198var clear = document.getElementById('servicesSearchClear');
199var empty = document.getElementById('servicesSearchEmpty');
200if (!container || !input || !clear || !empty) return;
201
202var groups = container.querySelectorAll('.onlineGroup');
203var logTimer = null;
204var lastLoggedQuery = '';
205function normalize(value) {
206return String(value || '').toLocaleLowerCase('ru-RU').replace(/\s+/g, ' ').trim();
207}
208function logMissingQuery(query) {
209if (query.length < 5 || query === lastLoggedQuery) return;
210lastLoggedQuery = query;
211fetch('/sug.php?serviceSearchMiss=1', {
212method: 'POST',
213credentials: 'same-origin',
214headers: {'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'},
215body: 'query=' + encodeURIComponent(query),
216keepalive: true
217}).catch(function () {});
218}
219function scheduleMissingQuery(query, found) {
220clearTimeout(logTimer);
221if (found || query.length < 5 || query === lastLoggedQuery) return;
222logTimer = setTimeout(function () {
223var currentQuery = normalize(input.value);
224if (currentQuery !== query || currentQuery.length < 5 || currentQuery === lastLoggedQuery) return;
225logMissingQuery(currentQuery);
226}, 1000);
227}
228function filterServices() {
229var query = normalize(input.value);
230var found = false;
231clear.style.display = query ? 'flex' : 'none';
232Array.prototype.forEach.call(groups, function (group) {
233var groupFound = false;
234var items = group.querySelectorAll('.linksList li');
235Array.prototype.forEach.call(items, function (item) {
236var matches = !query || normalize(item.textContent).indexOf(query) !== -1;
237item.classList.toggle('serviceSearchHidden', !matches);
238if (matches) groupFound = true;
239});
240group.classList.toggle('serviceSearchHidden', !groupFound);
241if (groupFound) found = true;
242});
243empty.hidden = !query || found;
244scheduleMissingQuery(query, found);
245}
246
247input.addEventListener('input', filterServices);
248input.addEventListener('search', filterServices);
249input.addEventListener('keydown', function (event) {
250if (event.key !== 'Enter' || empty.hidden) return;
251var searchText = String(input.value || '').replace(/\s+/g, ' ').trim();
252if (!searchText) return;
253event.preventDefault();
254clearTimeout(logTimer);
255logMissingQuery(normalize(searchText));
256window.location.href = 'https://sinonim.org/search.php?searchid=15355970&text=' + encodeURIComponent(searchText) + '&web=0';
257});
258clear.addEventListener('click', function () {
259input.value = '';
260lastLoggedQuery = '';
261filterServices();
262input.focus();
263});
264filterServices();
265}
266if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', initServicesSearch);
267else initServicesSearch();
268})();
269
270(function () {
271var RECENT_SERVICES_KEY = 'recentServicesV1';
272
273function readRecentServices() {
274try {
275var items = JSON.parse(localStorage.getItem(RECENT_SERVICES_KEY) || '[]');
276items = Array.isArray(items) ? items.filter(function (item) {
277return item && typeof item.href === 'string' && item.href !== '/' && typeof item.title === 'string';
278}).slice(0, 5) : [];
279localStorage.setItem(RECENT_SERVICES_KEY, JSON.stringify(items));
280return items;
281} catch (e) {
282return [];
283}
284}
285
286function getCurrentService() {
287var marker = document.querySelector('.currentRecentServiceData');
288return marker ? {href: marker.getAttribute('data-href'), title: marker.getAttribute('data-title')} : null;
289}
290
291function saveCurrentService(items) {
292var current = getCurrentService();
293if (!current || current.href === '/' || typeof current.href !== 'string' || typeof current.title !== 'string') return items;
294items = items.filter(function (item) { return item.href !== current.href; });
295items.unshift({href: current.href, title: current.title});
296items = items.slice(0, 5);
297try { localStorage.setItem(RECENT_SERVICES_KEY, JSON.stringify(items)); } catch (e) {}
298return items;
299}
300
301function createRecentServicesBlock() {
302var insertBefore = null;
303if (location.pathname === '/') {
304var select = document.getElementById('languageSelect');
305insertBefore = select ? select.parentElement : null;
306} else {
307insertBefore = document.querySelector('h3.collectionAI');
308}
309if (!insertBefore || !insertBefore.parentNode) return null;
310
311var style = document.createElement('style');
312style.textContent = '#recentServices{margin:18px 0 9px;padding:13px 16px;background:linear-gradient(135deg,#f8faff,#f5f3ff);border:1px solid #dfe4ff;border-left:3px solid #7c3aed;border-radius:10px;}#recentServicesTitle{display:flex;align-items:center;gap:8px;margin-bottom:10px;color:#1a1a2e;font-size:16px;font-weight:900;}#recentServicesTitle svg{width:21px;height:21px;color:#6d5ce7;flex:0 0 21px;}#recentServicesList{display:flex;flex-wrap:wrap;gap:8px}.recentServiceLink{display:inline-flex;align-items:center;gap:8px;padding:8px 11px;background:#fff;border:1px solid #e1e5f4;border-radius:9px;color:#334155;text-decoration:none;box-shadow:0 3px 10px rgba(51,65,85,.07);transition:transform .15s ease,border-color .15s ease,box-shadow .15s ease}.recentServiceLink:visited{color:#334155}.recentServiceLink:hover{transform:translateY(-1px);border-color:#a5b4fc;box-shadow:0 6px 15px rgba(79,70,229,.12)}.recentServiceArrow{color:#7c3aed;font-weight:900}';
313document.head.appendChild(style);
314
315var block = document.createElement('section');
316block.id = 'recentServices';
317block.hidden = true;
318block.setAttribute('aria-labelledby', 'recentServicesTitle');
319var title = document.createElement('div');
320title.id = 'recentServicesTitle';
321title.innerHTML = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="9"></circle><path d="M12 7v5l3 2"></path></svg>Недавно использовали';
322var list = document.createElement('div');
323list.id = 'recentServicesList';
324block.appendChild(title);
325block.appendChild(list);
326insertBefore.parentNode.insertBefore(block, insertBefore);
327return block;
328}
329
330function renderRecentServices(items) {
331var current = getCurrentService();
332if (location.pathname !== '/' && current) {
333items = items.filter(function (item) { return item.href !== current.href; });
334}
335if (!items.length) return;
336var block = document.getElementById('recentServices') || createRecentServicesBlock();
337var list = document.getElementById('recentServicesList');
338if (!block || !list) return;
339list.textContent = '';
340items.forEach(function (item) {
341var link = document.createElement('a');
342link.className = 'recentServiceLink';
343link.href = item.href;
344link.appendChild(document.createTextNode(item.title));
345var arrow = document.createElement('span');
346arrow.className = 'recentServiceArrow';
347arrow.setAttribute('aria-hidden', 'true');
348arrow.textContent = '\u279c';
349link.appendChild(arrow);
350list.appendChild(link);
351});
352block.hidden = false;
353}
354
355function initRecentServices() {
356renderRecentServices(saveCurrentService(readRecentServices()));
357}
358
359if (document.readyState === 'loading') {
360document.addEventListener('DOMContentLoaded', initRecentServices);
361} else {
362initRecentServices();
363}
364})();
365var TurndownService=function(){"use strict";function e(e,n){return Array(n+1).join(e)}var n=["ADDRESS","ARTICLE","ASIDE","AUDIO","BLOCKQUOTE","BODY","CANVAS","CENTER","DD","DIR","DIV","DL","DT","FIELDSET","FIGCAPTION","FIGURE","FOOTER","FORM","FRAMESET","H1","H2","H3","H4","H5","H6","HEADER","HGROUP","HR","HTML","ISINDEX","LI","MAIN","MENU","NAV","NOFRAMES","NOSCRIPT","OL","OUTPUT","P","PRE","SECTION","TABLE","TBODY","TD","TFOOT","TH","THEAD","TR","UL"];function t(e){return a(e,n)}var r=["AREA","BASE","BR","COL","COMMAND","EMBED","HR","IMG","INPUT","KEYGEN","LINK","META","PARAM","SOURCE","TRACK","WBR"];function i(e){return a(e,r)}var o=["A","TABLE","THEAD","TBODY","TFOOT","TH","TD","IFRAME","SCRIPT","AUDIO","VIDEO"];function a(e,n){return n.indexOf(e.nodeName)>=0}function l(e,n){return e.getElementsByTagName&&n.some((function(n){return e.getElementsByTagName(n).length}))}var u={};function c(e){return e?e.replace(/(\n+\s*)+/g,"\n"):""}function s(e){for(var n in this.options=e,this._keep=[],this._remove=[],this.blankRule={replacement:e.blankReplacement},this.keepReplacement=e.keepReplacement,this.defaultRule={replacement:e.defaultReplacement},this.array=[],e.rules)this.array.push(e.rules[n])}function f(e,n,t){for(var r=0;r<e.length;r++){var i=e[r];if(d(i,n,t))return i}}function d(e,n,t){var r=e.filter;if("string"==typeof r){if(r===n.nodeName.toLowerCase())return!0}else if(Array.isArray(r)){if(r.indexOf(n.nodeName.toLowerCase())>-1)return!0}else{if("function"!=typeof r)throw new TypeError("`filter` needs to be a string, array, or function");if(r.call(e,n,t))return!0}}function p(e){var n=e.nextSibling||e.parentNode;return e.parentNode.removeChild(e),n}function h(e,n,t){return e&&e.parentNode===n||t(n)?n.nextSibling||n.parentNode:n.firstChild||n.nextSibling||n.parentNode}u.paragraph={filter:"p",replacement:function(e){return"\n\n"+e+"\n\n"}},u.lineBreak={filter:"br",replacement:function(e,n,t){return t.br+"\n"}},u.heading={filter:["h1","h2","h3","h4","h5","h6"],replacement:function(n,t,r){var i=Number(t.
365nodeName.charAt(1));return"setext"===r.headingStyle&&i<3?"\n\n"+n+"\n"+e(1===i?"=":"-",n.length)+"\n\n":"\n\n"+e("#",i)+" "+n+"\n\n"}},u.blockquote={filter:"blockquote",replacement:function(e){return"\n\n"+(e=(e=e.replace(/^\n+|\n+$/g,"")).replace(/^/gm,"> "))+"\n\n"}},u.list={filter:["ul","ol"],replacement:function(e,n){var t=n.parentNode;return"LI"===t.nodeName&&t.lastElementChild===n?"\n"+e:"\n\n"+e+"\n\n"}},u.listItem={filter:"li",replacement:function(e,n,t){e=e.replace(/^\n+/,"").replace(/\n+$/,"\n").replace(/\n/gm,"\n    ");var r=t.bulletListMarker+"   ",i=n.parentNode;if("OL"===i.nodeName){var o=i.getAttribute("start"),a=Array.prototype.indexOf.call(i.children,n);r=(o?Number(o)+a:a+1)+".  "}return r+e+(n.nextSibling&&!/\n$/.test(e)?"\n":"")}},u.indentedCodeBlock={filter:function(e,n){return"indented"===n.codeBlockStyle&&"PRE"===e.nodeName&&e.firstChild&&"CODE"===e.firstChild.nodeName},replacement:function(e,n,t){return"\n\n    "+n.firstChild.textContent.replace(/\n/g,"\n    ")+"\n\n"}},u.fencedCodeBlock={filter:function(e,n){return"fenced"===n.codeBlockStyle&&"PRE"===e.nodeName&&e.firstChild&&"CODE"===e.firstChild.nodeName},replacement:function(n,t,r){for(var i,o=((t.firstChild.getAttribute("class")||"").match(/language-(\S+)/)||[null,""])[1],a=t.firstChild.textContent,l=r.fence.charAt(0),u=3,c=new RegExp("^"+l+"{3,}","gm");i=c.exec(a);)i[0].length>=u&&(u=i[0].length+1);var s=e(l,u);return"\n\n"+s+o+"\n"+a.replace(/\n$/,"")+"\n"+s+"\n\n"}},u.horizontalRule={filter:"hr",replacement:function(e,n,t){return"\n\n"+t.hr+"\n\n"}},u.inlineLink={filter:function(e,n){return"inlined"===n.linkStyle&&"A"===e.nodeName&&e.getAttribute("href")},replacement:function(e,n){var t=n.getAttribute("href");t&&(t=t.replace(/([()])/g,"\\$1"));var r=c(n.getAttribute("title"));return r&&(r=' "'+r.replace(/"/g,'\\"')+'"'),"["+e+"]("+t+r+")"}},u.referenceLink={filter:function(e,n){return"referenced"===n.linkStyle&&"A"===e.nodeName&&e.getAttribute("href")},replacement:function(e,n,t){var r,i,o=n.getAttribute("href"),a=c(n.getAttribute("title"));switch(a&&(a=' "'+a+'"'),t.linkReferenceStyle){case"collapsed":r="["+e+"][]",i="["+e+"]: "+o+a;break;case"shortcut":r="["+e+"]",i="["+e+"]: "+o+a;break;default:var l=this.references.length+1;r="["+e+"]["+l+"]",i="["+l+"]: "+o+a}return this.references.push(i),r},references:[],append:function(e){var n="";return this.references.length&&(n="\n\n"+this.references.join("\n")+"\n\n",this.references=[]),n}},u.emphasis={filter:["em","i"],replacement:function(e,n,t){return e.trim()?t.emDelimiter+e+t.emDelimiter:""}},u.strong={filter:["strong","b"],replacement:function(e,n,t){return e.trim()?t.strongDelimiter+e+t.strongDelimiter:""}},u.code={filter:function(e){var n=e.previousSibling||e.nextSibling,t="PRE"===e.parentNode.nodeName&&!n;return"CODE"===e.nodeName&&!t},replacement:function(e){if(!e)return"";e=e.replace(/\r?\n|\r/g," ");for(var n=/^`|^ .*?[^ ].* $|`$/.test(e)?" ":"",t="`",r=e.match(/`+/gm)||[];-1!==r.indexOf(t);)t+="`";return t+n+e+n+t}},u.image={filter:"img",replacement:function(e,n){var t=c(n.getAttribute("alt")),r=n.getAttribute("src")||"",i=c(n.getAttribute("title"));return r?"!["+t+"]("+r+(i?' "'+i+'"':"")+")":""}},s.prototype={add:function(e,n){this.array.unshift(n)},keep:function(e){this._keep.unshift({filter:e,replacement:this.keepReplacement})},remove:function(e){this._remove.unshift({filter:e,replacement:function(){return""}})},forNode:function(e){return e.isBlank?this.blankRule:(n=f(this.array,e,this.options))||(n=f(this._keep,e,this.options))||(n=f(this._remove,e,this.options))?n:this.defaultRule;var n},forEach:function(e){for(var n=0;n<this.array.length;n++)e(this.array[n],n)}};var g="undefined"!=typeof window?window:{};var m,v,A=function(){var e=g.DOMParser,n=!1;try{(new e).parseFromString("","text/html")&&(n=!0)}catch(e){}return n}()?g.DOMParser:(m=function(){},function(){var e=!1;try{document.implementation.createHTMLDocument("").open()}catch(n){g.ActiveXObject&&(e=!0)}return e}()?m.prototype.parseFromString=function(e){var n=new window.ActiveXObject("htmlfile");return n.designMode="on",n.open(),n.write(e),n.close(),n}:m.prototype.parseFromString=function(e){var n=document.implementation.createHTMLDocument("");return n.open(),n.write(e),n.close(),n},m);function y(e,n){var r;"string"==typeof e?r=(v=v||new A).parseFromString('<x-turndown id="turndown-root">'+e+"</x-turndown>","text/html").getElementById("turndown-root"):r=e.cloneNode(!0);return function(e){var n=e.element,t=e.isBlock,r=e.isVoid,i=e.isPre||function(e){return"PRE"===e.n
365odeName};if(n.firstChild&&!i(n)){for(var o=null,a=!1,l=null,u=h(l,n,i);u!==n;){if(3===u.nodeType||4===u.nodeType){var c=u.data.replace(/[ \r\n\t]+/g," ");if(o&&!/ $/.test(o.data)||a||" "!==c[0]||(c=c.substr(1)),!c){u=p(u);continue}u.data=c,o=u}else{if(1!==u.nodeType){u=p(u);continue}t(u)||"BR"===u.nodeName?(o&&(o.data=o.data.replace(/ $/,"")),o=null,a=!1):r(u)||i(u)?(o=null,a=!0):o&&(a=!1)}var s=h(l,u,i);l=u,u=s}o&&(o.data=o.data.replace(/ $/,""),o.data||p(o))}}({element:r,isBlock:t,isVoid:i,isPre:n.preformattedCode?N:null}),r}function N(e){return"PRE"===e.nodeName||"CODE"===e.nodeName}function E(e,n){return e.isBlock=t(e),e.isCode="CODE"===e.nodeName||e.parentNode.isCode,e.isBlank=function(e){return!i(e)&&!function(e){return a(e,o)}(e)&&/^\s*$/i.test(e.textContent)&&!function(e){return l(e,r)}(e)&&!function(e){return l(e,o)}(e)}(e),e.flankingWhitespace=function(e,n){if(e.isBlock||n.preformattedCode&&e.isCode)return{leading:"",trailing:""};var t=(r=e.textContent,i=r.match(/^(([ \t\r\n]*)(\s*))(?:(?=\S)[\s\S]*\S)?((\s*?)([ \t\r\n]*))$/),{leading:i[1],leadingAscii:i[2],leadingNonAscii:i[3],trailing:i[4],trailingNonAscii:i[5],trailingAscii:i[6]});var r,i;t.leadingAscii&&T("left",e,n)&&(t.leading=t.leadingNonAscii);t.trailingAscii&&T("right",e,n)&&(t.trailing=t.trailingNonAscii);return{leading:t.leading,trailing:t.trailing}}(e,n),e}function T(e,n,r){var i,o,a;return"left"===e?(i=n.previousSibling,o=/ $/):(i=n.nextSibling,o=/^ /),i&&(3===i.nodeType?a=o.test(i.nodeValue):r.preformattedCode&&"CODE"===i.nodeName?a=!1:1!==i.nodeType||t(i)||(a=o.test(i.textContent))),a}var R=Array.prototype.reduce,C=[[/\\/g,"\\\\"],[/\*/g,"\\*"],[/^-/g,"\\-"],[/^\+ /g,"\\+ "],[/^(=+)/g,"\\$1"],[/^(#{1,6}) /g,"\\$1 "],[/`/g,"\\`"],[/^~~~/g,"\\~~~"],[/\[/g,"\\["],[/\]/g,"\\]"],[/^>/g,"\\>"],[/_/g,"\\_"],[/^(\d+)\. /g,"$1\\. "]];function k(e){if(!(this instanceof k))return new k(e);var n={rules:u,headingStyle:"setext",hr:"* * *",bulletListMarker:"*",codeBlockStyle:"indented",fence:"```",emDelimiter:"_",strongDelimiter:"**",linkStyle:"inlined",linkReferenceStyle:"full",br:"  ",preformattedCode:!1,blankReplacement:function(e,n){return n.isBlock?"\n\n":""},keepReplacement:function(e,n){return n.isBlock?"\n\n"+n.outerHTML+"\n\n":n.outerHTML},defaultReplacement:function(e,n){return n.isBlock?"\n\n"+e+"\n\n":e}};this.options=function(e){for(var n=1;n<arguments.length;n++){var t=arguments[n];for(var r in t)t.hasOwnProperty(r)&&(e[r]=t[r])}return e}({},n,e),this.rules=new s(this.options)}function b(e){var n=this;return R.call(e.childNodes,(function(e,t){var r="";return 3===(t=new E(t,n.options)).nodeType?r=t.isCode?t.nodeValue:n.escape(t.nodeValue):1===t.nodeType&&(r=D.call(n,t)),S(e,r)}),"")}function O(e){var n=this;
365return this.rules.forEach((function(t){"function"==typeof t.append&&(e=S(e,t.append(n.options)))})),e.replace(/^[\t\r\n]+/,"").replace(/[\t\r\n\s]+$/,"")}function D(e){var n=this.rules.forNode(e),t=b.call(this,e),r=e.flankingWhitespace;return(r.leading||r.trailing)&&(t=t.trim()),r.leading+n.replacement(t,e,this.options)+r.trailing}function S(e,n){var t=function(e){for(var n=e.length;n>0&&"\n"===e[n-1];)n--;return e.substring(0,n)}(e),r=n.replace(/^\n*/,""),i=Math.max(e.length-t.length,n.length-r.length);return t+"\n\n".substring(0,i)+r}return k.prototype={turndown:function(e){if(!function(e){return null!=e&&("string"==typeof e||e.nodeType&&(1===e.nodeType||9===e.nodeType||11===e.nodeType))}(e))throw new TypeError(e+" is not a string, or an element/document/fragment node.");if(""===e)return"";var n=b.call(this,new y(e,this.options));return O.call(this,n)},use:function(e){if(Array.isArray(e))for(var n=0;n<e.length;n++)this.use(e[n]);else{if("function"!=typeof e)throw new TypeError("plugin must be a Function or an Array of Functions");e(this)}return this},addRule:function(e,n){return this.rules.add(e,n),this},keep:function(e){return this.rules.keep(e),this},remove:function(e){return this.rules.remove(e),this},escape:function(e){return C.reduce((function(e,n){return e.replace(n[0],n[1])}),e)}},k}();
366
367if (location.hostname.endsWith('.')) {
368const newHost = location.hostname.slice(0, -1);
369const newUrl = location.protocol + '//' + newHost + location.pathname + location.search + location.hash;
370location.replace(newUrl);
371}
372
373var micSVG = '<svg style="width: 24px; height: 24px; display: inline-block; vertical-align: middle;" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"><defs><style>.cls-1{fill:#101820;}</style></defs><title/><g data-name="Layer 46" id="Layer_46"><path class="cls-1" d="M16,27A10,10,0,0,1,6,17a1,1,0,0,1,2,0,8,8,0,0,0,16,0,1,1,0,0,1,2,0A10,10,0,0,1,16,27Z"/><path class="cls-1" d="M16,31a1,1,0,0,1-1-1V26a1,1,0,0,1,2,0v4A1,1,0,0,1,16,31Z"/><path class="cls-1" d="M16,23a6,6,0,0,1-6-6V7A6,6,0,0,1,22,7V17A6,6,0,0,1,16,23ZM16,3a4,4,0,0,0-4,4V17a4,4,0,0,0,8,0V7A4,4,0,0,0,16,3Z"/></g></svg>';
374function hideOrShow() {
375var hide = document.getElementById('hideFr').checked;
376var displayType = hide ? 'none' : '';
377var iter = 1;
378var tr = document.getElementById('tr' + iter);
379var as;
380while (tr) {
381as = document.getElementById('as' + iter);
382if (as && as.innerHTML.indexOf(' ') !== -1) {
383tr.style.display = displayType;
384}
385iter++;
386tr = document.getElementById('tr' + iter);
387}
388}
389
390function linksToList(what){
391let iter = 1;
392while(document.getElementById('tr'+iter) !== null){
393iter++;
394}
395const max = iter;
396iter = 1;
397let list = '';
398while(document.getElementById('tr'+iter) !== null){
399if(document.getElementById('tr'+iter).style.display != 'none'){
400if(document.getElementById('as'+iter) !== null) list = list + document.getElementById('as'+iter).innerHTML + ', '
401else list = list + document.getElementById('tr'+iter).innerHTML + ', '
402}
403if(Math.floor(max/2) == (iter) && max > 2) list = list + ' <span style="color:#888">https://sinonim.org/</span> ';
404iter++;
405}
406list = list.replace(/, $/g,'')
407document.getElementById('list-s').innerHTML = '<!--noindex--><h2>'+what+' строкой ('+(iter-1)+')</h2> '+'<div id="strokoy">'+list+'</div><!--/noindex-->';
408if(document.getElementById("naydenoNum") !== null && document.getElementById("naydenoStr") !== null){
409document.getElementById('naydenoNum').innerHTML = (iter-1).toString()
410document.getElementById('naydenoStr').innerHTML = 'всего'
411}
412}
413
414function linksToLi(what){
415let iter = 1;
416while(document.getElementById('tr'+iter) !== null){
417iter++;
418}
419const max = iter;
420iter = 1;
421let list = '<ul>';
422while(document.getElementById('tr'+iter) !== null){
423if(document.getElementById('tr'+iter).style.display != 'none'){
424if(document.getElementById('as'+iter) !== null) list = list + '<li>'+document.getElementById('as'+iter).innerHTML + '</li>'
425}
426if(Math.floor(max/2) == (iter) && max > 2) list = list + ' <span style="color:#888">https://sinonim.org/</span> ';
427iter++;
428}
429list = list + '</ul>';
430document.getElementById('list-s').innerHTML = '<h2>
430'+what+' списком ('+(iter-1)+')</h2> '+'<div id="strokoy">'+list+'</div>';
431}
432
433function sendReq(word,id){
434var request = new XMLHttpRequest();
435request.open('POST', '/zhaloba.php', true);
436request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
437request.send('word='+word);
438if(id != '0'){
439if(document.getElementById('tr'+id).style.display != 'none'){
440document.getElementById('tr'+id).style.display = 'none';
441}
442}
443}
444
445function getHexColor( color ){
446if( color.indexOf('#') != -1 ) return color;
447color = color
448.replace("rgb", "")
449.replace("rgba", "")
450.replace("(", "")
451.replace(")", "");
452color = color.split(",");
453return  "#"
454+ ( '0' + parseInt(color[0], 10).toString(16) ).slice(-2)
455+ ( '0' + parseInt(color[1], 10).toString(16) ).slice(-2)
456+ ( '0' + parseInt(color[2], 10).toString(16) ).slice(-2);
457}
458
459function solveCaptcha() {
460const num1 = Math.floor(Math.random() * 250) + 50;
461const num2 = Math.floor(Math.random() * 250) + 50;
462const userSolution = prompt(`Решите пример: ${num1} + ${num2} = ?`);
463
464// Пользователь нажал "Отмена" → просто выходим без "Неверно"
465if (userSolution === null) {
466return false;
467}
468
469if (parseInt(userSolution.trim(), 10) === num1 + num2) {
470return true;
471} else {
472alert("Неверно");
473return false;
474}
475}
476
477function sendReq2(word,type,id = ''){
478if ((type === 1 && !likeEnabled) || (type === 0 && !dislikeEnabled)) {
479return;
480}
481
482if (type === 1) {
483likeEnabled = false;
484setTimeout(function() { likeEnabled = true; }, likeDelay);
485if(likeDelay < 2500) likeDelay += 300;
486} else if (type === 0) {
487dislikeEnabled = false;
488setTimeout(function() { dislikeEnabled = true; }, dislikeDelay);
489if(dislikeDelay < 2500) dislikeDelay += 300;
490}
491
492var request = new XMLHttpRequest();
493request.open('POST', '/like.php', true);
494request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
495
496colorLike = getHexColor(document.getElementById("like957"+String(id)).style.backgroundColor);
497colorDis = getHexColor(document.getElementById("dislike957"+String(id)).style.backgroundColor);
498likeDig = Number(document.getElementById("likeDig"+String(id)).innerHTML);
499dislikeDig = Number(document.getElementById("dislikeDig"+String(id)).innerHTML);
500
501if(colorLike == '#baffbb') cancelLike = '1';
502else cancelLike = '0';
503
504if(colorDis == '#faddfb') cancelDislike = '1';
505else cancelDislike = '0';
506if(cancelLike == '1' || cancelDislike == '1') cancel = '1';
507else cancel = '0';
508
509allCancel = '0';
510
511if (typeof window.textQSc !== 'undefined'){
512add = '&text='+encodeURIComponent(window.textQSc);
513}
514else add = '';
515
516if(window.location.pathname.startsWith('/p/') && word.includes("_")){
517add = '&text='+word.split("_")[1].split(";")[0];
518}
519
520if ( type === 0 && cancelDislike === '0' && (!word.includes("_") || window.location.pathname.startsWith('/p/')) && !['/po/', '/to/'].some(p => window.location.pathname.startsWith(p)) ){
521const isCaptchaRequired = ['/'].some(path => window.location.pathname.startsWith(path));
522
523if (!isCaptchaRequired || solveCaptcha()) {
524const promptMessage = 'Напишите, в чем проблема (обязательное поле, минимум - 5 слов)?';
525const input = (prompt(promptMessage) || '').trim();
526
527if (input && input.split(' ').length >= 5 && !/([а-яёА-ЯЁ])\1{5,}/.test(input)) {
528sendReq(input + add, '0');
529allCancel = '0';
530} else {
531allCancel = '1';
532}
533} else {
534allCancel = '1';
535}
536}
537
538if(allCancel == '0'){
539if(type == 1){
540if(cancelLike == '0'){
541document.getElementById("like957"+String(id)).style.backgroundColor='#BAFFBB';
542document.getElementById("like957"+String(id)).style.borderColor = "#BAFFBB";
543if(word.includes("_") !== true) document.getElementById("like957"+String(id)).style.borderStyle = "solid";
544document.getElementById("likeDig"+String(id)).innerHTML = ''+(likeDig+1);
545if(cancelDislike == '1'){
546document.getElementById("dislikeDig"+String(id)).innerHTML = ''+(dislikeDig-1);
547document.getElementById("dislike957"+String(id)).style.backgroundColor='#FFF';
548document.getElementById("dislike957"+String(id)).style.borderColor = "#FFF";
549if(word.includes("_") !== true) document.getElementById("dislike957"+String(id)).style.borderStyle = "solid";
550}
551}
552else{
553document.getElementById("like957"+String(id)).style.backgroundColor='#FFF';
554document.getElementById("like957"+String(id)).style.borderColor = "#FFF";
555if(word.includes("_") !== true) document.getElementById("like957"+String(id)).style.borderStyle = "solid";
556document.getElementById("likeDig"+String(id)).innerHTML = ''+(likeDig-1);
557}
558
559}
560
561if(type == 0){
562if(cancelDislike == '0'){
563document.getElementById("dislike957"+String(id)).style.backgroundColor='#FADDFB';
564document.getElementById("dislike957"+String(id)).style.borderColor = "#FADDFB";
565if(word.includes("_") !== true) document.getElementById("dislike957"+String(id)).style.borderStyle = "solid";
566document.getElementById("dislikeDig"+String(id)).innerHTML = ''+(dislikeDig+1);
567if(cancelLike == '1'){
568document.getElementById("likeDig"+String(id)).innerHTML = ''+(likeDig-1);
569document.getElementById("like957"+String(id)).style.backgroundColor='#FFF';
570document.getElementById("like957"+String(id)).style.borderColor = "#FFF";
571if(word.includes("_") !== true) document.getElementById("like957"+String(id)).style.borderStyle = "solid";
572}
573}
574else{
575document.getElementById("dislike957"+String(id)).style.backgroundColor='#FFF';
576document.getElementById("dislike957"+String(id)).style.borderColor = "#FFF";
577if(word.includes("_") !== true) document.getElementById("dislike957"+String(id)).style.borderStyle = "solid";
578document.getElementById("dislikeDig"+String(id)).innerHTML = ''+(dislikeDig-1);
579}
580}
581
582request.send('word='+word+';'+cancelLike+';'+cancelDislike+add);
583}
584}
585
586function printDiv(a) {    
587var divToPrint = document.getElementById(a);
588var popupWin = window.open('', '_blank', 'width=800,height=800');
589popupWin.document.open();
590var style = popupWin.document.createElement('link');
591style.type = "text/css";
592style.rel = "stylesheet";
593style.href = "/css/style.css"; 
594style.media = "all";
595popupWin.document.write('<html><body onload="window.print()"><h2>' +document.title+'</h2>'+divToPrint.innerHTML + '</html>');
596popupWin.document.getElementsByTagName("head")[0].appendChild(style);
597popupWin.document.close();
598}
599
600function downloadWords(){
601content = '<html xmlns:v="urn:schemas-microsoft-com:vml"\n\
602xmlns:o="urn:schemas-microsoft-com:office:office"\n\
603xmlns:w="urn:schemas-microsoft-com:office:word"\n\
604xmlns="http://www.w3.org/TR/REC-html40">\n\
605<head>\n\
606<meta http-equiv=Content-Type content="text/html; charset=utf-8">\n\
607<meta name=ProgId content=Word.Document>\n\
608<meta name=Generator content="Microsoft Word 9">\n\
609<meta name=Originator content="Microsoft Word 9">\n\
610<!--[if !mso]>\n\
611<style>\n\
612v\\:* {behavior:url(#default#VML);}\n\
613o\\:* {behavior:url(#default#VML);}\n\
614w\\:* {behavior:url(#default#VML);}\n\
615.shape {behavior:url(#default#VML);}\n\
616</style>\n\
617<![endif]-->\n\
618<title>'+document.title+' | sinonim.org</title>\n\
619<!--[if gte mso 9]><xml>\n\
620<w:WordDocument>\n\
621<w:View>Print</w:View>\n\
622<w:DoNotHyphenateCaps/>\n\
623<w:PunctuationKerning/>\n\
624<w:DrawingGridHorizontalSpacing>9.35 pt</w:DrawingGridHorizontalSpacing>\n\
625<w:DrawingGridVerticalSpacing>9.35 pt</w:DrawingGridVerticalSpacing>\n\
626</w:WordDocument>\n\
627</xml><![endif]-->\n\
628<style>\n\
629<!--\n\
630/* Font Definitions */\n\
631@font-face\n\
632{font-family:Verdana;\n\
633panose-1:2 11 6 4 3 5 4 4 2 4;\n\
634mso-font-charset:0;\n\
635mso-generic-font-family:swiss;\n\
636mso-font-pitch:variable;\n\
637mso-font-signature:536871559 0 0 0 415 0;}\n\
638/* Style Definitions */\n\
639p.MsoNormal, li.MsoNormal, div.MsoNormal\n\
640{mso-style-parent:"";\n\
641margin:0in;\n\
642margin-bottom:.0001pt;\n\
643mso-pagination:widow-orphan;\n\
644font-size:7.5pt;\n\
645mso-bidi-font-size:8.0pt;\n\
646font-family:"Verdana";\n\
647mso-fareast-font-family:"Verdana";}\n\
648p.small\n\
649{mso-style-parent:"";\n\
650margin:0in;\n\
651margin-bottom:.0001pt;\n\
652mso-pagination:widow-orphan;\n\
653font-size:1.0pt;\n\
654mso-bidi-font-size:1.0pt;\n\
655font-family:"Verdana";\n\
656mso-fareast-font-family:"Verdana";}\n\
657@page Section1\n\
658{size:8.5in 11.0in;\n\
659margin:1.0in 1.25in 1.0in 1.25in;\n\
660mso-header-margin:.5in;\n\
661mso-footer-margin:.5in;\n\
662mso-paper-source:0;}\n\
663div.Section1\n\
664{page:Section1;}\n\
665td{vertical-align: top;}\n\
666-->\n\
667</style>\n\
668<!--[if gte mso 9]><xml>\n\
669<o:shapedefaults v:ext="edit" spidmax="1032">\n\
670<o:colormenu v:ext="edit" strokecolor="none"/>\n\
671</o:shapedefaults></xml><![endif]--><!--[if gte mso 9]><xml>\n\
672<o:shapelayout v:ext="edit">\n\
673<o:idmap v:ext="edit" data="1"/>\n\
674</o:shapelayout></xml><![endif]-->\n\
675<meta http-equiv="content-type" content="text/html; charset=utf-8" />               \n\
676</head>\n\
677<body>\n\
678<html>\n\
679<h2>'+document.title+'</h2>\n\
680<center>\n\
681<table style="width:100%;">\n\
682<tr style="border:none; width:100%;">\n\
683<td style="border:1px solid white;">';
684
685let iter = 1;
686while(document.getElementById('tr'+iter) !== null){
687iter++;
688}
689const max = iter;
690iter = 1;
691while(document.getElementById('tr'+iter) != null){
692if(document.getElementById('tr'+iter).style.display != 'none'){
693content = content + '<span>' + document.getElementById('as'+iter).innerHTML + '</span><br> '
694}
695if(iter/Math.ceil(max/3) === 1 || iter/Math.ceil(max/3) === 2) content = content + '</td><br><td style="border:1px solid white;">';
696iter++;
697}
698content = content + '</td></tr></table></center><br><div>Больше синонимов можно найти на сайте <a href="https://sinonim.org/">https://sinonim.org/</a></div></html></body></html>';
699
700var element = document.createElement('a');
701element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(content));
702if(navigator.platform.indexOf('Win') > -1) element.setAttribute('download', document.title+'.doc');
703else element.setAttribute('download', document.title+'.html');
704element.style.display = 'none';
705document.body.appendChild(element);
706element.click();
707document.body.removeChild(element);
708window.location.href = '#downl';
709return false;
710}
711
712function goToRifme(word){
713url = 'https://rifme.net'+'/r/' + encodeURIComponent(word)+'#frsi';
714window.open(url, '_blank');
715}
716
717function gotonenashli(){
718if (clicked === false) {
719clicked = true;
720location.href = "/switcher.php?ne_nashli";
721}
722}
723
724function cleanText(text) {
725// убираем только HTML, но сохраняем пробелы
726return text.replace(/<[^>]*>/g, '');
727}
728
729function sortList(ulId) {
730const ul = document.getElementById(ulId);
731if (!ul) return;
732
733sortOrder[ulId] = !sortOrder[ulId];
734const items = Array.from(ul.getElementsByTagName("LI"));
735
736items.sort((a, b) => {
737const textA = cleanText(a.textContent).trim();
738const textB = cleanText(b.textContent).trim();
739return sortOrder[ulId]
740? textA.localeCompare(textB)
741: textB.localeCompare(textA);
742});
743
744items.forEach(li => ul.appendChild(li));
745}
746
747function sortListByLength(ulId) {
748const ul = document.getElementById(ulId);
749if (!ul) return;
750
751lengthSortOrder[ulId] = !lengthSortOrder[ulId];
752const items = Array.from(ul.getElementsByTagName("LI"));
753
754items.sort((a, b) => {
755const lenA = cleanText(a.textContent).length;
756const lenB = cleanText(b.textContent).length;
757return lengthSortOrder[ulId]
758? lenA - lenB
759: lenB - lenA;
760});
761
762items.forEach(li => ul.appendChild(li));
763}
764
765function sortclick() {
766if (document.getElementById("assocPodryad") !== null) sortList('assocPodryad');
767if (document.getElementById("assocPodryad2") !== null) sortList('assocPodryad2');
768}
769
770function sortByLengthClick() {
771if (document.getElementById("assocPodryad") !== null) sortListByLength('assocPodryad');
772if (document.getElementById("assocPodryad2") !== null) sortListByLength('assocPodryad2');
773}
774
775function speak(text) {
776if (counter >= 85) {
777alert('Вы дошли до '+counter+'-го нажатия на кнопку произношения. Обновите страницу, чтобы снова включить произношение.');
778return;
779}
780
781var speechQuery = '?speechYa&token=c4497b740b8650d2b1aa7c460adc3b99&word='+encodeURI(text);
782var audio = new Audio('https://a.sinonim.org/data.php'+speechQuery);
783var speechFallbackStarted = false;
784var speechFallbackTimer;
785
786function stopSpeechFallbackTimer() {
787clearTimeout(speechFallbackTimer);
788}
789
790function startSpeechFallback() {
791if (speechFallbackStarted) return;
792speechFallbackStarted = true;
793stopSpeechFallbackTimer();
794audio.removeEventListener('error', startSpeechFallback);
795audio.src = '/data.php'+speechQuery;
796audio.load();
797var fallbackPlay = audio.play();
798if (fallbackPlay && typeof fallbackPlay.catch === 'function') fallbackPlay.catch(function() {});
799}
800
801audio.addEventListener('canplay', stopSpeechFallbackTimer, {once:true});
802audio.addEventListener('playing', stopSpeechFallbackTimer, {once:true});
803audio.addEventListener('error', startSpeechFallback, {once:true});
804speechFallbackTimer = setTimeout(startSpeechFallback, 4000);
805var primaryPlay = audio.play();
806if (primaryPlay && typeof primaryPlay.catch === 'function') primaryPlay.catch(function() {});
807counter++;
808}
809
810function speakG(text,lang = '') {
811if (counter >= 85) {
812alert('Вы дошли до ' + counter + '-го нажатия на кнопку произношения. Обновите страницу, чтобы снова включить произношение.');
813return;
814}
815
816fetch('/data.php?speechG&token=c4497b740b8650d2b1aa7c460adc3b99', {
817method: 'POST',
818headers: {
819'Content-Type': 'application/x-www-form-urlencoded'
820},
821body: 'word=' + encodeURIComponent(text)
822})
823.then(response => response.blob())
824.then(blob => {
825let audio = new Audio(URL.createObjectURL(blob));
826audio.play();
827counter++;
828})
829.catch(error => console.error('Ошибка при загрузке аудио:', error));
830}
831
832function speakText(text,lang) {
833const speech = new SpeechSynthesisUtterance(text);
834speech.lang = lang;
835speech.rate = 0.90;
836window.speechSynthesis.speak(speech);
837}
838
839function getCookieValue(cookieName) {
840const cookies = document.cookie.split('; ');
841const cookieEntry = cookies.find(row => row.startsWith(`${cookieName}=`));
842return cookieEntry ? cookieEntry.split('=')[1] : null;
843}
844
845function sortWords() {
846const container = document.getElementById('wordsContainer');
847const headers = Array.from(container.getElementsByClassName('slovaiz'));
848
849headers.forEach(header => {
850let nextElement = header.nextElementSibling;
851let allWords = [];
852let divContainers = [];
853
854// Собираем все слова и div контейнеры
855while (nextElement && nextElement.tagName === 'DIV') {
856let words = Array.from(nextElement.getElementsByClassName('riLi'));
857allWords = allWords.concat(words);
858divContainers.push(nextElement);
859nextElement = nextElement.nextElementSibling;
860}
861
862// Сортируем все собранные слова
863allWords.sort((a, b) => a.textContent.localeCompare(b.textContent, 'ru'));
864
865// Очищаем div контейнеры
866divContainers.forEach(div => {
867while (div.firstChild) {
868div.removeChild(div.firstChild);
869}
870});
871
872// Распределяем отсортированные слова обратно по div контейнерам
873let currentDiv = 0;
874allWords.forEach((word, index) => {
875if (divContainers[currentDiv] && divContainers[currentDiv].children.length >= divContainers[currentDiv].dataset.maxWords) {
876currentDiv++;
877}
878if (divContainers[currentDiv]) {
879divContainers[currentDiv].appendChild(word);
880divContainers[currentDiv].appendChild(document.createTextNode(' '));
881}
882});
883});
884}
885
886function sortListLength() {
887var ulPred, listData, allItems, i, switching, b, shouldSwitch;
888
889// Поиск элементов в DOM
890ulPred = document.querySelector(".ulPred");
891listData = document.querySelector(".listData");
892
893allItems = [];
894
895if (ulPred) {
896var ulPredList = ulPred.getElementsByTagName("LI");
897allItems = allItems.concat(Array.from(ulPredList));
898}
899
900if (listData) {
901var listDataList = listData.getElementsByTagName("LI");
902allItems = allItems.concat(Array.from(listDataList));
903}
904
905if (allItems.length === 0) {
906console.error("Оба списка не найдены!");
907return;
908}
909
910switching = true;
911while (switching) {
912switching = false;
913b = allItems;
914for (i = 0; i < (b.length - 1); i++) {
915shouldSwitch = false;
916if (b[i].innerHTML.length > b[i + 1].innerHTML.length) {
917shouldSwitch = true;
918break;
919}
920}
921if (shouldSwitch) {
922b.splice(i, 0, b.splice(i + 1, 1)[0]);
923switching = true;
924}
925}
926
927if (ulPred) {
928ulPred.innerHTML = '';
929allItems.forEach(function(li) {
930ulPred.appendChild(li);
931});
932}
933}
934
935function checkIfElementReached() {
936const el = document.querySelector('.ocenite');
937if (!el) return;
938if (el.getBoundingClientRect().top <= window.innerHeight) {
939getWords(wordForGetWords, sectionForGetWords, chtoForGetWords);
940window.removeEventListener('scroll', checkIfElementReached);
941}
942}
943
944function redirectToLanguage() {
945const languageSelect = document.getElementById("languageSelect");
946
947if (languageSelect !== null) {
948const selectedLang = languageSelect.value;
949
950if (selectedLang) {
951const selectedText = languageSelect.options[languageSelect.selectedIndex].textContent;
952const confirmation = confirm(
953window.location.href.includes('perevod')
954? `Поменять язык на ${selectedText}?`
955: `Поменять режим на ${selectedText}?`
956);
957
958if (confirmation) {
959previousLanguage = selectedLang;
960window.location.href = `/${selectedLang}#change`;
961} else {
962languageSelect.value = previousLanguage;
963}
964}
965}
966}
967
968function toggleText(event) {
969event.preventDefault();
970if(document.getElementById("sourceText") !== null && document.getElementById("toggleLink2") !== null){
971let textBlock = document.getElementById('sourceText');
972let link = document.getElementById('toggleLink2');
973
974if (textBlock.style.display === 'none' || textBlock.style.display === '') {
975textBlock.style.display = 'block';
976link.textContent = 'Скрыть введённый вами текст';
977} else {
978textBlock.style.display = 'none';
979link.textContent = 'Показать введённый вами текст';
980}
981}
982}
983
984function smoothScrollTo(el, offset = 40) {
985const targetY = Math.max(0, Math.round(window.pageYOffset + el.getBoundingClientRect().top - offset));
986// Скроллим только вверх (чтобы не дёргать, если и так выше)
987if (targetY < window.pageYOffset - 2) {
988window.scrollTo({ top: targetY, behavior: 'smooth' });
989}
990}
991
992if(window.location.pathname !== '/' && window.yaContextCb){
993if(!window.location.href.includes("/po/") && !window.location.href.includes("/to/") && (!window.location.href.includes("perevod") || window.isPR)){
994if (window.yaContextCb) {
995window.yaContextCb.push(() => {
996Ya.Context.AdvManager.render({
997"blockId": "R-A-1834941-10",
998"type": "fullscreen",
999"platform": "touch"
1000})
1001})
1002}
1003if(window.yaContextCb){
1004window.yaContextCb.push(() => {
1005Ya.Context.AdvManager.render({
1006"blockId": "R-A-1834941-15",
1007"type": "fullscreen",
1008"platform": "desktop"
1009})
1010})
1011}
1012if(window.yaContextCb){
1013window.yaContextCb.push(() => {
1014Ya.Context.AdvManager.render({
1015"blockId": "R-A-1834941-17",
1016"type": "floorAd",
1017"platform": "desktop"
1018})
1019})
1020}
1021}
1022if (window.innerWidth >= 1250){
1023window.yaContextCb.push(()=>{Ya.Context.AdvManager.render({"blockId": "R-A-1834941-11","renderTo": "yandex_rtb_R-A-1834941-11"})})
1024var adAdded = false;
1025window.addEventListener('scroll', function() {
1026var isb1 = document.getElementById('inSidebar');
1027var isb2 = document.getElementById('inSidebar2');
1028var scrollPosition = window.scrollY || document.documentElement.scrollTop;
1029if(scrollPosition<=2500){
1030isb1.style.position = 'absolute';
1031isb1.style.top = scrollPosition+'px';
1032}
1033if(!adAdded && scrollPosition > 2500){
1034isb2.innerHTML = '<div id="yandex_rtb_R-A-1834941-12" style="max-width: 160px;max-height: 600px;"></div>';
1035isb2.style.cssText = "border:1px solid #e2e2e2;position:fixed;top:20px";
1036if (window.yaContextCb) {
1037window.yaContextCb.push(()=>{
1038Ya.Context.AdvManager.render({"blockId": "R-A-1834941-12","renderTo": "yandex_rtb_R-A-1834941-12"});
1039});
1040}
1041adAdded = true;
1042}
1043if(scrollPosition<=(2500+646)){
1044isb2.style.position = 'absolute';
1045isb2.style.top = scrollPosition+(646-(scrollPosition-2500))+'px';
1046}
1047else{
1048isb2.style.position = 'absolute';
1049isb2.style.top = scrollPosition+'px';
1050}
1051});
1052}
1053else if(isAcceptLangRu === true && window.location.pathname !== '/'){
1054if(!window.location.href.includes("perevod") && !window.location.href.includes("/po/") && !window.location.href.includes("/to/") && typeof window.qvantOn !== 'undefined'){
1055window.JVC = window.JVC || [];
1056window.JVC.push("M_adhesion");
1057}
1058}
1059}
1060
1061function pluralForm(n, form1, form2, form5) {
1062n = Math.abs(n) % 100;
1063var n1 = n % 10;
1064if (n > 10 && n < 20) return form5;
1065if (n1 > 1 && n1 < 5) return form2;
1066if (n1 == 1) return form1;
1067return form5;
1068}
1069
1070function htmlspecialchars(str) {
1071if (!str) return str;
1072return str
1073.replace(/&/g, '&amp;')
1074.replace(/</g, '&lt;')
1075.replace(/>/g, '&gt;')
1076.replace(/"/g, '&quot;')
1077.replace(/'/g, '&#039;');
1078}
1079
1080async function copyListData() {
1081const listDataElement = document.querySelector('.listData');
1082if (!listDataElement) return;
1083
1084const textContent = listDataElement.innerText;
1085
1086try {
1087await navigator.clipboard.writeText(textContent);
1088
1089const copyMessage = document.getElementById('copyMessage');
1090copyMessage.innerHTML = '&nbsp; &#9989; Скопировано';
1091copyMessage.style.display = 'inline';
1092
1093setTimeout(() => {
1094copyMessage.style.display = 'none';
1095}, 1500);
1096} catch (err) {
1097alert('Не удалось скопировать: ' + err);
1098}
1099}
1100
1101function downloadListData(format) {
1102const listDataDiv = document.querySelector('.listData');
1103if (!listDataDiv) {
1104console.error('Элемент .listData не найден');
1105return;
1106}
1107
1108// Динамическая подгрузка html-docx, если ещё не загружен
1109function loadDocxScript(cb) {
1110if (window.htmlDocx) return cb();
1111
1112const s = document.createElement('script');
1113s.src = "/js/html-docx.min.js";
1114s.onload = cb;
1115document.head.appendChild(s);
1116}
1117
1118const makeFile = () => {
1119let styleSyntax = '';
1120if (location.href.includes('/syntax') || location.href.includes('/members')) {
1121styleSyntax = `
1122<link rel="stylesheet" href="https://sinonim.org/css/style.css">
1123<link rel="stylesheet" href="https://sinonim.org/css/syntax.css">`;
1124}
1125
1126const htmlContent = `<!DOCTYPE html><html lang="ru"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>sinonim.org export</title>${styleSyntax}<style>body{font-family:Arial,sans-serif;padding:10px;line-height:1.6;
1126overflow-wrap: anywhere;}a{color:#007BFF;text-decoration:none;}a:hover{text-decoration:underline;}</style></head><body>${listDataDiv.outerHTML}<p>Источник: <a href="${location.href}" target="_blank">${location.href}</a></p></body></html>`;
1127
1128if (format === 'docx') {
1129const blob = window.htmlDocx.asBlob(htmlContent);
1130const a = document.createElement('a');
1131a.href = URL.createObjectURL(blob);
1132a.download = 'sinonim.org.docx';
1133a.click();
1134return;
1135}
1136
1137const blob = new Blob([htmlContent], { type: 'text/html;charset=utf-8' });
1138const a = document.createElement('a');
1139a.href = URL.createObjectURL(blob);
1140a.download = 'sinonim.org.html';
1141a.click();
1142};
1143
1144format === 'docx' ? loadDocxScript(makeFile) : makeFile();
1145}
1146
1147
1148function getShareText () {
1149const el = document.querySelector('.listData');
1150if (!el) {
1151console.error('Элемент с классом .listData не найден.');
1152return 'нет текста';
1153}
1154return encodeURIComponent(el ? el.innerText.trim() + "\n" + window.location.href : '');
1155}
1156
1157function shareTelegram () {
1158const txt = getShareText();
1159window.open(`tg://msg?text=${txt}`, '_blank');
1160}
1161
1162function shareEmail () {
1163const body = getShareText();
1164window.location.href = `mailto:?body=${body}`;
1165}
1166
1167function shareWhatsApp () {
1168const txt = getShareText();
1169window.open(`https://wa.me/?text=${txt}`, '_blank');
1170}
1171
1172const turndownService = new TurndownService({
1173codeBlockStyle: 'fenced',
1174hr: '---',
1175bulletListMarker: '*',
1176headingStyle: 'atx'
1177});
1178
1179// Кастомные правила
1180turndownService.addRule('sup', {
1181filter: 'sup',
1182replacement: content => `^${content}^`
1183});
1184
1185turndownService.addRule('combinedBoldItalic', {
1186filter: node => 
1187node.nodeName === 'B' && node.children.length === 1 && node.children[0].nodeName === 'I' ||
1188node.nodeName === 'I' && node.children.length === 1 && node.children[0].nodeName === 'B',
1189replacement: content => `***${content}***`
1190});
1191
1192turndownService.addRule('tables', {
1193filter: 'table',
1194replacement: (content, node) => {
1195const rows = [];
1196const headerCells = node.querySelectorAll('thead th');
1197
1198if(headerCells.length > 0) {
1199const headerRow = Array.from(headerCells)
1200.map(cell => cell.textContent.trim().replace(/\n/g, ' ')).join(' | ');
1201rows.push(`| ${headerRow} |`);
1202
1203const separator = Array.from(headerCells)
1204.map(() => '---').join(' | ');
1205rows.push(`| ${separator} |`);
1206}
1207
1208const bodyRows = node.querySelectorAll('tbody tr');
1209bodyRows.forEach(tr => {
1210const cells = Array.from(tr.querySelectorAll('td'))
1211.map(cell => cell.textContent.trim().replace(/\n/g, ' '));
1212rows.push(`| ${cells.join(' | ')} |`);
1213});
1214
1215return rows.join('\n') + '\n\n';
1216}
1217});
1218
1219turndownService
1220.remove('style')
1221.remove('script')
1222.keep(['pre', 'code']);
1223
1224window.htmlToStars = function(html) {
1225let markdown = turndownService.turndown(html);
1226
1227return markdown
1228.replace(/&nbsp;/gi, ' ')
1229.replace(/&lt;/gi, '<')
1230.replace(/&gt;/gi, '>')
1231.replace(/&amp;/gi, '&');
1232};
1233
1234function appendTextServiceFileToken(formData){
1235var tokenInput = document.getElementById('textServiceFileToken');
1236var token = tokenInput ? String(tokenInput.value || '').trim() : '';
1237if(!token && typeof window.textServiceFileToken === 'string') token = window.textServiceFileToken.trim();
1238if(token) formData.append('textServiceFileToken',token);
1239}
1240
1241function continueText(word,sectionForGetWords,drugie){
1242document.getElementById('continueText').innerHTML = `<h2 id="wait">
1243<img src="https://sinonim.org/img/loading-128.gif" alt="Загрузка" width="18" height="18">
1244Генерируем продолжение, подождите 7 секунд...</h2>`;
1245var formData = new FormData();
1246formData.append('word', word);
1247formData.append('messages', htmlToStars(document.querySelector('.listData').innerHTML));
1248appendTextServiceFileToken(formData);
1249document.getElementById('buttonContinue').disabled = true;
1250if (typeof window.numContinue === "undefined") {
1251window.numContinue = 0;
1252}
1253fetch('/data.php?getData=' + sectionForGetWords + '&numContinue='+(++window.numContinue)+'&token=' + gwToken+'&drugie=' + drugie.toString(), {
1254method: 'POST',
1255body: formData
1256})
1257.then(response => response.text())
1258.then(data => {
1259document.getElementById('wait').remove();
1260if (data) {
1261let limitContinue;
1262if (sectionForGetWords === 'referat') {
1263limitContinue = 8;
1264} else {
1265limitContinue = 6;
1266}
1267if(window.numContinue < limitContinue){
1268document.getElementById('buttonContinue').disabled = false;
1269} else {
1270document.getElementById('buttonContinue').textContent = 'Достигнут лимит количества символов';
1271}
1272document.querySelector('.listData').innerHTML += "<br>"+data;
1273if (typeof renderMath === 'function') {
1274renderMath();
1275}
1276}
1277})
1278.catch(error => console.error('Ошибка запроса:', error));
1279}
1280
1281function reviseText(word, sectionForGetWords){
1282window.reviseWord = word;
1283window.reviseSection = sectionForGetWords;
1284
1285document.getElementById('reviseBlock').style.display = 'block';
1286document.getElementById('revise').style.display = 'none';
1287document.getElementById('reviseInput').focus();
1288}
1289
1290function submitRevise() {
1291const userPrompt = document.getElementById('reviseInput').value.trim();
1292if (!userPrompt) return;
1293
1294const word = window.reviseWord;
1295const sectionForGetWords = window.reviseSection;
1296
1297document.getElementById('reviseBlock').style.display = 'none';
1298document.getElementById('reviseInput').value = '';
1299
1300document.getElementById('genSP').insertAdjacentHTML(
1301'beforebegin',
1302'<h2 id="wait"><img src="https://sinonim.org/img/loading-128.gif" alt="Загрузка" width="18" height="18"> Идёт доработка, подождите 7 секунд</h2>'
1303);
1304
1305const formData = new FormData();
1306formData.append('word', word);
1307formData.append('messages', htmlToStars(document.querySelector('.listData').innerHTML));
1308formData.append('revisePrompt', userPrompt);
1309appendTextServiceFileToken(formData);
1310
1311if (document.getElementById("zaprosov")) {
1312document.getElementById("zaprosov").scrollIntoView({ behavior: "smooth" });
1313}
1314
1315fetch('/data.php?getData=' + sectionForGetWords + '&token=' + gwToken, {
1316method: 'POST',
1317body: formData
1318})
1319.then(r => r.text())
1320.then(data => {
1321var waitEl = document.getElementById('wait');
1322if (waitEl) {
1323waitEl.remove();
1324}
1325if (!data) return;
1326
1327const hasNeedReg = /class\s*=\s*(['"])[^'"]*\bneedReg\b[^'"]*\1|class\s*=\s*needReg\b/i.test(data);
1328const btnContinue = document.getElementById('buttonContinue');
1329const btnRevise = document.getElementById('revise');
1330const btnDru = document.getElementById('drugoyText');
1331[btnContinue, btnRevise, btnDru].forEach(b => { if (b) b.disabled = !!hasNeedReg; });
1332document.querySelectorAll('.res5426').forEach(el => { el.style.display = 'block'; if (!hasNeedReg) el.innerHTML = ''; });
1333document.querySelectorAll('.listData').forEach(el => { el.style.display = 'block'; if (!hasNeedReg) el.innerHTML = ''; });
1334
1335document.getElementById('revise').style.display = 'block';
1336
1337const list = document.querySelector('.listData');
1338const prefix = '<p style="display:none;">' + userPrompt + '</p>';
1339
1340if (hasNeedReg) {
1341list.insertAdjacentHTML('afterbegin', prefix + data);
1342} else {
1343list.innerHTML = prefix + data;
1344}
1345
1346if (typeof renderMath === 'function') renderMath();
1347})
1348.catch(err => console.error('Ошибка запроса:', err));
1349}
1350
1351function getWords(word,section,chto,drugie=0) {
1352if(drugie == 1){
1353if(section !== 'as' && section !== 'kb'){
1354document.querySelectorAll('.listData').forEach(el => el.innerHTML = '');
1355if(section !== 'idea' && section !== 'kb' && section !== 's' && section !== 't'){
1356document.querySelectorAll('.res5426').forEach(function(el) {
1357el.style.display = 'none';
1358});
1359if(document.getElementById("speak4357") !== null) document.getElementById('speak4357').remove();
1360if(document.getElementById("buttonContinue") !== null) document.getElementById('buttonContinue').remove();
1361if(document.getElementById("zaprosov") !== null){ document.getElementById("zaprosov").scrollIntoView({ behavior: "smooth" });}
1362}
1363}
1364if(section == 'as' || section == 'kb'){
1365document.querySelector('.ulForList').style.display = 'none';
1366}
1367document.querySelector('.upd78945').style.display = 'none';
1368}
1369document.body.style.cursor = 'wait';
1370
1371var genSButton = document.getElementById('genS');
1372document.getElementById('genSP').insertAdjacentHTML('beforebegin', '<h2 id="wait"><img src="https://sinonim.org/img/loading-128.gif" alt="Загрузка" width="18" height="18"> Идёт поиск, подождите 7 секунд</h2>');
1373genSButton.disabled = true;
1374genSButton.innerHTML = 'Ожидаем результат... &#129302;';
1375
1376var formData = new FormData();
1377formData.append('word', word);
1378appendTextServiceFileToken(formData);
1379
1380var existingAnchors = {};
1381if(section == 's' || section == 'a' || section == 'as' || section == 'kb'){
1382document.querySelectorAll('a[id^="as"], a.alink, a.riLi, a.neprBolshe').forEach(function(link) {
1383existingAnchors[link.textContent.toLowerCase().replace(/ё/g, 'е')] = true;
1384});
1385}
1386
1387fetch('/data.php?getData='+section+'&token='+gwToken+'&drugie='+drugie.toString(), {
1388method: 'POST',
1389body: formData
1390})
1391.then(response => response.text().then(data => {
1392if(!response.ok) throw new Error(data || 'Не удалось получить ответ нейросети. Попробуйте ещё раз через несколько секунд.');
1393return data;
1394}))
1395.then(data => {
1396document.body.style.cursor = 'initial';
1397genSButton.innerHTML = '&#129302;';
1398genSButton.style.display = "none";
1399document.getElementById('wait').remove();
1400if(data) {
1401const sections = ['sin', 'gen', 'perevod','sokr','sources','soch','title','article','reviews','punctuation','orfo','dream','addText','report','referat','gram','naming','story','solver','improve','fanfiction','questions','post','product','conclusion','pozdr','stih','song','nickname','syntax','members','essence','tests','taro','natal','code','unique','summary','detector','password','prompt'];
1402if(section !== 'p' && section !== 't' && !sections.includes(section.replace(/_[a-zA-Z]{2,3}$/, ''))){
1403var toList = 'Найденные '+chto;
1404if(section !== 'kb') var toList = document.title;
1405if(section == 'kb') var toList = '';
1406if(section == 's' && document.title.indexOf('+ слов') !== -1) var toList = '';
1407if(section != 'a' && section != 'as' && section != 's' && section != 'kb') var list = '<h2 class="listData">'+toList+' &#129302;</h2>';
1408else var list = '';
1409if(section == 's' || section == 'a') var spisok = 'ul';
1410else spisok = 'ul';
1411if(section !== 'kb' && section !== 'as') var list = list + '<'+spisok+' class="listData">';
1412else var list = list + '<ul class="assocPodryad ulForList" id="assocPodryad">';
1413
1414if(section == 's' || section == 'a'){
1415iter = 1;
1416while(document.getElementById('tr'+iter) !== null){
1417iter++;
1418}
1419iter--;
1420minIter = iter;
1421}
1422var iterData = 0;
1423data.split('; ').forEach(function(wordInList) {
1424wordInListTrim = wordInList.trim();
1425if (!existingAnchors.hasOwnProperty(wordInListTrim.toLowerCase().replace(/ё/g, 'е'))) {
1426if(section != 'idea'){
1427wordInListUri = encodeURIComponent(wordInList.trim());
1428addIter = '';
1429addIter2 = '';
1430if(section == 'a' || section == 's'){
1431addIter = ' id="tr'+(++iter)+'"';
1432addIter2 = ' id="as'+(iter)+'"';
1433}
1434list += `<li><span${addIter}><a${addIter2} class="alink" href="/${section}/${wordInListUri}">${wordInListTrim}</a></span></li> `;
1435}
1436else list += `<li>${wordInList}</li> `;
1437iterData++;
1438}
1439});
1440
1441if(section == 's' && iter !== undefined && minIter !== undefined && document.getElementById("numData") !== null && document.getElementById("numDataText") !== null){
1442var countList = (iter-minIter)+parseInt(document.getElementById('numData').innerHTML);
1443document.getElementById('numData').innerHTML = countList.toString()
1444document.getElementById('numDataText').innerHTML = (pluralForm(countList,'синоним','синонима','синонимов'))
1445}
1446
1447if(section == 's' && iter !== undefined && minIter !== undefined && document.getElementById("numData2") !== null && document.getElementById("numDataText2") !== null){
1448var countList = (iter-minIter)+parseInt(document.getElementById('numData2').innerHTML);
1449document.getElementById('numData2').innerHTML = countList.toString()
1450document.getElementById('numDataText2').innerHTML = (pluralForm(countList,'синоним','синонима','синонимов'))
1451}
1452
1453list += '</'+spisok+'>';
1454setTimeout(() => {
1455if(section == 'a' && minIter != 0) linksToList('Антонимы');
1456if(section == 's' && minIter != 0) linksToList('Синонимы');
1457}, 2000);
1458}
1459else{
1460if(section == 'p'){
1461var list = '<ul class="listData">';
1462data.match(/[^.!?]+[.!?]?/g).forEach(function(wordInList) {
1463list += `<li>${wordInList.trim()}</li> `;
1464});
1465list += '</ul>';
1466}
1467if(section == 't' || sections.includes(section.replace(/_[a-zA-Z]{2,3}$/, ''))){
1468var emoji129302 = '&#129302; '
1469if(sections.includes(section.replace(/_[a-zA-Z]{2,3}$/, ''))) emoji129302 = '';
1470var list = '<div class="listData" style="margin-bottom:12px">'+emoji129302+data+'</div>';
1471}
1472}
1473if(drugie == 0){
1474var drugieHTML = `<button id="drugoyText" class="buttonSpeak buttonMargins upd78945 resultActionBtn resultActionRedo" onclick="getWords(wordForGetWords,\'${section}\',\'${chto}\',1)">Переделать</button> `;
1475if (!['punctuation', 'orfo', 'gram', 'syntax', 'members','perevod_av','i
1475dea','a','s'].includes(section)){
1476drugieHTML = drugieHTML+` <button class="buttonSpeak buttonMargins resultActionBtn resultActionRevise" id="revise" onclick="reviseText(wordForGetWords, sectionForGetWords)">Доработать</button> <div id="reviseBlock" style="display:none;margin:10px 0;">
1477<textarea id="reviseInput" placeholder="Как доработать?" maxlength="5000"
1478style="width:97%;resize:vertical;background-color: #fbfbfb;font-size:100%;"></textarea>
1479<br>
1480<button onclick="submitRevise()" class="buttonSpeak buttonMargins resultActionBtn resultActionRevise">
1481Доработать
1482</button>
1483</div>`;
1484}
1485if(section == 'kb') drugieHTML = '<p>' + drugieHTML + '</p>';
1486if(section == 'punctuation' || section == 'orfo' || section == 'gram') drugieHTML = '<p><label><input type="checkbox" onchange="document.querySelectorAll(\'.greenBG\').forEach(el=>el.style.backgroundColor=this.checked?\'#d4fcdc\':\'#fff\')" checked="">  Подсветить различия</label></p>'+drugieHTML;
1487}
1488else var drugieHTML = '';
1489
1490if((section == 'syntax' || section == 'members') && word.length>100) drugieHTML = '';
1491
1492buttonCopy = '';
1493rezultat = '';
1494if(sections.includes(section.replace(/_[a-zA-Z]{2,3}$/, '')) && drugie == 0){
1495sendResult = '<div class="shareResult"><span class="shareResultLabel">Отправить результат:</span><div class="shareResultButtons"><button class="shareResultBtn shareResultTelegram" onclick="shareTelegram()" title="Отправить в Telegram" aria-label="Отправить в Telegram"></button>' +
1496'<button class="shareResultBtn shareResultEmail" onclick="shareEmail()" title="Отправить по Email" aria-label="Отправить по Email"></button>' +
1497'<button class="shareResultBtn shareResultWhatsApp" onclick="shareWhatsApp()" title="Отправить в WhatsApp" aria-label="Отправить в WhatsApp"></button></div></div>';
1498if(section == 'syntax' || section == 'members') sendResult = '';
1499let docxHTML = '';
1500if (navigator.userAgent.indexOf('Windows') !== -1) {
1501docxHTML = '<button class="buttonSpeak buttonMargins resultActionBtn resultActionDocx" onclick="downloadListData(\'docx\')">.docx</button>';
1502}
1503buttonCopy = ' <button id="buttonCopy" class="buttonSpeak buttonMargins resultActionBtn resultActionCopy" onclick="copyListData()" title="Скопировать результат в буфер обмена">Скопировать</button> <div id="copyMessage" style="display: none; font-weight: bold; margin-top: 10px;"></div> <button class="buttonSpeak buttonMargins resultActionBtn resultActionHtml" onclick="downloadListData(\'html\')">Скачать .html</button> ' + docxHTML + sendResult + '<div class="newQueryInlineWrap"><a href="#" class="newQueryInline" onclick="const t = document.getElementById(\'text_si\'); t.value = \'\'; t.focus(); t.scrollIntoView({ behavior: \'smooth\', block: \'center\' }); return false;"><span class="newQueryBtn__icon" aria-hidden="true"></span><span class="newQueryBtn__text">Сделать новый запрос</span></a></div><div style="margin-top:12px"><a href="#" class="newQueryBtn" onclick="const t = document.getElementById(\'text_si\'); t.value = \'\'; t.focus(); t.scrollIntoView({ behavior: \'smooth\', block: \'center\' }); return false;"><span class="newQueryBtn__icon" aria-hidden="true"></span><span class="newQueryBtn__text">Сделать новый запрос</span></a></div>';
1504rezultat = '<h2 class="res5426">Результат:</h2>';
1505}
1506if (['gen', 'sources', 'soch', 'title', 'article', 'reviews', 'dream', 'addText', 'report', 'referat', 'naming', 'story', 'solver','improve','fanfiction','questions','post','product','conclusion','pozdr','stih','song','sin','nickname','punctuation','gram','orfo','tests','taro','natal','code','unique','summary','detector','prompt'].includes(section)){
1507buttonContinue = `<div id="continueText"></div> 
1508<button id="buttonContinue" class="buttonSpeak buttonMargins resultActionBtn resultActionContinue" style="margin-bottom: 10px;" onclick="continueText(wordForGetWords, sectionForGetWords, ${drugie})">Продолжить текст</button> `;
1509}
1510else buttonContinue = '';
1511var listLength = (html => Object.assign(document.createElement('div'), {innerHTML: html}).textContent)(list).length;
1512if(section.startsWith('perevod') && listLength<255 && listLength>0){
1513const langMap = {
1514'perevod': 'en-US',
1515'perevod_es': 'es-ES',
1516'perevod_fr': 'fr-FR',
1517'perevod_de': 'de-DE',
1518'perevod_it': 'it-IT',
1519'perevod_ja': 'ja-JP',
1520'perevod_ko': 'ko-KR',
1521'perevod_zh': 'zh-CN',
1522'perevod_pt': 'pt-BR',
1523'perevod_hi': 'hi-IN',
1524'perevod_nl': 'nl-NL',
1525'perevod_pl': 'pl-PL',
1526'perevod_id': 'id-ID'
1527};
1528if(!Object.keys(langMap).includes(section.replace('_ru',''))){
1529buttonSpeak4386 = ` <input id="speak4357" class="buttonSpeak buttonMargins resultActionBtn resultActionListen" type="button" value="Прослушать" title="прослушать результат" onclick="this.disabled = true; (/[а-яё]/i.test(document.querySelector('.listData').innerText) ? speakG : speakG)(document.querySelector('.listData').innerText); setTimeout(() => this.disabled = false, 2000);">`;
1530}
1531else{
1532currentLang = langMap[section.replace('_ru','')];
1533buttonSpeak4386 = ` <input id="speak4357" class="buttonSpeak buttonMargins resultActionBtn resultActionListen" type="button" value="Прослушать" title="прослушать результат" onclick="this.disabled = true; forTranlate = document.querySelector('.listData').innerText; notRus = /[a-z]/i.test(document.querySelector('.listData').innerText) || !/[а-яё]/i.test(forTranlate); if(notRus) forTranlate = forTranlate.replace(/[а-яА-ЯёЁ]/g, ''); (notRus ? speakText : speakG)(forTranlate,currentLang); setTimeout(() => this.disabled = false, 2000);"> `;
1534}
1535}
1536else buttonSpeak4386 = '';
1537const adDivs = '<div id="yandex_rtb_R-A-1834941-5"></div><div id="yandex_rtb_R-A-1834941-6"></div>';
1538if (!(section == 's' && iterData == 0)) {
1539// Удаляем старые рекламные блоки если существуют
1540['yandex_rtb_R-A-1834941-5', 'yandex_rtb_R-A-1834941-6'].forEach(id => {
1541const adElement = document.getElementById(id);
1542if (adElement) adElement.remove();
1543});
1544
1545// Формируем HTML с новыми рекламными блоками
1546let yandexAdHTML;
1547if(section !== 'idea' && section !== 'as' && section != 'p'){
1548yandexAdHTML = `<div id="yandex_rtb_R-A-1834941-5"></div><div id="yandex_rtb_R-A-1834941-6"></div>`;
1549}
1550else{
1551yandexAdHTML = ``;
1552}
1553
1554// Собираем полный HTML для вставки
1555const fullContent = rezultat + list+yandexAdHTML + buttonContinue + drugieHTML + buttonSpeak4386 + buttonCopy;
1556
1557// Вставляем собранный контент
1558genSButton.insertAdjacentHTML('afterend', fullContent);
1559
1560const script = document.createElement('script');
1561script.textContent = `
1562if(document.getElementsByTagName("body")[0].offsetWidth<=600){
1563window.yaContextCb.push(() => {
1564Ya.Context.AdvManager.render({
1565renderTo: 'yandex_rtb_R-A-1834941-5',
1566blockId: 'R-A-1834941-5'
1567})
1568})
1569} else {
1570window.yaContextCb.push(() => {
1571Ya.Context.AdvManager.render({
1572renderTo: 'yandex_rtb_R-A-1834941-6',
1573blockId: 'R-A-1834941-6'
1574})
1575})
1576}
1577`;
1578if(section !== 'idea' && section !== 'as' && window.yaContextCb) document.body.appendChild(script);
1579}
1580if (typeof renderMath === 'function') {
1581renderMath();
1582}
1583} else {
1584if(chto != 'новые синонимы' && chto != 'новые антонимы' && chto != 'новые прилагательные' && chto != 'новые ассоциации' && chto != 'новые предложения' && chto != 'новое определение'){
1585genSButton.insertAdjacentHTML('afterend', '<p>Не смогли найти '+chto+'.</p>');
1586fetch('/data.php?error', {
1587method: 'POST',
1588headers: {
1589'Content-Type': 'application/x-www-form-urlencoded'
1590},
1591body: new URLSearchParams({ word })
1592}).then(r => r.text());
1593}
1594}
1595})
1596.catch(error => {
1597console.error('Ошибка:', error);
1598document.body.style.cursor = 'initial';
1599genSButton.innerHTML = '&#129302;';
1600genSButton.disabled = false;
1601var waitMessage = document.getElementById('wait');
1602if(waitMessage) waitMessage.remove();
1603var previousError = document.querySelector('.generationRequestError');
1604if(previousError) previousError.remove();
1605var errorMessage = document.createElement('p');
1606errorMessage.className = 'generationRequestError';
1607errorMessage.textContent = error.message || 'Не удалось получить ответ нейросети. Попробуйте ещё раз через несколько секунд.';
1608genSButton.insertAdjacentElement('afterend', errorMessage);
1609});
1610}
1611
1612document.addEventListener("DOMContentLoaded", function () {
1613if (window.location.href.includes("sinonim.org/perevod")) {
1614const spans = document.querySelectorAll("form div span");
1615const formPerevod = document.querySelector("form");
1616let hiddenInput = document.createElement("input");
1617hiddenInput.type = "hidden";
1618hiddenInput.name = "ru";
1619formPerevod.appendChild(hiddenInput);
1620
1621spans.forEach(span => {
1622const text = span.textContent.trim();
1623
1624// фильтруем только нужные варианты
1625if (
1626!text.endsWith("русский") &&
1627!text.startsWith("русский") &&
1628!text.includes("Автоматически")
1629) {
1630return;
1631}
1632
1633span.style.cursor = "pointer";
1634
1635span.addEventListener("click", function () {
1636// снимаем выделение со всех
1637spans.forEach(s => {
1638const sText = s.textContent.trim();
1639if (
1640sText.endsWith("русский") ||
1641sText.startsWith("русский") ||
1642sText.includes("Автоматически")
1643) {
1644s.style.background = "#007bff";
1645s.style.color = "#fff";
1646}
1647});
1648
1649// выделяем выбранный
1650this.style.background = "#0056b3";
1651this.style.color = "#fff";
1652
1653const selectedText = this.textContent.trim();
1654let selectedDirection = "";
1655
1656if (selectedText.endsWith("русский")) {
1657selectedDirection = "1";
1658} else if (selectedText.startsWith("русский")) {
1659selectedDirection = "0";
1660}
1661
1662hiddenInput.value = selectedDirection;
1663});
1664});
1665}
1666else{
1667const storageKey = `text_si_content_`+location.pathname;
1668const textareaPerevod = document.querySelector('textarea[id="text_si"]');
1669if (textareaPerevod) {
1670const saved = localStorage.getItem(storageKey);
1671if (saved) {
1672textareaPerevod.value = saved;
1673}
1674}
1675}
1676
1677const formLetter = document.querySelector('.formLetter');
1678const btn  = formLetter ? formLetter.querySelector('input[type="submit"]') : null;
1679
1680if (formLetter && btn) {
1681formLetter.addEventListener('submit', function () {
1682btn.disabled = true;
1683
1684setTimeout(() => {
1685btn.disabled = false;
1686}, 4000);
1687});
1688}
1689
1690const btnGen = document.getElementById('buttonSend');
1691if (btnGen) {
1692const form = btnGen.closest('form');
1693if (form) {
1694form.addEventListener('submit', function () {
1695btnGen.disabled = true;
1696btnGen.style.opacity = '0.4';
1697
1698setTimeout(() => {
1699btnGen.disabled = false;
1700btnGen.style.opacity = '';
1701}, 5000);
1702});
1703}
1704}
1705
1706const button = document.getElementById('buttonSend');
1707if (button) {
1708button.addEventListener('click', function(e) {
1709const textarea = document.getElementById('text_si');
1710if (textarea) {
1711textarea.value = textarea.value.trim();
1712if(window.location.href.includes('/syntax') || window.location.href.includes('/members') || window.location.href.includes('/sr/')) {
1713if(!textarea.value.endsWith('.') && !textarea.value.endsWith('!') && !textarea.value.endsWith('?') && !textarea.value.endsWith('…') && !textarea.value.endsWith('"')  && !textarea.value.endsWith('»') && textarea.value != '' && textarea.value.length < 300){
1714textarea.value += '.';
1715}
1716textarea.value = textarea.value.replace(/ ,/g, ',');
1717textarea.value = textarea.value.replace(/(\p{L}),(\p{L})/gu, '$1, $2');
1718textarea.value = textarea.value.charAt(0).toUpperCase() + textarea.value.slice(1);
1719}
1720if (window.location.href.includes('/dream') || window.location.href.includes('/pozdr')) {
1721textarea.value = textarea.value.replace(/\s{2,}/g, ' ');
1722}
1723}
1724});
1725}
1726
1727document.addEventListener('keydown', function(event) {
1728const isSlash = event.key === '/';
1729const isRusLetter = /^[а-яё]$/i.test(event.key);
1730
1731if (
1732(isSlash || isRusLetter) &&
1733!(event.ctrlKey || event.altKey || event.metaKey) &&
1734!(document.activeElement instanceof HTMLInputElement || document.activeElement instanceof HTMLTextAreaElement)
1735) {
1736const inputElem = document.querySelector('input[type="text"], textarea');
1737
1738if (inputElem) {
1739event.preventDefault();
1740inputElem.focus();
1741inputElem.scrollIntoView({ behavior: 'smooth', block: 'center' });
1742
1743// Если нажата русская буква — сразу вставим её в поле
1744if (isRusLetter) {
1745inputElem.value += event.key;
1746}
1747}
1748}
1749});
1750
1751if(document.getElementById("text_si") !== null){
1752document.getElementById("text_si").addEventListener("input", function () {
1753const textArea = this.value.trim();
1754const button = document.getElementById("buttonSend");
1755const pattern = /[а-я][А-Я]/;
1756
1757if (textArea.length === 4 && pattern.test(textArea)) {
1758if (button) {
1759button.disabled = true;
1760}
1761} else {
1762if (button) {
1763button.disabled = false;
1764}
1765}
1766});
1767
1768if(window.location.href.includes("sinonim.org/perevod")){
1769let isTyped = false;
1770let isTranslated = false;
1771const inputField = document.getElementById("text_si");
1772const translateButton = document.getElementById("buttonSend");
1773
1774inputField.addEventListener("input", function () {
1775isTyped = true;
1776});
1777
1778translateButton.addEventListener("click", function () {
1779isTranslated = true;
1780});
1781
1782window.addEventListener("beforeunload", function () {
1783if (isTyped && !isTranslated) {
1784let text = inputField.value.trim();
1785if (text.length > 0) {
1786let data = new FormData();
1787data.append("word", text);
1788
1789navigator.sendBeacon('/cron-sin.php?log_untranslated', data);
1790}
1791}
1792});
1793}
1794}
1795document.querySelectorAll('.unit').forEach(el => {
1796let node = el.firstChild;
1797while (node && node.nodeType !== 3) node = node.nextSibling;
1798
1799if (!node) return;
1800
1801const s = node.nodeValue;
1802const m = s.match(/^(\s*)(\d+(?:[.)])?)(\s*)/);
1803if (!m) return;
1804
1805const before = m[1] || '';
1806const num    = m[2] || '';
1807const after  = s.slice((m[1]+m[2]+m[3]).length);
1808const midsp  = m[3] || '';
1809
1810el.classList.add('starts-num');
1811
1812const frag = document.createDocumentFragment();
1813if (before) frag.appendChild(document.createTextNode(before));
1814
1815const span = document.createElement('span');
1816span.className = 'firstnum';
1817span.textContent = num;
1818frag.appendChild(span);
1819
1820if (midsp) frag.appendChild(document.createTextNode(midsp));
1821frag.appendChild(document.createTextNode(after));
1822
1823el.replaceChild(frag, node);
1824});
1825
1826(function(){
1827var label = document.querySelector('.proModeLabel');
1828if(!label) return;
1829
1830var btn = label.querySelector('.proTipBtn');
1831var tip = label.querySelector('.proTip');
1832if(!btn || !tip) return;
1833
1834// берём текст из title, чтобы не дублировать
1835var t = (label.getAttribute('title') || '').trim();
1836if(!t) t = 'Подсказка недоступна.';
1837tip.textContent = t;
1838
1839// (опционально) чтобы не было системного "title" на десктопе при наведении
1840// label.removeAttribute('title');
1841
1842function closeTip(){
1843label.classList.remove('proTipOpen');
1844btn.setAttribute('aria-expanded','false');
1845}
1846
1847btn.addEventListener('click', function(e){
1848e.preventDefault();
1849e.stopPropagation();
1850var open = label.classList.toggle('proTipOpen');
1851btn.setAttribute('aria-expanded', open ? 'true' : 'false');
1852});
1853
1854document.addEventListener('click', function(e){
1855if(!label.contains(e.target)) closeTip();
1856});
1857
1858document.addEventListener('keydown', function(e){
1859if(e.key === 'Escape') closeTip();
1860});
1861})();
1862
1863(function(){
1864var cb = document.querySelector('.ProModeInput');
1865var ta = document.getElementById('text_si');
1866if(!cb || !ta) return;
1867
1868var wrap = ta.parentElement;
1869if(wrap) wrap.classList.add('textareaProWrap');
1870
1871function sync(){
1872ta.classList.toggle('isPro', cb.checked);
1873if(wrap) wrap.classList.toggle('isPro', cb.checked);
1874}
1875
1876cb.addEventListener('change', sync);
1877sync();
1878})();
1879});
1880
1881if (!location.pathname.includes('perevod')) {
1882window.addEventListener('beforeunload', () => {
1883const textareaPerevod = document.querySelector('textarea[id="text_si"]');
1884if (textareaPerevod && textareaPerevod.value.trim() !== '') {
1885localStorage.setItem(`text_si_content_`+location.pathname, textareaPerevod.value);
1886}
1887});
1888}
1889
1890function updateRazryad(word, mode = 's', section = 'mg') {
1891fetch(`/data.php?mode=${mode}&${section}&word=${encodeURIComponent(word)}`, {cache: 'no-store'})
1892.then(r => r.text())
1893.then(text => {
1894const razryadElement = document.querySelector('.razryad'+mode.toUpperCase());
1895razryadElement.textContent = text.trim();
1896
1897// Находим ближайший родительский элемент с классом priznVnutri
1898const priznVnutri = razryadElement.closest('.priznVnutri');
1899if (priznVnutri) {
1900// Получаем весь текст внутри priznVnutri
1901const fullText = priznVnutri.textContent.trim();
1902if (fullText.startsWith(',')) {
1903// Ищем первый текстовый узел в priznVnutri
1904const textNode = findFirstTextNode(priznVnutri);
1905if (textNode) {
1906textNode.textContent = textNode.textContent.replace(/^,/, '');
1907}
1908}
1909}
1910})
1911.catch(err => console.error('Ошибка запроса:', err));
1912}
1913
1914// Рекурсивная функция для поиска первого текстового узла
1915function findFirstTextNode(element) {
1916for (let node of element.childNodes) {
1917if (node.nodeType === Node.TEXT_NODE && node.textContent.trim() !== '') {
1918return node;
1919}
1920if (node.nodeType === Node.ELEMENT_NODE) {
1921const found = findFirstTextNode(node);
1922if (found) return found;
1923}
1924}
1925return null;
1926}
1927
1928document.addEventListener('DOMContentLoaded', function(){
1929var isOperaMini = (navigator.userAgent.indexOf('Opera Mini') > -1);
1930var isOperaMobi = (navigator.userAgent.indexOf('Opera Mobi') > -1);
1931if (!isOperaMini && !isOperaMobi) {
1932const formConfigs = [
1933{ id: "submitShow", input: "text", baseUrl: "/", redirectUrl: "/s/", replaceYo: true },
1934{ id: "submitShow2", input: "text2", baseUrl: "/", redirectUrl: "/s/", replaceYo: true },
1935{ id: "submitShow3", input: "text3", baseUrl: "/p", redirectUrl: "/p/", replaceYo: true },
1936{ id: "submitShow3_2", input: "text3_2", baseUrl: "/p", redirectUrl: "/p/", replaceYo: true },
1937{ id: "submitShow4", input: "text4", baseUrl: "/a", redirectUrl: "/a/", replaceYo: true },
1938{ id: "submitShow5", input: "text5", baseUrl: "/a", redirectUrl: "/a/", replaceYo: true },
1939{ id: "submitShow6", input: "text6", baseUrl: "/as", redirectUrl: "/as/", replaceYo: true },
1940{ id: "submitShow7", input: "text7", baseUrl: "/as", redirectUrl: "/as/", replaceYo: true },
1941{ id: "submitShow8", input: "text8", baseUrl: "/mo", redirectUrl: "/mo/", replaceYo: true },
1942{ id: "submitShow9", input: "text9", baseUrl: "/f", redirectUrl: "/f/", replaceYo: false },
1943{ id: "submitShow10", input: "text10", baseUrl: "/f", redirectUrl: "/f/", replaceYo: false },
1944{ id: "submitShow11", input: "text11", baseUrl: "/t", redirectUrl: "/t/", replaceYo: false, pattern: /[,]$/ },
1945{ id: "submitShow12", input: "text12", baseUrl: "/t", redirectUrl: "/t/", replaceYo: false, pattern: /[,]$/ },
1946{ id: "submitShow16", input: "text16", baseUrl: "/kb", redirectUrl: "/kb/", replaceYo: true },
1947{ id: "submitShow17", input: "text17", baseUrl: "/kb", redirectUrl: "/kb/", replaceYo: true },
1948{ id: "submitShow18", input: "text18", baseUrl: "/mg", redirectUrl: "/mg/", replaceYo: false },
1949{ id: "submitShow19", input: "text19", baseUrl: "/kp", redirectUrl: "/kp/", replaceYo: false }
1950];
1951
1952const processWord = (value, config) => {
1953let word = value.toLowerCase().trim();
1954if (config.replaceYo) word = word.replace(/ё/g, 'е');
1955word = word.replace(/\//g, '').replace(/\\/g, '');
1956word = word.replace(config.pattern || /[\.,]$/g, '');
1957return word;
1958};
1959
1960// Функция для блокировки кнопки на 3 секунды
1961const disableButton = (form) => {
1962const submitButton = form.querySelector('button[type="submit"], input[type="submit"]');
1963if (submitButton) {
1964submitButton.disabled = true;
1965setTimeout(() => {
1966submitButton.disabled = false;
1967}, 3000);
1968}
1969};
1970
1971const createSubmitHandler = (config) => function(event) {
1972// Блокируем кнопку
1973disableButton(event.target);
1974
1975const input = document.getElementById(config.input);
1976if (input.value === '') {
1977window.location.href = config.baseUrl;
1978return false;
1979}
1980
1981const word = processWord(input.value, config);
1982
1983window.location.href = `${config.redirectUrl}${encodeURIComponent(word)}`;
1984return false;
1985};
1986
1987formConfigs.forEach(config => {
1988const form = document.getElementById(config.id);
1989if (form) form.onsubmit = createSubmitHandler(config);
1990});
1991
1992// Специальный обработчик для submitShow15
1993if (document.getElementById("submitShow15")) {
1994document.getElementById("submitShow15").onsubmit = function(event) {
1995// Блокируем кнопку
1996disableButton(event.target);
1997
1998const conditions = !document.querySelector('input[name="can_repeat"]').checked &&
1999!document.querySelector('input[name="nouns"]').checked &&
2000document.querySelector('select[name="num"]').value === '100' &&
2001document.querySelector('input[name="pattern"]').value === '' &&
2002document.querySelector('input[name="letters"]').value === '' &&
2003document.querySelector('input[name="exclude"]').value === '';
2004
2005if (conditions && !/[a-z]/i.test(document.getElementById("text15").value)) {
2006const word = document.getElementById("text15").value.toLowerCase().trim()
2007.replace(/\//g, '').replace(/\\/g, '').replace(/[,]$/g, '');
2008window.location.href = '/bu/' + encodeURIComponent(word);
2009return false;
2010}
2011};
2012}
2013}
2014});
2015
2016document.addEventListener('DOMContentLoaded', function() {
2017if(typeof wordForSpeak !== "undefined"){
2018const h1Element = document.querySelector("h1");
2019if (h1Element){ const h1Text = h1Element.innerText; }
2020let speakButton = document.getElementById("h1SpeakButton");
2021if(!speakButton){
2022speakButton = document.createElement("span");
2023speakButton.className = "buttonSpeak";
2024speakButton.innerHTML = " &#x1F50A";
2025if (h1Element){ h1Element.appendChild(speakButton); }
2026}
2027speakButton.title = "Произнести «"+wordForSpeak+"»";
2028speakButton.addEventListener('click', function() {
2029// Делаем кнопку неактивной
2030speakButton.style.pointerEvents = "none";
2031speakButton.style.opacity = "0.5";
2032
2033speak(wordForSpeak);
2034
2035// Включаем кнопку обратно через 2.5 секунды
2036setTimeout(() => {
2037speakButton.style.pointerEvents = "auto";
2038speakButton.style.opacity = "1";
2039}, 1500);
2040});
2041let favoriteButton = document.getElementById("h1FavoriteButton");
2042if(!favoriteButton){
2043favoriteButton = document.createElement("span");
2044favoriteButton.innerHTML = " &#11088;";
2045favoriteButton.className = "buttonFavorite";
2046if (h1Element){ h1Element.appendChild(favoriteButton); }
2047}
2048favoriteButton.title = "Добавить «" + wordForSpeak + "» в избранные слова";
2049favoriteButton.style.filter = "grayscale(100%)";
2050favoriteButton.addEventListener("click", function() {
2051if (favoriteButton.classList.contains("selected")) {
2052favoriteButton.classList.remove("selected");
2053favoriteButton.innerHTML = " &#11088;";
2054favoriteButton.style.filter = "grayscale(100%)";
2055var request = new XMLHttpRequest();
2056request.open('POST', '/addWord.php', true);
2057request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
2058request.send('deleteWord='+wordForSpeak);
2059}
2060else {
2061favoriteButton.classList.add("selected");
2062favoriteButton.innerHTML = " &#11088;";
2063if (navigator.onLine) {
2064var request = new XMLHttpRequest();
2065request.open('POST', '/addWord.php', true);
2066request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
2067request.send('word='+wordForSpeak);
2068request.addEventListener('load', function() {
2069if (request.status === 200) {
2070if (request.responseText === 'unauthorized') {
2071const addToFavoritesPrompt = confirm('Чтобы добавить слово в избранное, нужно зарегистрироваться. Перейти Ð
2071º регистрации?');
2072if (addToFavoritesPrompt) {
2073window.location.href = '/reg';
2074}
2075}
2076else{
2077favoriteButton.style.filter = "";
2078}
2079}
2080});
2081}
2082else alert("Нет сети, слово не добавлено");
2083}
2084});
2085}
2086
2087if(window.location.pathname.indexOf("/bu/") === 0){
2088var classname=document.getElementsByClassName("riLi");
2089var ondblclickS=function(){
2090window.open('https://sinonim.org/t/'+encodeURIComponent(this.dataset.w), '_blank');
2091};
2092for(var i=0;i<classname.length;i++){ classname[i].addEventListener('click',ondblclickS,false);	}
2093}
2094
2095if(window.location.pathname.indexOf("/kb/") === 0){
2096var classname=document.getElementsByClassName("riLi");
2097var ondblclickS=function(){
2098window.open('https://sinonim.org/kb/'+encodeURIComponent(this.dataset.w), '_blank');
2099};
2100for(var i=0;i<classname.length;i++){ classname[i].addEventListener('click',ondblclickS,false);	}
2101}
2102
2103if (window.webkitSpeechRecognition && window.location.href.startsWith('https://') && /iPhone/i.test(navigator.userAgent) === false && !window.location.href.includes('chat-gpt') && !window.location.href.includes('/gemini')){
2104// Расширяем селектор для textarea
2105document.querySelectorAll('input[type="text"], textarea').forEach((input) => {
2106if (["username", "word2", "exclude", "letters", "pattern"].includes(input.name)) {
2107return;
2108}
2109
2110const inputWidth = input.offsetWidth;
2111const inputHeight = input.offsetHeight;
2112if (input.tagName === 'INPUT') {
2113input.style.boxSizing = 'border-box';
2114if (inputWidth > 0) input.style.width = inputWidth + 'px';
2115input.style.paddingLeft = '36px';
2116}
2117
2118const wrapper = document.createElement('div');
2119wrapper.style.position = 'relative';
2120if(input.name != 'text_si') wrapper.style.display = 'inline-flex';
2121var micstylebottom = (input.name == 'text_si') ? '18px' : '12px';
2122wrapper.style.alignItems = 'center';
2123
2124// Улучшенная обработка размеров для textarea
2125if (inputWidth > 0 || inputHeight > 0) {
2126wrapper.style.width = inputWidth + 'px';
2127wrapper.style.minHeight = inputHeight + 'px';
2128}
2129
2130const mic = document.createElement('div');
2131mic.innerHTML = micSVG;
2132mic.classList.add('mic-icon');
2133mic.style.position = 'absolute';
2134mic.style.left = '8px';
2135mic.style.bottom = micstylebottom;
2136mic.style.cursor = 'pointer';
2137mic.style.fontSize = '32px';
2138mic.style.zIndex = '100';
2139if (input.tagName === 'TEXTAREA'){
2140mic.style.left = '8px';
2141mic.style.bottom = micstylebottom;
2142mic.style.opacity = '62%';
2143}
2144// Переносим стандартные стили textarea в wrapper
2145if (input.tagName === 'TEXTAREA') {
2146wrapper.style.whiteSpace = 'normal';
2147}
2148
2149input.parentNode.insertBefore(wrapper, input);
2150wrapper.appendChild(input);
2151wrapper.appendChild(mic);
2152
2153const recognition = new window.webkitSpeechRecognition();
2154recognition.lang = 'ru-RU';
2155recognition.continuous = false;
2156
2157let isListening = false;
2158let recognitionTimer;
2159mic.addEventListener('click', () => {
2160clearTimeout(recognitionTimer);
2161if (isListening) {
2162recognition.stop();
2163mic.innerHTML = micSVG;
2164mic.style.fontSize = '32px';
2165mic.style.width = null;
2166mic.style.height = null;
2167mic.style.background = null;
2168mic.style.borderRadius = null;
2169mic.style.left = '8px';
2170mic.style.bottom = micstylebottom;
2171isListening = false;
2172return;
2173} else {
2174let isiPhone = /iPhone/i.test(navigator.userAgent);
2175recognition.start();
2176recognitionTimer = setTimeout(() => { 
2177recognition.stop(); 
2178}, isiPhone ? 4500 : 9000);
2179mic.innerHTML = '';
2180mic.style.display = 'inline-block';
2181mic.style.width = '18px';
2182mic.style.height = '18px';
2183mic.style.background = 'red';
2184mic.style.borderRadius = '50%';
2185mic.style.left = '11px';
2186mic.style.bottom = micstylebottom;
2187isListening = true;
2188}
2189});
2190
2191recognition.onresult = function(event) {
2192input.value = event.results[0][0].transcript;
2193mic.innerHTML = micSVG;
2194mic.style.fontSize = '32px';
2195mic.style.left = '8px';
2196mic.style.bottom = micstylebottom;
2197mic.style.width = null;
2198mic.style.height = null;
2199mic.style.background = null;
2200mic.style.borderRadius = null;
2201isListening = false;
2202};
2203
2204recognition.onend = function() {
2205if (isListening) {
2206mic.innerHTML = micSVG;
2207mic.style.fontSize = '32px';
2208mic.style.left = '8px';
2209mic.style.bottom = micstylebottom;
2210mic.style.width = null;
2211mic.style.height = null;
2212mic.style.background = null;
2213mic.style.borderRadius = null;
2214isListening = false;
2215}
2216};
2217});
2218}
2219
2220// Получаем все поля ввода
2221let inputFields = document.querySelectorAll('input[type="text"]');
2222inputFields.forEach(function(inputField) {
2223// Добавляем относительное позиционирование к полю ввода
2224inputField.style.position = 'relative';
2225
2226// Создаем элемент для крестика
2227let closeButton = document.createElement('span');
2228
2229// Добавляем текст и стиль крестика
2230
2231if(document.location.href == 'https://sinonim.org/myWords' || document.location.href == 'https://sinonim.org/reg'){
2232closeButton.textContent = '';
2233}
2234else{
2235closeButton.textContent = '×';
2236}
2237
2238closeButton.style.cssText = `
2239position: absolute;
2240cursor: pointer;
2241visibility: hidden;
2242top: 50%;
2243right: 10px;
2244transform: translateY(-50%);
2245font-size: 27px;
2246padding: 0 4px;
2247`;
2248
2249// Добавляем крестик в поле ввода
2250inputField.parentNode.insertBefore(closeButton, inputField.nextSibling);
2251
2252// Показываем крестик, когда в поле ввода есть текст
2253inputField.addEventListener('input', function() {
2254closeButton.style.visibility = this.value ? 'visible' : 'hidden';
2255});
2256
2257inputField.addEventListener('focus', function() {
2258closeButton.style.visibility = this.value ? 'visible' : 'hidden';
2259});
2260
2261// Очищаем поле ввода, скрываем крестик и устанавливаем фокус на поле при клике на крестик
2262closeButton.addEventListener('click', function() {
2263inputField.value = '';
2264closeButton.style.visibility = 'hidden';
2265inputField.focus();
2266});
2267});
2268
2269let currentSuggestionIndex = -1;
2270let suggestionTimers = {};
2271let lastSuggestionQueries = {};
2272
2273function getSuggestionWord(id) {
2274var input = document.getElementById(id);
2275return input ? input.value.toLowerCase().replace(/^[\s\v\x00]+/, '') : '';
2276}
2277
2278function scheduleSug(id, section, formId) {
2279clearTimeout(suggestionTimers[id]);
2280var word = getSuggestionWord(id);
2281
2282if(word.length < 1){
2283lastSuggestionQueries[id] = '';
2284if(document.getElementById("suggestions")) document.getElementById("suggestions").style.display = "none";
2285currentSuggestionIndex = -1;
2286return;
2287}
2288
2289suggestionTimers[id] = setTimeout(function() {
2290if(getSuggestionWord(id) !== word || lastSuggestionQueries[id] === word) return;
2291lastSuggestionQueries[id] = word;
2292getSug(id, section, formId);
2293}, 200);
2294}
2295
2296function hideSuggestions() {
2297hideTimeout = setTimeout(() => {
2298if(document.getElementById("suggestions")) document.getElementById("suggestions").style.display = "none";
2299currentSuggestionIndex = -1;
2300}, 300);
2301}
2302function getSug(id,section,formId){
2303currentSuggestionIndex = -1;
2304var word = getSuggestionWord(id);
2305if(word.length < 1){
2306if(document.getElementById("suggestions")) document.getElementById("suggestions").style.display = "none";
2307return '';
2308}
2309var data = 'word='+encodeURIComponent(word)+'&section='+section;
2310
2311function showSuggestions(responseText) {
2312
2313var searchElement = document.getElementById(id);
2314var suggestionsBox = document.getElementById("suggestions");
2315if (!suggestionsBox){
2316suggestionsBox = document.createElement('div');
2317suggestionsBox.id = 'suggestions';
2318}
2319document.getElementById(formId).insertAdjacentElement('afterend', suggestionsBox);
2320suggestionsBox.innerHTML = '';
2321suggestionsBox.style.width = (parseInt(getComputedStyle(searchElement).width) + 36) + 'px';
2322
2323var input = document.getElementById(id).value.toLowerCase();
2324if (!input) {
2325suggestionsBox.style.display = "none";
2326return;
2327}
2328var items = responseText.split('\n');
2329items.forEach(item => {
2330if (item.toLowerCase().length > 2) {
2331let div = document.createElement('div');
2332div.className = 'suggestion';
2333div.innerHTML = item;
2334div.addEventListener('click', function() {
2335document.getElementById(id).value = this.textContent;
2336suggestionsBox.style.display = "none";
2337var request = new XMLHttpRequest();
2338request.open('POST', '/sent.php', true);
2339request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
2340request.send('word='+encodeURIComponent(this.textContent.replace(/^[\s\v\x00]+/, '')));
2341if(section != 'sc' && section != 'bu'){
2342window.location.href = '/' + section + '/' + encodeURIComponent(this.textContent.trim()) + '#sug';
2343}
2344else if(section == 'sc'){
2345if(parseInt(document.querySelector('select[name="num"]').value)>1 && parseInt(document.querySelector('select[name="num"]').value)<32){
2346var selectedNum = '&num=' + document.querySelector('select[name="num"]').value
2347}
2348else{
2349var selectedNum = '';
2350}
2351window.location.href = '/' + section + '?text=' + encodeURIComponent(this.textContent.trim()) + selectedNum + '#sug';
2352}
2353else if(section == 'bu'){
2354if(document.querySelector('select[name="num"]').value === '100' && !document.querySelector('input[name="can_repeat"]').checked && !document.querySelector('input[name="nouns"]').checked && document.querySelector('input[name="letters"]').value.trim() == "" && document.querySelector('input[name="exclude"]').value.trim() == "" && document.querySelector('input[name="pattern"]').value.trim() == "") window.location.href = '/' + section + '/' + encodeURIComponent(this.textContent.trim()) + '#sug';
2355}
2356});
2357suggestionsBox.appendChild(div);
2358}
2359});
2360// Показываем подсказки только когда данные пришли с сервера
2361suggestionsBox.style.display = "block";
2362}
2363
2364function sendSuggestionsRequest(url, useFallback) {
2365var request = new XMLHttpRequest();
2366var requestFinished = false;
2367
2368function requestFailed() {
2369if (requestFinished) return;
2370if (getSuggestionWord(id) !== word) {
2371requestFinished = true;
2372return;
2373}
2374requestFinished = true;
2375if (useFallback) sendSuggestionsRequest('/sug.php', false);
2376}
2377
2378request.open('POST', url, true);
2379request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
2380if (useFallback) request.timeout = 3000;
2381request.onload = function() {
2382if (requestFinished) return;
2383if (getSuggestionWord(id) !== word) {
2384requestFinished = true;
2385return;
2386}
2387if (request.status == 200) {
2388requestFinished = true;
2389showSuggestions(request.responseText);
2390} else {
2391requestFailed();
2392}
2393};
2394request.onerror = requestFailed;
2395request.ontimeout = requestFailed;
2396request.send(data);
2397}
2398
2399sendSuggestionsRequest('https://a.sinonim.org/sug.php', true);
2400}
2401
2402let idsForSug = ["text","text2","text3","text3_2","text4","text5","text6","text7","text8","text9","text10","text11","text12","text13","text15","text16","text17","text18","text19"];
2403let idsForSugForm = ["submitShow","submitShow2","submitShow3","submitShow3_2","submitShow4","submitShow5","submitShow6","submitShow7","submitShow8","submitShow9","submitShow10","submitShow11","submitShow12","submitShow13","submitShow15","submitShow16","submitShow17","submitShow18","submitShow19"];
2404let sectionForSug = ["s","s","p","p","a","a","as","as","mo","f","f","t","t","sc","bu","kb","kb","mg","kp"];
2405
2406for (let i = 0; i < idsForSug.length; i++) {
2407let inputElement = document.getElementById(idsForSug[i]);
2408if (!inputElement) continue;
2409let mobileSuggestionScrolled = false;
2410inputElement.addEventListener('focus', function() {
2411mobileSuggestionScrolled = false;
2412});
2413inputElement.addEventListener('input', function() {
2414if (mobileSuggestionScrolled || !window.matchMedia('(max-width: 700px)').matches || !this.value.length) return;
2415mobileSuggestionScrolled = true;
2416const currentInput = this;
2417setTimeout(function() {
2418currentInput.scrollIntoView({behavior: 'smooth', block: 'start', inline: 'nearest'});
2419}, 180);
2420});
2421inputElement.addEventListener('input', function() {
2422scheduleSug(this.id, sectionForSug[i], idsForSugForm[i]);
2423});
2424inputElement.addEventListener('keyup', function(event) {
2425if(event.key === 'ArrowUp' || event.key === 'ArrowDown') {
2426let suggestions = document.getElementsByClassName('suggestion');
2427if(suggestions.length > 0) {
2428if(suggestions[currentSuggestionIndex]){
2429suggestions[currentSuggestionIndex].classList.remove('active');
2430suggestions[currentSuggestionIndex].style.backgroundColor = "";
2431}
2432if(event.key === 'ArrowUp') {
2433currentSuggestionIndex--;
2434if(currentSuggestionIndex < 0) currentSuggestionIndex = suggestions.length - 1;
2435} else {
2436currentSuggestionIndex++;
2437if(currentSuggestionIndex > suggestions.length - 1) currentSuggestionIndex = 0;
2438}
2439if(suggestions[currentSuggestionIndex]){
2440suggestions[currentSuggestionIndex].classList.add('active');
2441suggestions[currentSuggestionIndex].style.backgroundColor = "#f8f8f8";
2442}
2443}
2444} else if(event.key === 'Enter' && document.getElementsByClassName('active').length > 0) {
2445document.getElementsByClassName('active')[0].click();
2446event.preventDefault();
2447}
2448});
2449// убираем событие 'focus' здесь
2450inputElement.addEventListener('blur', hideSuggestions);
2451}
2452});
2453
2454document.addEventListener("DOMContentLoaded", function() {
2455document.querySelectorAll('.number').forEach(function(el) {
2456el.textContent = el.textContent.replace('.', '');
2457});
2458
2459if(document.getElementById("assocPodryad")){
2460const toggleButton = document.getElementById('toggleButton');
2461const assocPodryad = document.getElementById('assocPodryad');
2462const listItems = assocPodryad.querySelectorAll('li');
2463
2464toggleButton.addEventListener('click', function() {
2465listItems.forEach(function(item) {
2466const computedStyle = getComputedStyle(item);
2467if (computedStyle.display === 'inline-block') {
2468item.style.display = 'block';
2469} else {
2470item.style.display = 'inline-block';
2471}
2472});
2473});
2474}
2475});
2476
2477document.addEventListener("DOMContentLoaded", function() {
2478if(document.getElementById("podskazki1")){
2479const podskazki1 = document.getElementById("podskazki1");
2480const toggleLink = document.createElement("a");
2481
2482toggleLink.href = "#";
2483toggleLink.id = "toggleLink";
2484toggleLink.textContent = "Развернуть";
2485
2486podskazki1.appendChild(toggleLink);
2487
2488let isCollapsed = true;
2489
2490toggleLink.addEventListener("click", function(event) {
2491event.preventDefault();
2492
2493if (isCollapsed) {
2494podskazki1.style.height = "auto";
2495toggleLink.textContent = "Свернуть";
2496toggleLink.style.bottom = "0";
2497toggleLink.style.top = "auto";
2498toggleLink.style.fontSize = '14px';
2499toggleLink.style.position="relative";
2500} else {
2501podskazki1.style.height = "60px";
2502toggleLink.textContent = "Развернуть";
2503toggleLink.style.bottom = "0";
2504toggleLink.style.top = "auto";
2505toggleLink.style.fontSize = '16px';
2506toggleLink.style.position="absolute";
2507}
2508
2509isCollapsed = !isCollapsed;
2510});
2511}
2512});
2513
2514document.addEventListener('DOMContentLoaded', function() {
2515(function () {
2516  var p = document.getElementById('otvetim');
2517  if (!p) return;
2518
2519  var email = String.fromCharCode(
2520    119, 101, 98, 64, 115, 105, 110, 111,
2521    110, 105, 109, 46, 111, 114, 103
2522  );
2523
2524  var subject = 'Вопрос по сайту sinonim.org';
2525
2526  var body =
2527    'Здравствуйте!\n\n' +
2528    'Опишите ваш вопрос:\n\n\n' +
2529    'Страница: ' + window.location.href + '\n' +
2530    'ID пользователя: ' + (window.userId || 'не определён');
2531
2532  var link = document.createElement('a');
2533
2534  link.href =
2535    'mailto:' + email +
2536    '?subject=' + encodeURIComponent(subject) +
2537    '&body=' + encodeURIComponent(body);
2538
2539  link.textContent = email;
2540  p.appendChild(link);
2541})();
2542
2543var headers = document.querySelectorAll('.forSort');
2544
2545// Применяем функцию к каждому заголовку
2546headers.forEach(function(header) {
2547header.style.cursor = 'pointer';
2548header.addEventListener('click', function() {
2549sortTable(header.cellIndex);
2550});
2551});
2552});
2553
2554document.addEventListener("DOMContentLoaded", function () {
2555const form = document.getElementById("submitShow13");
2556const btn  = document.getElementById("inputSc");
2557
2558if (form && btn) {
2559form.addEventListener("submit", function () {
2560btn.disabled = true;
2561setTimeout(() => {
2562btn.disabled = false;
2563}, 2000);
2564});
2565}
2566});
2567
2568(function () {
2569var style = document.createElement('style');
2570style.textContent = '.hideLikesEnabled .tdLike{display:none!important}';
2571document.head.appendChild(style);
2572try {
2573document.documentElement.classList.toggle('hideLikesEnabled', localStorage.getItem('hideLikes') === 'true');
2574} catch (e) {}
2575})();
2576
2577document.addEventListener('DOMContentLoaded', function () {
2578const checkbox = document.getElementById("hideLikes");
2579const labelHideLikes = document.getElementById("lableHideLikes");
2580
2581// Показывать переключатель только на странице myWords
2582if (!window.location.href.includes('myWords') && labelHideLikes) {
2583labelHideLikes.style.display = 'none';
2584}
2585
2586if (!checkbox) return;
2587
2588// Чтение состояния (по умолчанию false)
2589const savedState = localStorage.getItem("hideLikes") === "true";
2590
2591// Устанавливаем состояние чекбокса
2592checkbox.checked = savedState;
2593
2594// Переключение
2595checkbox.addEventListener("change", () => {
2596const hide = checkbox.checked;
2597localStorage.setItem("hideLikes", hide);
2598document.documentElement.classList.toggle('hideLikesEnabled', hide);
2599});
2600});
2601
2602
2603document.addEventListener('DOMContentLoaded', () => {
2604const LIMIT = 64;
2605
2606document.querySelectorAll('.linksList').forEach(ul => {
2607const items = Array.from(ul.children).filter(el => el.tagName === 'LI');
2608if (items.length <= LIMIT) return;
2609
2610// Обёртка (для кнопки и градиента), не меняем детей ul
2611const wrap = document.createElement('div');
2612wrap.className = 'linksList-wrap';
2613ul.parentNode.insertBefore(wrap, ul);
2614wrap.appendChild(ul);
2615
2616// Кнопка
2617const hiddenCount = items.length - LIMIT;
2618const btn = document.createElement('button');
2619btn.className = 'linksList-toggle';
2620btn.type = 'button';
2621btn.setAttribute('aria-expanded', 'false');
2622btn.innerHTML = `Показать ещё ${hiddenCount} <span class="arr">&#9660;</span>`;
2623wrap.appendChild(btn);
2624
2625// Градиент
2626const fade = document.createElement('div');
2627fade.className = 'linksList-fade';
2628wrap.appendChild(fade);
2629
2630// Устанавливаем высоту по 8-му элементу
2631function setClamp() {
2632const last = items[LIMIT - 1];
2633if (!last) return;
2634const clamp = last.offsetTop + last.offsetHeight - ul.offsetTop;
2635ul.style.setProperty('--linksClamp', clamp + 'px');
2636if (!ul.classList.contains('is-open')) {
2637ul.style.maxHeight = `var(--linksClamp)`;
2638}
2639}
2640
2641// Стартовое состояние
2642ul.classList.add('linksList-collapsible');
2643ul.classList.remove('is-open');
2644// Ждём рендер, потом меряем
2645requestAnimationFrame(() => {
2646setClamp();
2647});
2648
2649// Тоггл
2650btn.addEventListener('click', () => {
2651const isOpen = ul.classList.toggle('is-open');
2652if (isOpen) {
2653ul.style.maxHeight = ul.scrollHeight + 'px';
2654btn.setAttribute('aria-expanded', 'true');
2655btn.innerHTML = `Свернуть <span class="arr">&#9650;</span>`;
2656fade.style.opacity = '0';
2657fade.style.pointerEvents = 'none';
2658} else {
2659// 1) Вернули к clamp и подготовили transition
2660setClamp();
2661btn.setAttribute('aria-expanded', 'false');
2662btn.innerHTML = `Показать ещё ${hiddenCount} <span class="arr">&#9660;</span>`;
2663fade.style.opacity = '';
2664fade.style.pointerEvents = '';
2665
2666// 2) Двойной rAF — чтобы transition наверняка стартовал
2667requestAnimationFrame(() => {
2668requestAnimationFrame(() => {
2669setClamp();
2670
2671// 3) Плавно прокручиваем к началу свёрнутого списка
2672//    (до первого видимого элемента / верхушки ul)
2673// Можно выбрать items[0] или обёртку wrap — обычно удобнее к началу списка:
2674const anchor = items[0] || ul;
2675smoothScrollTo(anchor, 40);
2676});
2677});
2678}
2679});
2680// Пересчёт при ресайзе/перекладке шрифтов
2681let t;
2682window.addEventListener('resize', () => {
2683clearTimeout(t);
2684t = setTimeout(() => {
2685if (!ul.classList.contains('is-open')) setClamp();
2686}, 100);
2687});
2688});
2689});
2690
2691// history
2692const STORAGE_KEY = 'textareaHistory_' + location.pathname;
2693
2694document.addEventListener('DOMContentLoaded', () => {
2695const textarea = document.getElementById('text_si');
2696const saveBtn = document.getElementById('buttonSend');
2697const clearBtn = document.getElementById('clearHistory');
2698const toggleLink = document.getElementById('toggleHistory');
2699const historyContainer = document.getElementById('historyContainer');
2700const historyList = document.getElementById('historyList');
2701const showHistoryLink = document.getElementById('showHistoryLink');
2702
2703if (!textarea || !saveBtn) {
2704return;
2705}
2706
2707function saveTextHistory(newText) {
2708let history = JSON.parse(localStorage.getItem(STORAGE_KEY)) || [];
2709newText = newText.trim();
2710
2711if (newText && !history.includes(newText)) {
2712history.unshift(newText);
2713if (history.length > 60) history.pop();
2714localStorage.setItem(STORAGE_KEY, JSON.stringify(history));
2715}
2716}
2717
2718function renderHistory() {
2719const history = JSON.parse(localStorage.getItem(STORAGE_KEY)) || [];
2720
2721if(historyList) historyList.innerHTML = '';
2722
2723if (history.length === 0) {
2724if(historyContainer) historyContainer.style.display = 'none';
2725document.getElementById('toggleHistory').textContent = 'История ваших запросов пуста';
2726return;
2727}
2728
2729history.forEach(item => {
2730const li = document.createElement('li');
2731const pre = document.createElement('pre');
2732const shortText = item.length > 100
2733? item.slice(0, item.slice(0, 100).lastIndexOf(' ')) + '…'
2734: item;
2735
2736pre.style.whiteSpace = 'pre-wrap';
2737pre.style.cursor = 'pointer';
2738pre.title = item;
2739pre.textContent = shortText;
2740
2741pre.onclick = () => {
2742textarea.value = item;
2743textarea.focus();
2744};
2745
2746li.appendChild(pre);
2747if(historyList) historyList.appendChild(li);
2748});
2749}
2750
2751saveBtn.addEventListener('click', () => {
2752const text = textarea.value;
2753saveTextHistory(text);
2754renderHistory();
2755});
2756
2757if (clearBtn) {
2758clearBtn.addEventListener('click', () => {
2759localStorage.removeItem(STORAGE_KEY);
2760renderHistory();
2761});
2762}
2763if(toggleLink){
2764toggleLink.addEventListener('click', (e) => {
2765e.preventDefault();
2766historyContainer.style.display = (historyContainer.style.display === 'none') ? 'block' : 'none';
2767});
2768}
2769renderHistory();
2770});
2771// history
2772
2773document.addEventListener('DOMContentLoaded', function() {
2774const toggle = document.getElementById('toggleColor');
2775if (!toggle) return;
2776
2777// восстанавливаем состояние из localStorage
2778const saved = localStorage.getItem('toggleColor');
2779if (saved !== null) toggle.checked = saved === 'true';
2780
2781function updateColorState() {
2782document.querySelectorAll('.assocPodryad').forEach(list => {
2783if (toggle.checked) {
2784list.classList.add('nocolor');
2785} else {
2786list.classList.remove('nocolor');
2787}
2788});
2789localStorage.setItem('toggleColor', toggle.checked);
2790}
2791
2792// начальная установка
2793updateColorState();
2794
2795// при изменении
2796toggle.addEventListener('change', updateColorState);
2797});
2798
2799document.addEventListener('DOMContentLoaded', function() {
2800const textServiceFile = document.getElementById('textServiceFile');
2801const textServiceFileToken = document.getElementById('textServiceFileToken');
2802const textServiceFileState = document.getElementById('textServiceFileState');
2803const textServiceFileName = document.getElementById('textServiceFileName');
2804const textServiceFileClear = document.getElementById('textServiceFileClear');
2805
2806if(textServiceFile && textServiceFileState && textServiceFileName){
2807const clearTextServiceFile = function(){
2808textServiceFile.value = '';
2809if(textServiceFileToken) textServiceFileToken.value = '';
2810if(typeof window.textServiceFileToken === 'string') window.textServiceFileToken = '';
2811textServiceFileName.textContent = '';
2812textServiceFileState.style.display = 'none';
2813};
2814window.clearTextServiceFile = clearTextServiceFile;
2815textServiceFile.addEventListener('change',function(){
2816const file = this.files && this.files[0] ? this.files[0] : null;
2817if(!file){
2818clearTextServiceFile();
2819return;
2820}
2821if(file.size > 15 * 1024 * 1024){
2822clearTextServiceFile();
2823alert('Файл должен быть не больше 15 МБ.');
2824return;
2825}
2826if(textServiceFileToken) textServiceFileToken.value = '';
2827if(typeof window.textServiceFileToken === 'string') window.textServiceFileToken = '';
2828textServiceFileName.textContent = file.name;
2829textServiceFileState.style.display = 'inline-flex';
2830});
2831if(textServiceFileClear){
2832textServiceFileClear.addEventListener('click',function(event){
2833event.preventDefault();
2834clearTextServiceFile();
2835});
2836}
2837}
2838});
2839
2840document.addEventListener('DOMContentLoaded', function() {
2841const imageUploadElement = document.getElementById('image-upload');
2842const sectionParam = typeof currentLangJS !== 'undefined' 
2843? `&section=${encodeURIComponent(String(currentLangJS))}` 
2844: '';
2845
2846if (imageUploadElement) {
2847imageUploadElement.addEventListener('change', function(event) {
2848const file = event.target.files[0];
2849if (!file) return;
2850
2851setTimeout(() => {
2852const textSiElement = document.getElementById('text_si');
2853if (textSiElement) {
2854textSiElement.value = 'Идёт отправка. Пожалуйста, подождите...';
2855}
2856
2857const formData = new FormData();
2858formData.append('image', file);
2859
2860fetch(`/data.php?ocr${sectionParam}`, {
2861method: 'POST',
2862body: formData
2863})
2864.then(response => response.json())
2865.then(data => {
2866const textSiElement = document.getElementById('text_si');
2867if (!textSiElement) return;
2868
2869let text = data.text || 'Не распознали';
2870
2871// --- Учитываем maxlength ---
2872const maxLen = parseInt(textSiElement.getAttribute('maxlength'), 10);
2873if (maxLen && text.length > maxLen) {
2874text = text.substring(0, maxLen);
2875}
2876
2877textSiElement.value = text;
2878
2879// если count() существует — вызываем
2880if (typeof count === 'function') {
2881count();
2882}
2883})
2884.catch(error => {
2885const textSiElement = document.getElementById('text_si');
2886if (textSiElement) {
2887const button = document.getElementById("buttonSend");
2888if (button) {
2889button.disabled = true;
2890}
2891textSiElement.value = 'Ошибка при распознавании текста.';
2892}
2893});
2894}, 300);
2895});
2896}
2897
2898const buttonSend = document.getElementById('buttonSend');
2899
2900if (buttonSend) {
2901buttonSend.addEventListener('click', function(e) {
2902const textarea = document.getElementById('text_si');
2903
2904if (textarea && textarea.value.trim() === 'Идёт отправка. Пожалуйста, подождите...') {
2905alert('Фото ещё не отправилось. Пожалуйста, подождите.');
2906e.preventDefault();
2907return false;
2908}
2909
2910if(textarea && (textarea.value.trim() === 'Не распознали' || textarea.value.trim() === 'Ошибка при распознавании текста.')){
2911alert('Не смогли извлечь текст.');
2912e.preventDefault();
2913return false;
2914}
2915});
2916}
2917
2918document.querySelectorAll('.pos-hint').forEach(function(hint) {
2919hint.addEventListener('click', function(e) {
2920var id = this.getAttribute('data-target');
2921if (!id) return;
2922
2923var block = document.getElementById(id);
2924if (!block) return;
2925
2926block.style.display =
2927(block.style.display === 'none' || block.style.display === '') ? 'inline' : 'none';
2928
2929e.stopPropagation();
2930});
2931});
2932
2933window.addEventListener("pageshow", function () {if(document.getElementById("languageSelect") !== null) document.getElementById("languageSelect").value = currentLangJS;});
2934
2935if(
2936document.getElementById("text_si") !== null &&
2937!window.isPR &&
2938!window.location.href.includes('/sr/') && !window.location.href.includes('/po/') && !window.location.href.includes('/to/') && !window.location.href.includes('/password')
2939){
2940if (navigator.platform.toLowerCase().includes("win")) {
2941document.getElementById("text_si").focus();
2942}
2943}
2944else if (navigator.platform.toLowerCase().includes("win") && !window.location.pathname.startsWith("/audio") && !window.location.pathname.startsWith("/celebrity") && !window.location.pathname.startsWith("/change-color")) {
2945const firstInput = document.querySelector('input[type="text"]');
2946if (firstInput) firstInput.focus();
2947}
2948
2949if(typeof runcheckIfElementReached !== 'undefined' && typeof sectionForGetWords !== 'undefined' && typeof chtoForGetWords !== 'undefined'){
2950window.addEventListener('scroll', checkIfElementReached);
2951}
2952});
2953
2954function runCopyLink() {
2955try {
2956document.addEventListener("copy", copyLink);
2957} catch (e) {}
2958}
2959
2960function copyLink(event) {
2961var selection;
2962if (window.getSelection) {
2963selection = window.getSelection();
2964} else if (document.getSelection) {
2965selection = document.getSelection();
2966} else if (document.selection) {
2967selection = document.selection.createRange();
2968}
2969
2970if (selection && selection.toString().length >= 25) {
2971var htmlText = '';
2972var plainText = selection.toString();
2973
2974// Преобразуем выделение в HTML (если поддерживается)
2975if (selection.rangeCount) {
2976var container = document.createElement("div");
2977for (var i = 0; i < selection.rangeCount; i++) {
2978container.appendChild(selection.getRangeAt(i).cloneContents());
2979}
2980htmlText = container.innerHTML;
2981}
2982
2983htmlText += '<br /><br />Больше: <a href="' + document.location.href + '">' + document.location.href + '</a>';
2984plainText += '\n\nБольше: ' + document.location.href;
2985
2986// Используем clipboardData, если оно доступно
2987if (event && event.clipboardData) {
2988event.preventDefault();
2989event.clipboardData.setData("text/html", htmlText);
2990event.clipboardData.setData("text/plain", plainText);
2991}
2992}
2993}
2994runCopyLink();
2995
2996var sortAscending = true;
2997
2998function cleanHTMLFromComments(html) {
2999return html.replace(/<!--[\s\S]*?-->/g, '');
3000}
3001
3002function sortTable(columnIndex) {
3003var table = document.getElementById("mainTable");
3004var rows = Array.from(table.rows).slice(1);
3005
3006rows.forEach(function(row) {
3007Array.from(row.getElementsByTagName("TD")).forEach(function(cell) {
3008cell.innerHTML = cleanHTMLFromComments(cell.innerHTML);
3009});
3010});
3011
3012// Отделяем строки с 'trrec' и без него
3013var trrecRows = rows.filter(row => row.classList.contains('trrec'));
3014var sortableRows = rows.filter(row => !row.classList.contains('trrec'));
3015
3016// Проверяем, является ли содержимое ячейки числом
3017var isNumeric = sortableRows.some(row => {
3018var cellContent = row.getElementsByTagName("TD")[columnIndex].textContent.trim();
3019return !isNaN(cellContent) && cellContent !== '';
3020});
3021
3022// Сортируем строки без 'trrec'
3023sortableRows.sort(function(rowA, rowB) {
3024var cellA = rowA.getElementsByTagName("TD")[columnIndex].textContent.trim();
3025var cellB = rowB.getElementsByTagName("TD")[columnIndex].textContent.trim();
3026
3027if (cellA == '-') cellA = '0';
3028if (cellB == '-') cellB = '0';
3029
3030if (isNumeric) {
3031return sortAscending ? parseFloat(cellA) - parseFloat(cellB) : parseFloat(cellB) - parseFloat(cellA);
3032} else {
3033return sortAscending ? cellA.localeCompare(cellB) : cellB.localeCompare(cellA);
3034}
3035});
3036
3037// Определяем позицию для вставки строк с 'trrec'
3038var insertAt = Math.floor(sortableRows.length / 2);
3039
3040// Вставляем строки с 'trrec' в середину
3041trrecRows.forEach(function(row, index) {
3042sortableRows.splice(insertAt + index, 0, row);
3043});
3044
3045// Очищаем таблицу и вставляем отсортированные строки
3046while (table.rows.length > 1) {
3047table.deleteRow(1);
3048}
3049
3050sortableRows.forEach(function(row) {
3051table.appendChild(row);
3052});
3053
3054sortAscending = !sortAscending;
3055}
3056
3057document.addEventListener('DOMContentLoaded', function () {
3058window.addEventListener('scroll', function() {
3059var pageY = window.pageYOffset || document.documentElement.scrollTop;
3060var innerHeight = document.documentElement.clientHeight;
3061var updownElem = document.getElementById('naverhId');
3062var bottomElem = document.getElementById('vnizId');
3063
3064if (pageY > innerHeight * 2) {
3065if (updownElem) updownElem.style.display = 'block';
3066if (bottomElem) bottomElem.style.display = 'block';
3067} else {
3068if (updownElem) updownElem.style.display = 'none';
3069if (bottomElem) bottomElem.style.display = 'none';
3070}
3071});
3072var bottomElem = document.getElementById('vnizId');
3073if (bottomElem) bottomElem.addEventListener('click', function(event) {
3074event.preventDefault();
3075window.scrollTo({top: document.documentElement.scrollHeight, behavior: 'smooth'});
3076});
3077});
3078
3079var likeEnabled = true;
3080var dislikeEnabled = true;
3081var likeDelay = 1300;
3082var dislikeDelay = 1000;
3083var clicked = false;
3084let sortOrder = {};
3085let lengthSortOrder = {};
3086let counter = 0;
3087let previousLanguage = (document.getElementById("languageSelect") || {}).value || "";
3088
3089(function () {
3090// курсор при наведении
3091if (!document.getElementById('_unitTextCopyStyle')) {
3092var st = document.createElement('style');
3093st.id = '_unitTextCopyStyle';
3094st.textContent = 'div.unitText{cursor:pointer}';
3095document.head.appendChild(st);
3096}
3097// title для копирования
3098document.addEventListener('mouseover', function (e) {
3099var el = e.target.closest('div.unitText');
3100if (el && !el.hasAttribute('title')) {
3101el.setAttribute('title', 'кликните для копирования');
3102}
3103});
3104
3105function copyText(text) {
3106if (navigator.clipboard && window.isSecureContext) {
3107return navigator.clipboard.writeText(text);
3108}
3109return new Promise(function (resolve, reject) {
3110try {
3111var ta = document.createElement('textarea');
3112ta.value = text;
3113ta.setAttribute('readonly', '');
3114ta.style.position = 'fixed';
3115ta.style.left = '-9999px';
3116ta.style.top = '-9999px';
3117document.body.appendChild(ta);
3118ta.focus();
3119ta.select();
3120var ok = document.execCommand('copy');
3121document.body.removeChild(ta);
3122ok ? resolve() : reject(new Error('execCommand copy failed'));
3123} catch (e) {
3124reject(e);
3125}
3126});
3127}
3128
3129function selectElementText(el) {
3130var sel = window.getSelection && window.getSelection();
3131if (!sel || !document.createRange) return;
3132sel.removeAllRanges();
3133var range = document.createRange();
3134range.selectNodeContents(el);
3135sel.addRange(range);
3136}
3137
3138function showCopiedBadge(el) {
3139var request = new XMLHttpRequest();
3140request.open('POST', '/like.php?copy='+el.dataset.id, true);
3141request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
3142request.send('word=;');
3143if (el.__copiedBadgeTimer) clearTimeout(el.__copiedBadgeTimer);
3144
3145var prevPos = getComputedStyle(el).position;
3146if (prevPos === 'static') el.style.position = 'relative';
3147
3148var badge = el.querySelector(':scope > ._copied_badge');
3149if (!badge) {
3150badge = document.createElement('div');
3151badge.className = '_copied_badge';
3152badge.textContent = 'Скопировано';
3153badge.style.cssText =
3154'position:absolute;
3154left:50%;top:50%;transform:translate(-50%,-50%);' +
3155'background:#22c55e;color:#fff;padding:8px 14px;border-radius:999px;' +
3156'font:600 14px/1.1 Arial, sans-serif;box-shadow:0 6px 18px rgba(0,0,0,.18);' +
3157'pointer-events:none;opacity:0;transition:opacity .15s ease;z-index:9999;';
3158el.appendChild(badge);
3159}
3160
3161requestAnimationFrame(function () { badge.style.opacity = '1'; });
3162
3163el.__copiedBadgeTimer = setTimeout(function () {
3164badge.style.opacity = '0';
3165setTimeout(function () {
3166if (badge && badge.parentNode === el) el.removeChild(badge);
3167if (prevPos === 'static') el.style.position = '';
3168}, 180);
3169}, 2500);
3170}
3171
3172document.addEventListener('click', function (e) {
3173var el = e.target && e.target.closest ? e.target.closest('div.unitText') : null;
3174if (!el) return;
3175
3176selectElementText(el);
3177
3178// текст + перенос строки + ссылка на текущую страницу
3179var text = (el.innerText || el.textContent || '').trim();
3180var toCopy = text + "\n" + location.href;
3181
3182copyText(toCopy).then(function () {
3183showCopiedBadge(el);
3184}).catch(function () {
3185showCopiedBadge(el);
3186});
3187});
3188})();
3189
3190function togglePlusHint() {
3191var ta = document.getElementById('text_si');
3192var a  = document.querySelector('a.badge-plus');
3193if (!ta || !a) return;
3194
3195var max = parseInt(ta.getAttribute('maxlength') || '0', 10);
3196var len = (ta.value || '').length;
3197
3198// "не хватает" = упёрлись в лимит (дальше ввод не растёт)
3199if (max > 0 && len >= max) a.textContent = 'Удвоить лимит символов';
3200else a.textContent = 'Убрать рекламу';
3201}
3202
3203// чтобы корректно выставилось при загрузке (если textarea уже заполнена)
3204document.addEventListener('DOMContentLoaded', togglePlusHint);
3205
3206(function () {
3207if (location.hostname === 'pro.sinonim.org') return;
3208var deferredPrompt = null;
3209var installBtn = null;
3210var installPromo = null;
3211var footerInstallBtn = null;
3212var DISMISS_KEY = 'installPromoClosedAt';
3213var DISMISS_TIME = 7 * 24 * 60 * 60 * 1000;
3214
3215function wasDismissed() {
3216try {
3217var closedAt = parseInt(localStorage.getItem(DISMISS_KEY) || '0', 10);
3218return closedAt && Date.now() - closedAt < DISMISS_TIME;
3219} catch (e) {
3220return false;
3221}
3222}
3223
3224function hideInstallPromo() {
3225if (installPromo) installPromo.classList.remove('is-visible');
3226}
3227
3228function setFooterInstallVisible(visible) {
3229if (footerInstallBtn) footerInstallBtn.style.display = visible && deferredPrompt ? 'block' : 'none';
3230}
3231
3232function showInstallPromo() {
3233if (!installPromo || !deferredPrompt || wasDismissed()) return;
3234setFooterInstallVisible(false);
3235installPromo.classList.add('is-visible');
3236}
3237
3238function updateInstallUi() {
3239if (!deferredPrompt) {
3240hideInstallPromo();
3241setFooterInstallVisible(false);
3242return;
3243}
3244if (wasDismissed()) {
3245hideInstallPromo();
3246setFooterInstallVisible(true);
3247} else {
3248showInstallPromo();
3249}
3250}
3251
3252function requestInstall() {
3253if (!deferredPrompt) return;
3254deferredPrompt.prompt();
3255deferredPrompt.userChoice.then(function () {
3256deferredPrompt = null;
3257hideInstallPromo();
3258setFooterInstallVisible(false);
3259});
3260}
3261
3262function createInstallBtn() {
3263if (document.getElementById('installPromo')) return;
3264
3265var style = document.createElement('style');
3266style.textContent =
3267'#installPromo{display:none;position:fixed;right:18px;bottom:18px;z-index:10000;width:min(530px,calc(100vw - 36px));box-sizing:border-box;align-items:center;gap:10px;padding:11px 40px 11px 12px;background:linear-gradient(135deg,#fff 0%,#eef2ff 100%);border:1px solid #c7d2fe;border-radius:16px;box-shadow:0 14px 38px rgba(49,46,129,.24);color:#1e1b4b;font-family:Arial,sans-serif;}' +
3268'#installPromo.is-visible{display:flex;animation:installPromoIn .38s ease-out;}' +
3269'#installPromoIcon{display:flex;align-items:center;justify-content:center;flex:0 0 42px;width:42px;height:42px;border-radius:12px;background:linear-gradient(135deg,#4f46e5,#7c3aed);box-shadow:0 7px 18px rgba(79,70,229,.28);font-size:22px;}' +
3270'#installPromoText{min-width:185px;flex:1;}' +
3271'#installPromoTitle{font-size:15px;line-height:1.2;font-weight:700;color:#1e1b4b;white-space:nowrap;}
3271' +
3272'#installPromoDesc{margin-top:2px;font-size:12.5px;line-height:1.3;color:#6366f1;white-space:nowrap;}' +
3273'#installPromoDescMobile{display:none;}' +
3274'#installBtn{flex:0 0 auto;padding:10px 15px;border:0;border-radius:11px;background:linear-gradient(135deg,#4f46e5,#7c3aed);color:#fff;font-size:14px;font-weight:700;white-space:nowrap;cursor:pointer;box-shadow:0 7px 19px rgba(79,70,229,.32);transition:transform .15s ease,box-shadow .15s ease;}' +
3275'#installBtn:hover{transform:translateY(-2px);box-shadow:0 12px 28px rgba(79,70,229,.44);}' +
3276'#installBtn:active{transform:translateY(1px);}' +
3277'#installPromoClose{position:absolute;top:50%;right:7px;transform:translateY(-50%);width:28px;height:28px;padding:0;border:0;background:transparent;color:#818cf8;font-size:22px;line-height:28px;cursor:pointer;border-radius:50%;}' +
3278'#installPromoClose:hover{background:#e0e7ff;color:#4338ca;}' +
3279'#installFooterBtn{display:none;padding:9px 15px;border:0;border-radius:11px;background:linear-gradient(135deg,#4f46e5,#7c3aed);color:#fff;font-size:14px;font-weight:700;cursor:pointer;box-shadow:0 6px 16px rgba(79,70,229,.28);transition:transform .15s ease,box-shadow .15s ease;}' +
3280'#installFooterBtn:hover{transform:translateY(-1px);box-shadow:0 9px 22px rgba(79,70,229,.4);}' +
3281'#installFooterBtn:active{transform:translateY(1px);}' +
3282'#installPromo.is-visible #installBtn{animation:installBtnPulse 1.05s ease .45s 2;}' +
3283'@keyframes installPromoIn{from{opacity:0;transform:translateY(18px) scale(.97)}to{opacity:1;transform:translateY(0) scale(1)}}' +
3284'@keyframes installBtnPulse{0%,100%{transform:scale(1)}50%{transform:scale(1.055);box-shadow:0 10px 30px rgba(79,70,229,.55)}}' +
3285'@media(max-width:700px){#installPromo{left:10px;right:10px;bottom:calc(10px + env(safe-area-inset-bottom));width:auto;flex-wrap:wrap;padding:13px 38px 13px 14px;gap:10px;}#installPromoIcon{flex-basis:44px;width:44px;height:44px;}#installPromoText{min-width:0;padding-right:5px;}#installPromoTitle,#installPromoDesc{white-space:normal;}#installPromoDescDesktop{display:none;}#installPromoDescMobile{display:inline;}#installBtn{width:calc(100% + 24px);margin-right:-24px;font-size:15px;}#installPromoClose{top:5px;right:7px;transform:none;}}' +
3286'@media(prefers-reduced-motion:reduce){#installPromo.is-visible,#installPromo.is-visible #installBtn{animation:none;}}';
3287document.head.appendChild(style);
3288
3289var promo = document.createElement('div');
3290promo.id = 'installPromo';
3291promo.setAttribute('role', 'dialog');
3292promo.setAttribute('aria-label', 'Установить приложение Sinonim');
3293promo.innerHTML = '<button type="button" id="installPromoClose" aria-label="Закрыть" title="Не показывать 7 дней">&times;</button>' +
3294'<div id="installPromoIcon" aria-hidden="true">&#128241;</div>' +
3295'<div id="installPromoText"><div id="installPromoTitle">Sinonim всегда под рукой</div><div id="installPromoDesc">Быстрый доступ <span id="installPromoDescDesktop">с рабочего стола</span><span id="installPromoDescMobile">с главного экрана</span></div></div>';
3296
3297var btn = document.createElement('button');
3298btn.id = 'installBtn';
3299btn.type = 'button';
3300btn.textContent = 'Установить приложение';
3301
3302btn.addEventListener('click', requestInstall);
3303
3304promo.appendChild(btn);
3305document.body.appendChild(promo);
3306installPromo = promo;
3307installBtn = btn;
3308
3309var footerHolder = document.getElementById('links4339');
3310if (footerHolder) {
3311var footerBtn = document.createElement('button');
3312footerBtn.id = 'installFooterBtn';
3313footerBtn.type = 'button';
3314footerBtn.innerHTML = '&#128241; Установить приложение';
3315footerBtn.addEventListener('click', requestInstall);
3316footerHolder.appendChild(footerBtn);
3317footerInstallBtn = footerBtn;
3318}
3319
3320document.getElementById('installPromoClose').addEventListener('click', function () {
3321try { localStorage.setItem(DISMISS_KEY, String(Date.now())); } catch (e) {}
3322hideInstallPromo();
3323setFooterInstallVisible(true);
3324});
3325
3326// если событие beforeinstallprompt пришло раньше, чем создали кнопку
3327updateInstallUi();
3328}
3329
3330window.addEventListener('beforeinstallprompt', function (e) {
3331console.log('beforeinstallprompt!');
3332e.preventDefault();
3333deferredPrompt = e;
3334updateInstallUi();
3335});
3336
3337window.addEventListener('appinstalled', function () {
3338console.log('PWA installed');
3339if (installBtn) {
3340hideInstallPromo();
3341setFooterInstallVisible(false);
3342}
3343});
3344
3345if (document.readyState === 'loading') {
3346document.addEventListener('DOMContentLoaded', createInstallBtn);
3347} else {
3348createInstallBtn();
3349}
3350})();
3351if ('serviceWorker' in navigator) {navigator.serviceWorker.register('/sw.js');}
3352
3353
3354
3355(function () {
3356'use strict';
3357var API = '/data.php?favoriteTools=1';
3358var state = {auth:null, setup:false, favorites:[], services:{}};
3359
3360function keyFromHref(href) {
3361try {
3362var path = new URL(href, location.origin).pathname.replace(/^\/+|\/+$/g, '');
3363return path && /^[A-Za-z0-9_-]+(?:\/[A-Za-z0-9_-]+)*$/.test(path) ? path : '';
3364} catch (e) { return ''; }
3365}
3366
3367function ucfirst(text) {
3368text = String(text || '').trim();
3369return text ? text.charAt(0).toUpperCase() + text.slice(1) : text;
3370}
3371
3372function collectServices() {
3373var links = document.querySelectorAll('#onlineServices .linksList a[href]');
3374Array.prototype.forEach.call(links, function (link) {
3375var key = keyFromHref(link.getAttribute('href'));
3376if (!key) return;
3377state.services[key] = {href:'/' + key, title:ucfirst(link.textContent)};
3378});
3379var marker = document.querySelector('.currentRecentServiceData');
3380if (marker) {
3381var markerKey = keyFromHref(marker.getAttribute('data-href'));
3382if (markerKey) {
3383state.services[markerKey] = {href:'/' + markerKey, title:ucfirst(marker.getAttribute('data-title'))};
3384}
3385}
3386}
3387
3388function mergeServerServices(services) {
3389if (!services || typeof services !== 'object') return;
3390Object.keys(services).forEach(function (key) {
3391var service = services[key];
3392if (!service || typeof service.href !== 'string' || typeof service.title !== 'string') return;
3393state.services[key] = {href:service.href, title:service.title};
3394});
3395}
3396
3397function bindCurrentFavoriteButton() {
3398var button = document.getElementById('toolFavoriteButton');
3399if (!button || button.getAttribute('data-favorite-bound') === '1') return;
3400button.setAttribute('data-favorite-bound', '1');
3401button.addEventListener('click', toggleFavorite);
3402button.addEventListener('keydown', function (event) {
3403if (event.key === 'Enter' || event.key === ' ') {
3404event.preventDefault();
3405button.click();
3406}
3407});
3408}
3409
3410function insertTarget() {
3411return document.getElementById('recentServices') || document.querySelector('h3.collectionAI');
3412}
3413
3414function showNotice(html) {
3415var notice = document.getElementById('favoriteToolsNotice');
3416if (!notice) {
3417notice = document.createElement('div');
3418notice.id = 'favoriteToolsNotice';
3419document.body.appendChild(notice);
3420}
3421if (state.noticeTimer) clearTimeout(state.noticeTimer);
3422notice.innerHTML = '<button type="button" class="favoriteNoticeClose" aria-label="Закрыть">&times;</button>' + html;
3423notice.hidden = false;
3424notice.querySelector('.favoriteNoticeClose').onclick = function () {
3425notice.hidden = true;
3426clearTimeout(state.noticeTimer);
3427};
3428state.noticeTimer = setTimeout(function () { notice.hidden = true; },8000);
3429}
3430
3431function createBlock() {
3432var block = document.getElementById('favoriteTools');
3433if (block) return block;
3434var target = insertTarget();
3435if (!target || !target.parentNode) return null;
3436block = document.createElement('section');
3437block.id = 'favoriteTools';
3438block.hidden = true;
3439block.innerHTML = '<div id="favoriteToolsTitle"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="m12 2.8 2.75 5.57 6.15.9-4.45 4.33 1.05 6.12L12 16.83 6.5 19.72l1.05-6.12L3.1 9.27l6.15-.9L12 2.8Z"></path></svg>Избранные инструменты</div><div id="favoriteToolsList"></div>';
3440target.parentNode.insertBefore(block, target);
3441return block;
3442}
3443
3444function isFavorite(key) {
3445return state.favorites.indexOf(key) !== -1;
3446}
3447
3448function refreshToggles() {
3449var buttons = document.querySelectorAll('#toolFavoriteButton');
3450Array.prototype.forEach.call(buttons, function (button) {
3451var active = isFavorite(button.getAttribute('data-tool'));
3452button.classList.toggle('is-favorite', active);
3453button.style.filter = active ? 'none' : 'grayscale(100%)';
3454button.setAttribute('aria-pressed', active ? 'true' : 'false');
3455button.setAttribute('aria-label', active ? 'Удалить из избранного' : 'Добавить в избранное');
3456button.title = active ? 'Удалить из избранного' : 'Добавить в избранное';
3457});
3458}
3459
3460function renderFavorites() {
3461refreshToggles();
3462var block = document.getElementById('favoriteTools');
3463var available = state.favorites.filter(function (key) { return state.services[key]; });
3464if (!available.length) {
3465if (block) block.hidden = true;
3466return;
3467}
3468block = block || createBlock();
3469if (!block) return;
3470var list = block.querySelector('#favoriteToolsList');
3471list.textContent = '';
3472available.forEach(function (key) {
3473var service = state.services[key];
3474var item = document.createElement('span');
3475item.className = 'favoriteToolItem';
3476var link = document.createElement('a');
3477link.className = 'favoriteToolLink';
3478link.href = service.href;
3479link.textContent = service.title;
3480var remove = document.createElement('button');
3481remove.type = 'button';
3482remove.className = 'favoriteToolRemove';
3483remove.setAttribute('data-tool', key);
3484remove.setAttribute('aria-label', 'Удалить «' + service.title + '» из избранного');
3485remove.title = 'Удалить из избранного';
3486remove.innerHTML = '&times;';
3487remove.addEventListener('click', toggleFavorite);
3488item.appendChild(link);
3489item.appendChild(remove);
3490list.appendChild(item);
3491});
3492block.hidden = false;
3493}
3494
3495function request(method, data) {
3496var options = {method:method, credentials:'same-origin', headers:{'Accept':'application/json'}};
3497if (method === 'POST') {
3498options.headers['Content-Type'] = 'application/x-www-form-urlencoded; charset=UTF-8';
3499options.body = 'action=' + encodeURIComponent(data.action) + '&tool=' + encodeURIComponent(data.tool);
3500}
3501return fetch(API, options).then(function (response) {
3502return response.json().catch(function () { return {ok:false,error:'Некорректный ответ сервера.'}; });
3503});
3504}
3505
3506function toggleFavorite(event) {
3507event.preventDefault();
3508event.stopPropagation();
3509var button = event.currentTarget;
3510var tool = button.getAttribute('data-tool');
3511if (!tool) return;
3512if (state.auth === false) {
3513showNotice('Чтобы сохранять избранные инструменты, <a href="/login">войдите</a> или <a href="/reg">зарегистрируйтесь</a>.');
3514return;
3515}
3516if (state.setup) {
3517showNotice('Хранилище избранных инструментов ещё не настроено.');
3518return;
3519}
3520if (state.auth !== true || button.disabled) return;
3521button.disabled = true;
3522var action = isFavorite(tool) ? 'remove' : 'add';
3523request('POST', {action:action, tool:tool}).then(function (data) {
3524button.disabled = false;
3525if (data && data.ok) {
3526state.favorites = Array.isArray(data.favorites) ? data.favorites : [];
3527mergeServerServices(data.services);
3528renderFavorites();
3529if (action === 'add') showNotice('Сервис добавлен в избранное. Вы можете открыть избранные сервисы на <a href="/">главной странице сайта</a> или в <a href="/myWords">личном кабинете</a>.');
3530return;
3531}
3532if (data && data.auth === false) {
3533state.auth = false;
3534showNotice('Чтобы сохранять избранные инструменты, <a href="/login">войдите</a> или <a href="/reg">зарегистрируйтесь</a>.');
3535return;
3536}
3537showNotice((data && data.error) || 'Не удалось изменить избранное. Попробуйте ещё раз.');
3538}).catch(function () {
3539button.disabled = false;
3540showNotice('Не удалось изменить избранное. Проверьте соединение и попробуйте ещё раз.');
3541});
3542}
3543
3544function addStyles() {
3545var style = document.createElement('style');
3546style.textContent = '#toolFavoriteButton{display:inline-block;margin-left:8px;transition:filter .15s ease,transform .15s ease}#toolFavoriteButton:hover{transform:scale(1.08)}#toolFavoriteButton:focus-visible,.favoriteToolRemove:focus-visible{outline:2px solid #6366f1;outline-offset:2px}#favoriteTools{margin:18px 0 9px;padding:13px 16px;background:linear-gradient(135deg,#fffdf4,#fff8dd);border:1px solid #f2df9b;border-left:3px solid #e3a008;border-radius:10px}#favoriteToolsTitle{display:flex;align-items:center;gap:8px;margin-bottom:10px;color:#1a1a2e;font-size:16px;font-weight:900}#favoriteToolsTitle svg{width:21px;height:21px;fill:#e3a008;stroke:#ca8a04;stroke-width:1;flex:0 0 21px}#favoriteToolsList{display:flex;flex-wrap:wrap;gap:8px}.favoriteToolItem{display:inline-flex;align-items:center;overflow:hidden;background:#fff;border:1px solid #eadb9d;border-radius:9px;box-shadow:0 3px 10px rgba(120,91,10,.07)}.favoriteToolLink{padding:8px 5px 8px 11px;color:#334155;text-decoration:none}.favoriteToolLink:visited{color:#334155}.favoriteToolRemove{display:inline-flex;align-items:center;justify-content:center;align-self:stretch;min-width:30px;padding:0 8px;border:0;background:transparent;color:#a16207;font-size:20px;line-height:1;cursor:pointer}.favoriteToolRemove:hover{background:#fff3bf;color:#92400e}#favoriteToolsNotice{position:fixed;top:16px;left:50%;z-index:10001;box-sizing:border-box;width:min(560px,calc(100% - 28px));margin:0;padding:13px 44px 13px 16px;transform:translateX(-50%);border:1px solid #c7d2fe;border-radius:12px;background:#f5f7ff;color:#334155;box-shadow:0 10px 35px rgba(51,65,85,.22)}#favoriteToolsNotice a{font-weight:700}.favoriteNoticeClose{position:absolute;top:5px;right:7px;width:28px;height:28px;padding:0;border:0;background:transparent;color:#64748b;font-size:22px;cursor:pointer}@media(max-width:600px){.favoriteToolLink{padding-top:9px;padding-bottom:9px}#favoriteToolsNotice{top:10px}}';
3547document.head.appendChild(style);
3548}
3549
3550function initFavoriteTools() {
3551if (window.favoriteToolsAuth !== true) return;
3552addStyles();
3553collectServices();
3554bindCurrentFavoriteButton();
3555refreshToggles();
3556request('GET', {}).then(function (data) {
3557if (data && data.ok) {
3558state.auth = data.auth !== false;
3559state.setup = !!data.setup;
3560state.favorites = Array.isArray(data.favorites) ? data.favorites : [];
3561mergeServerServices(data.services);
3562renderFavorites();
3563} else if (data && data.auth === false) {
3564state.auth = false;
3565refreshToggles();
3566}
3567}).catch(function () {});
3568}
3569
3570if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', initFavoriteTools);
3571else initFavoriteTools();
3572})();
3573/* Помощник на главной странице */
3574(function(){
3575function initHomeAssistant(){
3576var box=document.getElementById('homeAssistant');
3577if(!box)return;
3578var form=document.getElementById('homeAssistantForm');
3579var input=document.getElementById('homeAssistantInput');
3580var send=document.getElementById('homeAssistantSend');
3581var history=document.getElementById('homeAssistantHistory');
3582var status=document.getElementById('homeAssistantStatus');
3583var fileInput=document.getElementById('homeAssistantFileInput');
3584var fileList=document.getElementById('homeAssistantFiles');
3585var clear=document.getElementById('homeAssistantClear');
3586var attach=document.querySelector('.homeAssistantAttach');
3587if(!form||!input||!send||!history||!status||!fileInput||!fileList||!clear)return;
3588var historyKey='sinonimHomeAssistantHistoryV1';
3589var dialogKey='sinonimHomeAssistantDialogV1';
3590var messages=[];
3591var selectedFiles=[];
3592var allowedPaths={};
3593var allowedFile=/\.(?:jpe?g|png|webp|pdf|docx?|txt|pptx?|xlsx?)$/i;
3594Array.prototype.forEach.call(document.querySelectorAll('#onlineServices a[href]'),function(link){
3595try{allowedPaths[new URL(link.href,location.origin).pathname]=true;}catch(e){}
3596});
3597['/price','/myWords','/login','/reg'].forEach(function(path){allowedPaths[path]=true;});
3598function readStorage(key,fallback){try{return localStorage.getItem(key)||fallback;}catch(e){return fallback;}}
3599function writeStorage(key,value){try{localStorage.setItem(key,value);}catch(e){}}
3600try{
3601var saved=JSON.parse(readStorage(historyKey,'[]'));
3602if(Array.isArray(saved))messages=saved.filter(function(item){return item&&/^(user|assistant)$/.test(item.role)&&typeof item.text==='string';}).map(function(item){return {role:item.role,text:item.text,files:Array.isArray(item.files)?item.files.slice(0,3):[],html:item.html===true};}).slice(-20);
3603}catch(e){messages=[];}
3604var dialogId=readStorage(dialogKey,'');
3605if(!/^\d{10,16}$/.test(dialogId)){
3606dialogId=String(Date.now());
3607writeStorage(dialogKey,dialogId);
3608}
3609function safeAssistantHtml(value){
3610var doc=new DOMParser().parseFromString(String(value||''),'text/html');
3611Array.prototype.forEach.call(doc.querySelectorAll('script,style,iframe,object,embed,form'),function(el){el.remove();});
3612Array.prototype.forEach.call(doc.body.querySelectorAll('*'),function(el){
3613Array.prototype.forEach.call(el.attributes,function(attr){if(/^on/i.test(attr.name)||(attr.name==='href'&&/^\s*javascript:/i.test(attr.value)))el.removeAttribute(attr.name);});
3614});
3615return doc.body.innerHTML;
3616}
3617function renderAssistantMath(target){return window.sinonimMath.render(target);}
3618
3619function addLinkedText(container,text){
3620var pattern=/https:\/\/sinonim\.org\/[A-Za-z0-9_\/-]*/g;
3621var from=0,match;
3622while((match=pattern.exec(text))!==null){
3623container.appendChild(document.createTextNode(text.slice(from,match.index)));
3624var url=match[0];
3625try{
3626var parsed=new URL(url);
3627if(allowedPaths[parsed.pathname]){
3628var link=document.createElement('a');
3629link.href=parsed.pathname;
3630link.textContent=url;
3631container.appendChild(link);
3632}else container.appendChild(document.createTextNode(url));
3633}catch(e){container.appendChild(document.createTextNode(url));}
3634from=match.index+url.length;
3635}
3636container.appendChild(document.createTextNode(text.slice(from)));
3637}
3638function render(){
3639window.sinonimMath.clear(history);
3640history.textContent='';
3641history.classList.toggle('hasMessages',messages.length>0);
3642clear.hidden=messages.length===0;
3643messages.forEach(function(item){
3644var message=document.createElement('div');
3645message.className='homeAssistantMessage '+item.role;
3646if(item.role==='assistant'&&item.html)message.innerHTML=safeAssistantHtml(item.text);
3647else addLinkedText(message,item.text);
3648if(item.files&&item.files.length){
3649var files=document.createElement('div');
3650files.className='homeAssistantMessageFiles';
3651item.files.forEach(function(name){var tag=document.createElement('span');tag.innerHTML='&#128206; ';tag.appendChild(document.createTextNode(name));files.appendChild(tag);});
3652message.appendChild(files);
3653}
3654history.appendChild(message);
3655if(item.role==='assistant')renderAssistantMath(message);
3656});
3657history.scrollTop=history.scrollHeight;
3658}
3659function save(){writeStorage(historyKey,JSON.stringify(messages.slice(-20)));}
3660function addMessage(role,text,files,html){
3661messages.push({role:role,text:String(text||'').trim(),files:Array.isArray(files)?files.slice(0,3):[],html:html===true});
3662messages=messages.slice(-20);
3663save();
3664render();
3665if(role==='assistant'){
3666var answers=history.querySelectorAll('.homeAssistantMessage.assistant');
3667var lastAnswer=answers[answers.length-1];
3668if(lastAnswer)requestAnimationFrame(function(){lastAnswer.scrollIntoView({behavior:'smooth',block:'start'});});
3669}
3670}
3671function limitMessageHtml(type){
3672if(type==='plus')return '<strong>Дневной лимит запросов исчерпан. Попробуйте завтра или <a href="/price_plus">подключите PLUS</a>.</strong>';
3673return '<strong>Дневной лимит запросов для гостей исчерпан. <a href="/login">Войдите</a> или <a href="/reg">зарегистрируйтесь</a>.</strong>';
3674}
3675function renderFiles(){
3676fileList.textContent='';
3677fileList.classList.toggle('hasFiles',selectedFiles.length>0);
3678selectedFiles.forEach(function(file,index){
3679var item=document.createElement('span');
3680item.className='homeAssistantFile';
3681var name=document.createElement('span');
3682name.className='homeAssistantFileName';
3683name.textContent=file.name;
3684var remove=document.createElement('button');
3685remove.type='button';
3686remove.setAttribute('aria-label','Убрать '+file.name);
3687remove.textContent='×';
3688remove.addEventListener('click',function(){selectedFiles.splice(index,1);renderFiles();});
3689item.appendChild(name);item.appendChild(remove);fileList.appendChild(item);
3690});
3691}
3692fileInput.addEventListener('change',function(){
3693status.textContent='';status.classList.remove('isError');
3694var skippedByCount=false;
3695Array.prototype.forEach.call(fileInput.files||[],function(file){
3696if(selectedFiles.length>=3){skippedByCount=true;return;}
3697if(!allowedFile.test(file.name)){status.textContent='Поддерживаются изображения, PDF, Word, TXT, PowerPoint и Excel.';status.classList.add('isError');return;}
3698if(file.size>20*1024*1024){status.textContent='Каждый файл должен быть не больше 20 МБ.';status.classList.add('isError');return;}
3699var duplicate=selectedFiles.some(function(old){return old.name===file.name&&old.size===file.size&&old.lastModified===file.lastModified;});
3700if(!duplicate)selectedFiles.push(file);
3701});
3702if(skippedByCount&&!status.textContent){status.textContent='Можно прикрепить не больше 3 файлов.';status.classList.add('isError');}
3703fileInput.value='';renderFiles();
3704});
3705function showPending(){
3706history.classList.add('hasMessages');
3707var pending=document.createElement('div');
3708pending.className='homeAssistantMessage assistant isPending';
3709pending.innerHTML='<span>Подбираю ответ</span><span class="homeAssistantThinkingDots" aria-hidden="true"><i></i><i></i><i></i></span>';
3710history.appendChild(pending);
3711history.scrollTop=history.scrollHeight;
3712return pending;
3713}
3714function setBusy(busy){
3715send.disabled=busy;
3716clear.disabled=busy;
3717input.disabled=busy;
3718fileInput.disabled=busy;
3719if(attach)attach.classList.toggle('isDisabled',busy);
3720send.querySelector('span').textContent='Спросить';
3721}
3722clear.addEventListener('click',function(){
3723if(!messages.length||!confirm('Очистить историю этого помощника?'))return;
3724messages=[];
3725save();
3726dialogId=String(Date.now());
3727writeStorage(dialogKey,dialogId);
3728status.textContent='';
3729status.classList.remove('isError');
3730render();
3731input.focus();
3732});
3733form.addEventListener('submit',function(event){
3734event.preventDefault();
3735var word=input.value.trim();
3736if(!word||send.disabled)return;
3737if(messages.length===0&&Array.from(word).length<5){
3738var firstNumber=Math.floor(Math.random()*9)+1;
3739var secondNumber=Math.floor(Math.random()*9)+1;
3740var answer=prompt('Решите простой пример, чтобы отправить сообщение: '+firstNumber+' + '+secondNumber+' = ?');
3741if(answer===null)return;
3742if(String(answer).trim()!==String(firstNumber+secondNumber)){
3743alert('Неверный ответ. Попробуйте ещё раз.');
3744return;
3745}
3746}
3747status.textContent='';
3748status.classList.remove('isError');
3749var sentFiles=selectedFiles.slice();
3750var sentFileNames=sentFiles.map(function(file){return file.name;});
3751addMessage('user',word,sentFileNames);
3752input.value='';
3753setBusy(true);
3754var pending=showPending();
3755var body=new FormData();
3756body.append('token',box.dataset.token||'');body.append('word',word);body.append('dialogId',dialogId);body.append('title','Помощник по сайту');body.append('temperature','0.3');body.append('siteAssistant','1');body.append('history',JSON.stringify(messages.slice(-9,-1)));
3757sentFiles.forEach(function(file){body.append('assistantFiles[]',file,file.name);});
3758fetch('/data.php?chatGptGemini=1',{method:'POST',credentials:'same-origin',body:body})
3759.then(function(response){return response.json().then(function(data){if((!response.ok||!data.ok)&&!data.limit)throw new Error(data.text||'Не удалось получить ответ.');return data;});})
3760.then(function(data){
3761if(pending.parentNode)pending.parentNode.removeChild(pending);
3762if(data.limit){
3763addMessage('assistant',limitMessageHtml(data.limitType),[],true);
3764setBusy(false);
3765return;
3766}
3767var answer=String(data.text||'').trim();
3768if(!answer)throw new Error('Не удалось получить ответ. Попробуйте ещё раз.');
3769addMessage('assistant',answer,[],true);
3770selectedFiles=[];renderFiles();
3771setBusy(false);
3772})
3773.catch(function(error){
3774if(pending.parentNode)pending.parentNode.removeChild(pending);
3775status.textContent=error.message||'Не удалось получить ответ. Попробуйте ещё раз.';
3776status.classList.add('isError');
3777setBusy(false);
3778input.focus();
3779});
3780});
3781input.addEventListener('keydown',function(event){
3782if(event.key==='Enter'&&event.shiftKey){event.preventDefault();if(typeof form.requestSubmit==='function')form.requestSubmit();else send.click();}
3783});
3784render();
3785}
3786if(document.readyState==='loading')document.addEventListener('DOMContentLoaded',initHomeAssistant);
3787else initHomeAssistant();
3788})();

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.