LINUX.ORG.RU

Избранные сообщения serg002

LORScriptPack - UserScript, делающий ЛОР удобным

Форум — Linux-org-ru

Фичи:

  • Редактор сообщений с графическим интерфейсом из lorify-ng
  • Нормальное цитирование кнопкой
  • Делает поле ввода сообщения широким по умолчанию
  • Возможность ответить одним сообщением на несколько
  • Возможность обратиться к пользователю по нику
  • Возможность скопировать ник пользователя в профиле
  • Копируемый и сворачиваемый код
  • Показывает местное время вместо московского
  • Поиск google помимо внутреннего
  • При внутреннем поиске сортирует от новых к старым по умолчанию [отключаемая фича]
  • В темах, отличных от black, скрывает новости и галерею, добавляет теги и документацию [отключаемые фичи]
  • Нормальный поиск тегов
  • Скрывает облако тегов
  • В темах, отличных от black, делает только технические разделы в трекере по умолчанию [отключаемая фича]
  • Преобразует ссылки на картинки в сами картинки
  • Выделяет комментарии от автора темы классом by-ts, а его ник - классом ts-nick
  • Выделяет нечетные комментарии классом odd, а четные - классом even
  • Кнопка «Пожаловаться» возле каждого сообщения
  • Прокручиваемый в обе стороны код
  • Скрывает звезды и реакции
  • Выделяет комментарии ТСа цветом

Включает коды из других проектов:

  • LORLocalDate 1.0.0 от post-factum, Creative Commons Attribution 3.0 Unported
  • Date Format 1.2.3, MIT
  • Lorify-ng 3.2.0, MIT

Скриншоты

https://images.linuxforum.ru/images/2024/03/23/IZOBRAZENIE1471e033edbdb898.png
https://images.linuxforum.ru/images/2024/03/23/IZOBRAZENIE.png

Установка

  1. Установите расширение Greasemonkey.
  2. Нажмите «Создать пользовательский скрипт».
  3. Нажмите Ctrl+A и вставьте код скрипта, затем нажмите Ctrl+S.

Установка на телефон

  1. Установите приложение Berry Browser.
  2. Нажмите Settings -> Web -> Userscripts
  3. Нажмите на плюс, затем Edit, вставьте код скрипта, затем нажмите на дискету.

Настройка

Чтобы выключить опцию, поменяйте true на false в строчке с соответствующей опцией:

NEW_TO_OLD_SEARCH_BY_DEFAULT
HIDE_NEWS_AND_GALLERY
ADD_TAGS_AND_DOCS
TECH_ONLY_BY_DEFAULT

Создание юзерстилей

/*Чередующиеся сообщения разных цветов*/

.messages .odd {
    background-color: #FBFCFB;
}

.messages .even {
    background-color: #F0FFF7;
}

/*Кастомизируем вид кнопок редактора сообщений*/

#btn-i {
    font-style: italic;
}

#btn-quote::before {
    content: 'Цитата';
}

#btn-quote span {
    display: none;
}
// ==UserScript==
// @name           LORScriptPack
// @description    Пак скриптов для ЛОРа
// @namespace      linux.org.ru
// @include        http://www.linux.org.ru/*
// @include        https://www.linux.org.ru/*
// @author         damix9
// @license        MIT
// @version        2.0
// ==/UserScript==

/*
MIT License

Copyright (c) 2024 damix9
Copyright (c) 2022 OpenA
Copyright (c) 2007-2009 Steven Levithan <stevenlevithan.com>
Copyright (с) 2013 Oleksandr Natalenko aka post-factum

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

// Настройки
const NEW_TO_OLD_SEARCH_BY_DEFAULT = true;
const HIDE_NEWS_AND_GALLERY = true;
const ADD_TAGS_AND_DOCS = true;
const TECH_ONLY_BY_DEFAULT = true;

// Глобальные константы и переменные
const parser = new DOMParser();
const formats = ['jpeg', 'jpg', 'png', 'webp', 'tif', 'tiff'];
let CommentForm = null;
let tagListWrapper = null;
let divPopup = null; let timer = null;

// Функции

function _setup(el, attrs, events) {

	if (!el)
		return '';

	switch (typeof el) {
		case 'string':
			el = document.createElement(el);
		case 'object':
			for (const key in attrs) {
				attrs[key] === undefined ? el.removeAttribute(key) :
				key === 'html' ? el.innerHTML   = attrs[key] :
				key === 'text' ? el.textContent = attrs[key] :
				key in el    && (el[key]        = attrs[key] ) == attrs[key]
							 &&  el[key]       == attrs[key] || el.setAttribute(key, attrs[key]);
			}
			for (const name in events) {
				if (!events[name])
					continue;
				if (Array.isArray(events[name]))
					events[name].forEach(handler => el.addEventListener(name, handler, false));
				else
					el.addEventListener(name, events[name], false);
			}
	}
	return el;
}

// Возвращает расширение файла, находящегося по адресу url
function _getUrlExt(url) {
  	let oURL = new URL(url);
  	let path = oURL.pathname;
    return path.split('.').pop().toLowerCase();
}

// Возвращает название темы, выбранной в настройках
function getCurrentTheme() {
    let firstLinkNode = document.head.querySelector('link');
    let cssUrl = new URL(firstLinkNode.href);
    return cssUrl.pathname.split('/')[1]
}

// Добавляет CSS к странице
function addCss(sheet) {
  var head = document.head;
  let style = _setup('style', { text: sheet });
  head.appendChild(style);
}

function strContains(str, phrase) {
	return str.indexOf(phrase) !== -1;
}

// В течение 3 секунд показывает всплывающее уведомление с текстом text
function popup(text) {
    clearTimeout(timer);
    divPopup.innerHTML = text;
    divPopup.style.visibility = 'visible';
    divPopup.classList.add('shown');
    timer = setTimeout(() => {
        divPopup.classList.remove('shown');
        timer = setTimeout(() => {
            divPopup.style.visibility = 'hidden';
        }, 1000)
    }, 3000)
}

//******************************************************************************************
//
// A CGI program uses the following syntax to add cookie information to the HTTP header:
// 
// Set-Cookie:   name=value   
// [;EXPIRES=dateValue]   
// [;DOMAIN=domainName]   
// [;PATH=pathName]   
// [;SECURE]
//
// This function sets a client-side cookie as above.  Only first 2 parameters are required
// Rest of the parameters are optional. If no szExpires value is set, cookie is a session cookie.
//
// Prototype : setCookie(szName, szValue [,szExpires] [,szPath] [,szDomain] [,bSecure])
//******************************************************************************************


function setCookie(szName, szValue, szExpires, szPath, szDomain, bSecure)
{
 	var szCookieText = 	   escape(szName) + '=' + escape(szValue);
	szCookieText +=	 	   (szExpires ? '; EXPIRES=' + szExpires.toGMTString() : '');
	szCookieText += 	   (szPath ? '; PATH=' + szPath : '');
	szCookieText += 	   (szDomain ? '; DOMAIN=' + szDomain : '');
	szCookieText += 	   (bSecure ? '; SECURE' : '');
	
	document.cookie = szCookieText;
}

//******************************************************************************************
// This functions reads & returns the cookie value of the specified cookie (by cookie name) 
//
// Prototype : getCookie(szName)
//******************************************************************************************

function getCookie(szName)
{
 	var szValue =	  null;
	if(document.cookie)	   //only if exists
	{
       	var arr = 		  document.cookie.split((escape(szName) + '=')); 
       	if(2 <= arr.length)
       	{
           	var arr2 = 	   arr[1].split(';');
       		szValue  = 	   unescape(arr2[0]);
       	}
	}
	return szValue;
}

//******************************************************************************************
// To delete a cookie, pass name of the cookie to be deleted
//
// Prototype : deleteCookie(szName)
//******************************************************************************************

function deleteCookie(szName)
{
 	var tmp = 	  			 	 getCookie(szName);
	if(tmp) 
	{ setCookie(szName,tmp,(new Date(1))); }
}

// Добавляет к форме отправке сообщения кнопки для редактирования разметки, делает поле ввода сообщения широким
function handleCommentForm(form) {
    const TEXT_AREA    = form.elements.msg, TITLE_AREA = form.elements.title || { value: '' };
	const MARKUP_PANEL = _setup('div', { id: 'markup-panel', class: 'lorcode'});

	for (let attrs of [
		{ lorcode: 'b'  , title: 'Жирный' },
		{ lorcode: 'i'  , title: 'Курсив' },
		{ lorcode: 'u'  , title: 'Подчеркнутый' },
		{ lorcode: 's'  , title: 'Зачеркнутый' },
		{ lorcode: 'em' , title: 'Курсив выделения' },
        { lorcode: 'strong' , title: 'Жирный выделения' },
        { lorcode: 'url' , title: 'Ссылка' },
		{ lorcode: 'list' , title: 'Список' },
		{ lorcode: 'list' , title: 'Нумерованный список', attr: '1' },
        { lorcode: '*'    , title: 'Элемент списка' },
		{ lorcode: 'pre'   , title: 'Преформатированный текст' },
        { lorcode: 'br'    , title: 'С новой строки' },
		{ lorcode: 'code'  , title: 'Код' },
		{ lorcode: 'code'  , title: 'Bash', attr: 'bash' },
		{ lorcode: 'code'  , title: 'HTML', attr: 'html' },
		{ lorcode: 'code'  , title: 'CSS', attr: 'css' },
		{ lorcode: 'code'  , title: 'JavaScript', attr: 'js' },
		{ lorcode: 'code'  , title: 'PHP', attr: 'php' },
		{ lorcode: 'code'  , title: 'SQL', attr: 'sql' },
		{ lorcode: 'code'  , title: 'Си', attr: 'c' },
		{ lorcode: 'code'  , title: 'C++', attr: 'cpp' },
		{ lorcode: 'code'  , title: 'Java', attr: 'java' },
        { lorcode: 'inline', title: 'Код в той же строке' },
		{ lorcode: 'user'  , title: 'Ник пользователя' },
        { lorcode: 'quote' , title: 'Цитата' }
	]) {
		attrs.type  = 'button';
        attrs.id = 'btn-' + attrs.lorcode.toLowerCase();
		attrs.class = 'btn btn-default';
        let label  = _setup('span', { text: attrs.lorcode });
        if (attrs.attr) {
            let a = attrs.attr.toLowerCase();
            attrs.id += `-${a}`;
            label.innerHTML += `=${a}`
        }
        let button = _setup('button', attrs);
        button.appendChild(label);
		MARKUP_PANEL.append(button)
	}
    
    lorcodeMarkup = lorcodeMarkup.bind(TEXT_AREA);

    for (let i = 0; i < MARKUP_PANEL.childNodes.length; i++) {
        let btn = MARKUP_PANEL.childNodes[i];
        btn.addEventListener('click', function(e) {
    		e.preventDefault();
    		const tag  = this.getAttribute('lorcode');
            const attr = this.getAttribute('attr');
            lorcodeMarkup(tag, attr)
    	});
    }
    TEXT_AREA.parentNode.firstElementChild.after(MARKUP_PANEL);
    TEXT_AREA.style = "width: 70em; height: 10em;"
}

// Пишет в поле ввода сообщения текст str, туда, где стоял курсор,
// Ставит курсор в конец добавленного текста и выделяет поле ввода
function injectText(str) {
    const txtArea = CommentForm.elements.msg;
    
    let val = txtArea.value,
        len = txtArea.value.length,
      start = txtArea.selectionStart,
        end = txtArea.selectionEnd;
        
    txtArea.value = val.substring(0, start) + str + val.substring(end);
    
    txtArea.selectionStart = txtArea.selectionEnd = start + str.length;
    
    txtArea.focus()
}

// Берет выделенный текст в указанный тег с указанным аттрибутом
// Если тег br или * то ставит их еще и в начале каждой строки выделенного текста
// Всегда вызывать bind() или call(), передав в них textarea, в которой надо делать разметку
function lorcodeMarkup(tag, attr) {
    const val = this.value,
	      end = this.selectionEnd,
	    start = this.selectionStart,
	    collp = start === end;

	let mtext = '', open = '', close = '', 
        soff = 0, eoff = 0;
        
    mtext = val.substring(start, end);
    
    switch (tag) {
        case 'br':
            open = '[br]';
            if (!collp) 
                mtext = mtext.replace(/\n/gm, '\n'+'[br]');
            break;
        case '*':
            open = '[*]';
            if (!collp)
                mtext = mtext.replace(/\[\/?\*\]/g, '').replace(/\n/gm, '\n'+'[*]');
            break;
        case 'url':
            let uri = prompt('Введите адрес ссылки');
            if (uri) {
                open = `[url=${uri}]`;
                close = '[/url]';
            }
            else {
                return;
            }
            break;
        default:
            open = attr ? `[${tag}=${attr}]` : `[${tag}]`;
            close = `[/${tag}]`;
    }
    
    soff = open.length; eoff = open.length + mtext.length;
    
    this.value = val.substring(0, start) + open + mtext + close + val.substring(end);
    this.focus();
    this.setSelectionRange(start + soff, start + eoff);
    this.dispatchEvent(new InputEvent('input', { bubbles: true }))
}

// Отправлет сообщение с текстом msg в тему c id topic
function sendMessageToTopic(topic, msg) {
    let newTab = open(`https://www.linux.org.ru/add_comment.jsp?topic=${topic}&msg=${msg}`, '_blank');
    newTab.focus()
}

// Обратиться к пользователю по нику
function castUser(nick) {
    injectText('[user]' + nick + '[/user], ')
}

// Цитировать. Аргументы опциональны. Если указаны, то цитировать с ником.
function quote(nick, link) {
    const wSelect = window.getSelection();
    if (wSelect.isCollapsed) {
        return
    }
    let simple = (nick === undefined) && (link === undefined);
    let text = simple ? '' : '[user]' + nick + '[/user] [url=' + link + ']пишет[/url]:\n';
    text += '[quote]' + wSelect.toString().trim() + '[/quote]' + '\n';
    injectText(text)
}

// Отправить модераторам ссылку на сообщение с нарушением правил
function reportHam(link) {
    
    let violation = prompt('Введите текст жалобы');
    
    if (violation == null) {
        return
    }
    
    let specTopicId = getCookie('SPECTOPIC_ID');
    let text = link;
    
    if (violation != "") {
        text += '%0D%0A%5Bbr%5D' + violation; // перенос строки и [br]
    }
    
    if (!(specTopicId)) {
        alert('Не задан спецтопик!\nОткройте его (Форум -> Linux-org-ru -> Ссылки на некорректные сообщения) и нажмите кнопку \"Это спецтопик\".');
        return
    }
    
    sendMessageToTopic(specTopicId, text)
}

// Сохраняет ID текущей темы в cookie SPECTOPIC_ID
function itsSpecTopic() {
    let path = window.location.pathname;
    let topicId = path.split('/')[3];
    let expires = new Date();
    expires.setDate(expires.getDate() + 60);
    setCookie('SPECTOPIC_ID', topicId, expires, '/');
    alert('Спецтопик установлен')
}

let firstLetter = '';

// Обновляет отображаемый список тегов, показывает теги, соответстующие 
// поисковому запросу, т.е. начинающиеся на строку, содержащуюся в поле ввода
async function searchTags(e) {
    let query = e.target.value.trim().toLowerCase();
    let queryFirstLetter = query.charAt(0);
    
    if (queryFirstLetter != firstLetter) {
        
        firstLetter = queryFirstLetter;
        
        if (firstLetter != '') {
            let response = await fetch('https://www.linux.org.ru/tags/' + firstLetter);
            let txt = await response.text();
            let oDoc = parser.parseFromString(txt, 'text/html');
            
            let content = oDoc.getElementById('bd');
            let tagList = content.getElementsByTagName('ul')[0];
            
            let oldTagList = tagListWrapper.firstChild;
            
            if (oldTagList) {
                tagListWrapper.replaceChild(tagList, oldTagList)
            }
            else {
                tagListWrapper.appendChild(tagList);
            }
        }
    }
    
    let tagList = tagListWrapper.firstChild;
    
    if (tagList) {
        for (let i = 0, c = tagList.children.length; i < c; i++) {
            let tag = tagList.children[i];
            let s = tag.children[0].text;
            if (s.startsWith(query)) {
                tag.style.display = 'list-item'
            }
            else {
                tag.style.display = 'none'
            }
        }
    }
}

// Добавляет к сообщению (теме или комментарию) недостающие ссылки внизу.
// Передать DOM Node элемента div.reply сообщения и логин его отправителя.
function addReplyLinks(replyNode, author) {
    
    let links = replyNode.firstElementChild;
    let linkToComment = links.lastElementChild.firstElementChild.href;
    
    let nick = _setup(
        'a',
        { text: 'Ник', href: linkToComment + '#nick' },
        { click: (e) => { e.preventDefault(); castUser(author) } }
    );
    let report = _setup(
        'a',
        { text: 'Пожаловаться', href: linkToComment + '#report' },
        { click: (e) => { e.preventDefault(); reportHam(linkToComment) } }
    );
    let simpleQuote = _setup(
        'a',
        { text: 'Цитировать', href: linkToComment + '#quote' },
        { click: (e) => { e.preventDefault(); quote() } }
    );
    let advancedQuote = _setup(
        'a',
        { text: 'Цитировать с ником', href: linkToComment + '#quote' },
        { click: (e) => { e.preventDefault(); quote(author, linkToComment) } }
    );
    
    let nickLi = _setup('li');          nickLi.appendChild(nick);
    let reportLi = _setup('li');        reportLi.appendChild(report);
    let quoteLi = _setup('li');         quoteLi.appendChild(simpleQuote);
    let advancedQuoteLi = _setup('li'); advancedQuoteLi.appendChild(advancedQuote);
    
    links.firstElementChild.before(nickLi);
    nickLi.after(' ');
    links.appendChild(reportLi);
    reportLi.before(' ');
    links.appendChild(quoteLi);
    quoteLi.before(' ');
    links.appendChild(advancedQuoteLi);
    advancedQuoteLi.before(' ')
}

// Принимает событие клика, копирует в буфер обмена текст поля, на котором кликнули
function copyNick (e) {
    e.target.select();
    if (document.execCommand('copy')) {
      	popup('Текст скопирован в буфер обмена')
    }
}

// Принимает блок с кодом, выделяет его текст
function selectCode (codeNode) {
    let range = document.createRange();
    range.selectNodeContents(codeNode.firstElementChild);
    
    let selection = window.getSelection();
    selection.removeAllRanges();
    selection.addRange(range)
}

// Принимает блок с кодом, сворачивает/разворачивает его
function toggleCodeSpoiler(codeNode) {
    codeNode.classList.toggle('unspoiled')
}

/*
 * Date Format 1.2.3
 * (c) 2007-2009 Steven Levithan <stevenlevithan.com>
 * MIT license
 *
 * Includes enhancements by Scott Trenda <scott.trenda.net>
 * and Kris Kowal <cixar.com/~kris.kowal/>
 *
 * Accepts a date, a mask, or a date and a mask.
 * Returns a formatted version of the given date.
 * The date defaults to the current date/time.
 * The mask defaults to dateFormat.masks.default.
 */

var dateFormat = function () {
	var	token = /d{1,4}|m{1,4}|yy(?:yy)?|([HhMsTt])\1?|[LloSZ]|"[^"]*"|'[^']*'/g,
		timezone = /\b(?:[PMCEA][SDP]T|(?:Pacific|Mountain|Central|Eastern|Atlantic) (?:Standard|Daylight|Prevailing) Time|(?:GMT|UTC)(?:[-+]\d{4})?)\b/g,
		timezoneClip = /[^-+\dA-Z]/g,
		pad = function (val, len) {
			val = String(val);
			len = len || 2;
			while (val.length < len) val = "0" + val;
			return val;
		};

	// Regexes and supporting functions are cached through closure
	return function (date, mask, utc) {
		var dF = dateFormat;

		// You can't provide utc if you skip other args (use the "UTC:" mask prefix)
		if (arguments.length == 1 && Object.prototype.toString.call(date) == "[object String]" && !/\d/.test(date)) {
			mask = date;
			date = undefined;
		}

		// Passing date through Date applies Date.parse, if necessary
		date = date ? new Date(date) : new Date;
		if (isNaN(date)) throw SyntaxError("invalid date");

		mask = String(dF.masks[mask] || mask || dF.masks["default"]);

		// Allow setting the utc argument via the mask
		if (mask.slice(0, 4) == "UTC:") {
			mask = mask.slice(4);
			utc = true;
		}

		var	_ = utc ? "getUTC" : "get",
			d = date[_ + "Date"](),
			D = date[_ + "Day"](),
			m = date[_ + "Month"](),
			y = date[_ + "FullYear"](),
			H = date[_ + "Hours"](),
			M = date[_ + "Minutes"](),
			s = date[_ + "Seconds"](),
			L = date[_ + "Milliseconds"](),
			o = utc ? 0 : date.getTimezoneOffset(),
			flags = {
				d:    d,
				dd:   pad(d),
				ddd:  dF.i18n.dayNames[D],
				dddd: dF.i18n.dayNames[D + 7],
				m:    m + 1,
				mm:   pad(m + 1),
				mmm:  dF.i18n.monthNames[m],
				mmmm: dF.i18n.monthNames[m + 12],
				yy:   String(y).slice(2),
				yyyy: y,
				h:    H % 12 || 12,
				hh:   pad(H % 12 || 12),
				H:    H,
				HH:   pad(H),
				M:    M,
				MM:   pad(M),
				s:    s,
				ss:   pad(s),
				l:    pad(L, 3),
				L:    pad(L > 99 ? Math.round(L / 10) : L),
				t:    H < 12 ? "a"  : "p",
				tt:   H < 12 ? "am" : "pm",
				T:    H < 12 ? "A"  : "P",
				TT:   H < 12 ? "AM" : "PM",
				Z:    utc ? "UTC" : (String(date).match(timezone) || [""]).pop().replace(timezoneClip, ""),
				o:    (o > 0 ? "-" : "+") + pad(Math.floor(Math.abs(o) / 60) * 100 + Math.abs(o) % 60, 4),
				S:    ["th", "st", "nd", "rd"][d % 10 > 3 ? 0 : (d % 100 - d % 10 != 10) * d % 10]
			};

		return mask.replace(token, function ($0) {
			return $0 in flags ? flags[$0] : $0.slice(1, $0.length - 1);
		});
	};
}();

// Some common format strings
dateFormat.masks = {
	"default":      "ddd mmm dd yyyy HH:MM:ss",
	shortDate:      "m/d/yy",
	mediumDate:     "mmm d, yyyy",
	longDate:       "mmmm d, yyyy",
	fullDate:       "dddd, mmmm d, yyyy",
	shortTime:      "h:MM TT",
	mediumTime:     "h:MM:ss TT",
	longTime:       "h:MM:ss TT Z",
	isoDate:        "yyyy-mm-dd",
	isoTime:        "HH:MM:ss",
	isoDateTime:    "yyyy-mm-dd'T'HH:MM:ss",
	isoUtcDateTime: "UTC:yyyy-mm-dd'T'HH:MM:ss'Z'"
};

// Internationalization strings
dateFormat.i18n = {
	dayNames: [
		"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat",
		"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"
	],
	monthNames: [
		"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
		"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"
	]
};

// Принимает событие отправки формы поиска в Google.
// Перенаправляет пользователя на страницу поиска Google по сайту
function searchByGoogle(event) {
    event.preventDefault();
    let term = document.getElementById('qg').value;
    let redirect = 'https://www.google.com/search?q=' + term + ' site:linux.org.ru';
    window.location.href = redirect
}


// Основной код

const LOR_CSS = `
    .code {
        overflow: scroll;
    }

    .code.spoiled {
        overflow-y: scroll;
    }

    .code pre {
        overflow-x: unset;
        overflow-y: unset;
        word-wrap: initial;
    }

    pre code {
    	white-space: pre;
    }

    .code .spoiler-open {
        display: none;
    }

    .stars {
        display: none;
    }

    .reactions {
        display: none;
    }

    #markup-panel .btn {
        margin: 2px;
    }

    .tag-cloud {
        display: none;
    }

    #qg {
        margin-right: 5px;
    }

    a img.lorpic {
        max-width: 700px;
    }

    @media (max-width: 900px) {
        a img.lorpic {
            max-width: 60vw;
            height: auto;
        }
        
        .popup-message {
            width: 90vw;
        }
    }

    .user-tag {
        display: none;
    }

    .popup-message {
        visibility: hidden;
        opacity: 0;
        position: fixed;
        right: 20px;
        bottom: 20px;
        padding: 10px;
        width: 300px;
        border-style: solid;
        transition-property: opacity;
        transition-duration: 1s;
        transition-timing-function: linear;
    }

    .shown {
        visibility: visible;
        opacity: 1;
    }
`;
const LOR_DARK = `
    .messages .by-ts {
        background-color: #3d2300;
    }

    a img.lorpic {
        background-color: #FFC;
    }

    .popup-message {
        border-width: thin;
        border-color: #8ae234;
        background-color: #033;
    }
`;
const LOR_LIGHT = `
    .messages .by-ts {
        background-color: #FFC;
    }

    .popup-message {
        border-width: medium;
        border-color: #000;
        background-color: #FC6;
    }
    
    div.code {
        background-color: #FFF;
    }
`;

const LOR_BLACK = `
    .ts-nick::after {
        content: '[ТС]';
        color: #F30;
        font-weight: bold;
    }

    a img.lorpic {
        background-color: #FFC;
    }

    .popup-message {
        border-width: thin;
        border-color: #8ae234;
        background-color: #033;
    }
`;

const theme = getCurrentTheme();

addCss(LOR_CSS);

if (theme == 'tango') {
    addCss(LOR_DARK)
}
else if (theme == 'waltz') {
    addCss(LOR_LIGHT)
}
else if (theme == 'black') {
    addCss(LOR_BLACK)
}

CommentForm = document.forms.commentForm || document.forms.messageForm;

if (CommentForm) {
    handleCommentForm(CommentForm);
}

let firstSign = document.querySelector('footer div.sign');
let TS = firstSign ? firstSign.firstElementChild.textContent : null;
if (TS) {
  	let nextBlock = firstSign.parentElement.nextElementSibling;
    if (nextBlock.className == "reply") {
    	addReplyLinks(nextBlock, TS)
    }
}

let comments = document.getElementById('comments') && document.getElementById('comments').getElementsByClassName('msg-container');

if (comments) {

    for (let i = 0, c = comments.length; i < c; i++) {
        
        let sign  = comments[i].getElementsByClassName('sign')[0];
        let reply = comments[i].getElementsByClassName('reply')[0];
        
        let author = sign.firstElementChild.textContent;
        let parentNode = comments[i].parentElement;
        
        if (author == TS) {
            parentNode.classList.add('by-ts');
            sign.classList.add('ts-nick')
        }
        
        // Если индекс комментария в массиве comments четный,
        if ((i % 2) == 0) {
            // то по счету комментарий нечетный
            parentNode.classList.add('odd')
        }
        else {
            parentNode.classList.add('even')
        }
        // Первый комментарий имеет индекс 0
        
        addReplyLinks(reply, author)
    }
}


// Делаем местное время

let times = document.getElementsByTagName("time");
let c = times.length;

for (i = 0; i < c; i++) {
    let attrTime = new Date(times[i].getAttribute("datetime", 0));
	let nowTime = new Date();
	let diff = Math.round(nowTime.getTime() / 1000) - Math.round(attrTime.getTime() / 1000);
    
    let attrDay = new Date(attrTime.getTime()); 
    let nowDay = new Date(nowTime.getTime());
    attrDay.setHours(0, 0, 0, 0);
    nowDay.setHours(0, 0, 0, 0);
    let today = attrDay.getTime() == nowDay.getTime();
    let yesterday = (attrDay.getTime() + 86400000) == nowDay.getTime();
    let timeText;
    
    if ((strContains(document.URL, "/tracker") || strContains(document.URL, "/notifications"))) { 
        let minutes = Math.ceil(diff / 60);
        if (minutes < 60) {
            timeText = minutes + " мин."
        }
        else if (today) {
            timeText = dateFormat(attrTime.getTime(), "HH:MM:ss")
        }
        else if (yesterday) {
            timeText = "вчера " + dateFormat(attrTime.getTime(), "HH:MM:ss")
        }
        else {
            timeText = dateFormat(attrTime.getTime(), "dd.mm.yy HH:MM")
        }
    }
    else {
        if (today) {
            timeText = "сегодня " + dateFormat(attrTime.getTime(), "HH:MM")
        }
        else if (yesterday) {
            timeText = "вчера " + dateFormat(attrTime.getTime(), "HH:MM")
        }
        else {
            timeText = dateFormat(attrTime.getTime(), "dd.mm.yyyy HH:MM")
        }
    }
    times[i].innerHTML = timeText
}


// Настраиваем меню

if (theme == 'tango' || theme == 'waltz') {
    let menu = document.getElementsByClassName('menu')[0].getElementsByTagName('ul')[0]; // Главное меню

    if (TECH_ONLY_BY_DEFAULT) {
        menu.children[4].firstElementChild.href = '/tracker/?filter=tech';
    }

    if (HIDE_NEWS_AND_GALLERY) {
        // Убираем первые два раздела - новости и галерею
        menu.firstElementChild.remove();
        menu.firstElementChild.remove();
    }

    if (ADD_TAGS_AND_DOCS) {
        let itmTags = _setup('li');  let itmWiki = _setup('li');
        let linkTags = _setup(
            'a',
            { href: '/tags/', text: 'Теги' }
        );
        let linkWiki = _setup(
            'a',
            { href: 'http://lorwiki.zhbert.ru/', text: 'Документация', target: '_blank' }
        );
        itmTags.appendChild(linkTags); itmWiki.appendChild(linkWiki);

        menu.appendChild(itmTags);
        menu.appendChild(itmWiki);
        itmWiki.before(' ');
    }
}

// =================================================================================

let related = document.getElementById('related-topics');
if (related) {
    let btnItsSpectopic = _setup(
        'input',
        { type: 'button', value: 'Это спецтопик' },
        { click: itsSpecTopic }
    );
    related.before(btnItsSpectopic);
}

// Добавляем всплывающее уведомление
divPopup = _setup(
    'div',
    { class: 'popup-message' }
);
document.body.appendChild(divPopup);


// Сворачиваемый и копируемый код

let codes = document.querySelectorAll('div.code');

for (let i = 0, c = codes.length; i < c; i++) {
    let code = codes[i];
    let linkToggleCodeSpoiler = _setup(
        'a',
        { href: '#toggleCodeSpoiler', text: 'Развернуть/Свернуть' },
        { click: (e) => { e.preventDefault(); toggleCodeSpoiler(code) } }
    );
    let linkSelectCode = _setup(
        'a',
        { href: '#selectCode', text: 'Выделить' },
        { click: (e) => { e.preventDefault(); selectCode(code) } }
    );
  	let codeControls = _setup(
      	'div',
      	{ class: 'code-controls' }
     );
  	codeControls.appendChild(linkToggleCodeSpoiler);
  	codeControls.appendChild(linkSelectCode);
  	
    linkToggleCodeSpoiler.before('[');
    linkSelectCode.before('] [');
    linkSelectCode.after(']');
  
  	code.before(codeControls)
}


// Картинки

let links = document.getElementsByTagName('a');

for (i = 0, c = links.length; i < c; i++) {
    let link = links[i];
    if (!link.getAttribute('itemprop')) {
        let url = link.href;
        let ext = _getUrlExt(url);
        if (formats.includes(ext)) {
            let content = link.textContent;
            if (content != 'Просмотр') {
                let image = _setup(
                    'img',
                    { class: 'lorpic', src: url, title: content, alt: url },
                );
                link.innerHTML = '';
                link.target = '_blank';
                link.appendChild(image)
            }
        }
    }
}

// ================================================================================

if (window.location.pathname == '/tags/') {
    tagSearchBar = _setup('div', { style: 'margin: 20px 0' });
    let tagSearchInput = _setup(
        'input', 
        { placeholder: 'Поиск меток ...', size: 30, autofocus: true }, 
        { keyup: searchTags }
    );
    tagSearchBar.appendChild(tagSearchInput);
    let tagsFirstLetters = document.getElementsByClassName('tags-first-letters')[0];
    tagsFirstLetters.after(tagSearchBar);
    tagListWrapper = _setup('span');
    tagSearchBar.after(tagListWrapper);
  	
  	document.getElementsByTagName('section')[0].className = 'tag-cloud'
}

if (window.location.pathname.startsWith('/people')) {
    let profile = document.getElementsByClassName('vcard')[0]
    
    if (profile) {
        let nick = profile.getElementsByClassName('nickname')[0];
        let nickName = '[user]' + nick.textContent.trim() + '[/user]';
        
      	profile.appendChild(_setup('br'));
        profile.appendChild(_setup('b', { text: 'Копировать ник: ' }));
        let copyInput = _setup(
            'input', 
            { value: nickName, size: nickName.length, readOnly: true },
            { click: copyNick }
        );
        profile.appendChild(copyInput);
        profile.appendChild(_setup('br'))
    }
}

if ((window.location.pathname == '/search.jsp') && (window.location.search == '')) {
    let searchContainer = document.getElementById('bd');
    let hdrGoogle = _setup(
        'h1',
        { text: 'Поиск в Google' }
    );
    let inpGoogle = _setup(
        'input',
        { id: 'qg', size: 50, class: 'input-lg', type: 'search', maxlength: 250 }
    );
    let btnGoogle = _setup(
        'button',
        { class: 'btn btn-primary', text: 'Поиск' }
    )
    let googleSearchBar = _setup(
        'form', 
        { class: 'control-group' },
        { submit: searchByGoogle }
    );
    googleSearchBar.appendChild(hdrGoogle);
    googleSearchBar.appendChild(inpGoogle);
    googleSearchBar.appendChild(btnGoogle);
    
    searchContainer.appendChild(googleSearchBar);
    
    let defaultSortOrder = _setup(
        'input',
        { type: 'hidden', name: 'sort', value: 'DATE' }
    );
    
    if (NEW_TO_OLD_SEARCH_BY_DEFAULT) {
        searchContainer.getElementsByTagName('form')[0].appendChild(defaultSortOrder)
    }
}

 

damix9
()

Как сделать компактней панель тегов?

Форум — Desktop

В awesome расстояние между тегами меньше и выглядит это аккуратней. Может какой-то патч есть, которы позволяет менять размер отступов между тегами?(делать их меньше по ширине(width))

 ,

serg002
()

Лор, я тебя теряю!

Форум — Admin

Сделал себе домашний маршрутизатор. Веб на клиентах доступен, но не весь — часть сайтов (и этот среди них) не работают. На самом маршрутизаторе всё открывается.

Схема:

[cli] 192.168.0.2/29
  |
----- 192.168.0.0/29
  |
 br0: 192.168.0.1/29
[rtr]
 ppp0: 10.100.9.5/32
  |
----- pppoe
  |
[isp]  10.100.0.1/32
  |
 ...

Клиент:

root@cli:~# curl -v -k https://www.linux.org.ru -o /dev/null
* Adding handle: conn: 0x7fab74004000
* Adding handle: send: 0
* Adding handle: recv: 0
* Curl_addHandleToPipeline: length: 1
* - Conn 0 (0x7fab74004000) send_pipe: 1, recv_pipe: 0
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0* About to connect() to www.linux.org.ru port 443 (#0)
*   Trying 178.248.233.6...
* Connected to www.linux.org.ru (178.248.233.6) port 443 (#0)
  0     0    0     0    0     0      0      0 --:--:--  0:04:59 --:--:--     0* Operation timed out after 300448 milliseconds with 0 out of 0 bytes received
  0     0    0     0    0     0      0      0 --:--:--  0:05:00 --:--:--     0
* Closing connection 0
curl: (28) Operation timed out after 300448 milliseconds with 0 out of 0 bytes received

Роутер:

root@rtr:~# nft list ruleset
table inet home {
        chain input {
                type filter hook input priority filter; policy drop;
                ct state established,related accept
                iifname vmap { "lo" : accept, "br0" : jump input_int }
        }
        chain input_int {
                icmp type echo-request accept
                ip saddr 192.168.0.2 tcp dport 22 accept
        }
        chain forward {
                type filter hook forward priority filter; policy drop;
                ct state established,related accept
                iifname "br0" accept
        }
        chain postrouting {
                type nat hook postrouting priority srcnat; policy accept;
                oifname "ppp0" masquerade
        }
}

В первом приближении пакеты проходят:

root@rtr:~# tcpdump -i br0
21:07:28.759275 IP 192.168.0.2.62653 > dns.google.domain: 11903+ A? www.linux.org.ru. (34)
21:07:28.759867 IP 192.168.0.2.55189 > dns.google.domain: 56192+ AAAA? www.linux.org.ru. (34)
21:07:28.779138 IP dns.google.domain > 192.168.0.2.62653: 11903 1/0/0 A 178.248.233.6 (50)
21:07:28.781905 IP 192.168.0.2.51398 > 178.248.233.6.https: Flags [S], seq 4008111092, win 65535, options [mss 1460,nop,wscale 4,nop,nop,TS val 1102006709 ecr 0,sackOK,eol], length 0
21:07:28.785758 IP 178.248.233.6.https > 192.168.0.2.51398: Flags [S.], seq 3128115892, ack 4008111093, win 5792, options [mss 1460,nop,wscale 9,sackOK,TS val 289594195 ecr 1102006709], length 0
21:07:28.787025 IP 192.168.0.2.51398 > 178.248.233.6.https: Flags [.], ack 1, win 8235, options [nop,nop,TS val 1102006714 ecr 289594195], length 0
21:07:28.790257 IP 192.168.0.2.51398 > 178.248.233.6.https: Flags [P.], seq 1:179, ack 1, win 8235, options [nop,nop,TS val 1102006717 ecr 289594195], length 178
21:07:28.793882 IP 178.248.233.6.https > 192.168.0.2.51398: Flags [.], ack 179, win 60, options [nop,nop,TS val 289594259 ecr 1102006717], length 0
21:07:28.798772 IP 178.248.233.6.https > 192.168.0.2.51398: Flags [P.], seq 2897:4038, ack 179, win 60, options [nop,nop,TS val 289594264 ecr 1102006717], length 1141
21:07:28.800222 IP 192.168.0.2.51398 > 178.248.233.6.https: Flags [.], ack 1, win 8235, options [nop,nop,TS val 1102006726 ecr 289594259,nop,nop,sack 1 {2897:4038}], length 0
21:07:32.290387 IP 192.168.0.2.51315 > 178.248.233.6.https: Flags [.], ack 817493168, win 8235, length 0
21:07:33.798702 IP 178.248.233.6.https > 192.168.0.2.51398: Flags [F.], seq 4038, ack 179, win 60, options [nop,nop,TS val 289599264 ecr 1102006726], length 0
21:07:33.995497 IP 192.168.0.2.51398 > 178.248.233.6.https: Flags [.], ack 1, win 8235, options [nop,nop,TS val 1102011916 ecr 289594259,nop,nop,sack 1 {2897:4038}], length 0
21:07:51.112302 IP 192.168.0.2.51317 > 178.248.233.6.https: Flags [.], ack 1788523727, win 8235, length 0
21:07:57.637922 IP 192.168.0.2.51377 > 178.248.233.6.https: Flags [.], ack 3270250373, win 8235, length 0
21:08:10.959408 IP 192.168.0.2.51378 > 178.248.233.6.https: Flags [.], ack 1734482898, win 8235, length 0
21:08:34.005182 IP 192.168.0.2.51398 > 178.248.233.6.https: Flags [.], ack 1, win 8235, length 0
21:08:47.300446 IP 192.168.0.2.51315 > 178.248.233.6.https: Flags [.], ack 1, win 8235, length 0
21:08:57.645557 IP 192.168.0.2.51377 > 178.248.233.6.https: Flags [.], ack 1, win 8235, length 0
21:09:06.122845 IP 192.168.0.2.51317 > 178.248.233.6.https: Flags [.], ack 1, win 8235, length 0
21:09:10.966172 IP 192.168.0.2.51378 > 178.248.233.6.https: Flags [.], ack 1, win 8235, length 0
21:09:34.514986 IP 192.168.0.2.51398 > 178.248.233.6.https: Flags [.], ack 1, win 8235, length 0
21:09:58.156700 IP 192.168.0.2.51377 > 178.248.233.6.https: Flags [.], ack 1, win 8235, length 0
21:10:02.311781 IP 192.168.0.2.51315 > 178.248.233.6.https: Flags [.], ack 1, win 8235, length 0
21:10:10.974358 IP 192.168.0.2.51378 > 178.248.233.6.https: Flags [.], ack 1, win 8235, length 0
21:10:21.132956 IP 192.168.0.2.51317 > 178.248.233.6.https: Flags [.], ack 1, win 8235, length 0
21:10:34.521112 IP 192.168.0.2.51398 > 178.248.233.6.https: Flags [.], ack 1, win 8235, length 0
21:11:34.526793 IP 192.168.0.2.51398 > 178.248.233.6.https: Flags [.], ack 1, win 8235, length 0
21:12:29.224679 IP 192.168.0.2.51398 > 178.248.233.6.https: Flags [F.], seq 179, ack 1, win 8235, options [nop,nop,TS val 1102307109 ecr 289594259,nop,nop,sack 1 {2897:4038}], length 0
21:12:29.525558 IP 192.168.0.2.51398 > 178.248.233.6.https: Flags [F.], seq 179, ack 1, win 8235, options [nop,nop,TS val 1102307410 ecr 289594259,nop,nop,sack 1 {2897:4038}], length 0
21:12:29.829200 IP 192.168.0.2.51398 > 178.248.233.6.https: Flags [F.], seq 179, ack 1, win 8235, options [nop,nop,TS val 1102307713 ecr 289594259,nop,nop,sack 1 {2897:4038}], length 0
21:12:30.233351 IP 192.168.0.2.51398 > 178.248.233.6.https: Flags [F.], seq 179, ack 1, win 8235, options [nop,nop,TS val 1102308117 ecr 289594259,nop,nop,sack 1 {2897:4038}], length 0
21:12:30.741193 IP 192.168.0.2.51398 > 178.248.233.6.https: Flags [F.], seq 179, ack 1, win 8235, options [nop,nop,TS val 1102308622 ecr 289594259,nop,nop,sack 1 {2897:4038}], length 0
21:12:31.447212 IP 192.168.0.2.51398 > 178.248.233.6.https: Flags [F.], seq 179, ack 1, win 8235, options [nop,nop,TS val 1102309329 ecr 289594259,nop,nop,sack 1 {2897:4038}], length 0
21:12:32.662818 IP 192.168.0.2.51398 > 178.248.233.6.https: Flags [F.], seq 179, ack 1, win 8235, options [nop,nop,TS val 1102310541 ecr 289594259,nop,nop,sack 1 {2897:4038}], length 0
21:12:34.580798 IP 192.168.0.2.51398 > 178.248.233.6.https: Flags [.], ack 1, win 8235, length 0
21:12:34.782531 IP 192.168.0.2.51398 > 178.248.233.6.https: Flags [F.], seq 179, ack 1, win 8235, options [nop,nop,TS val 1102312662 ecr 289594259,nop,nop,sack 1 {2897:4038}], length 0
21:12:36.906159 IP 192.168.0.2.51398 > 178.248.233.6.https: Flags [F.], seq 179, ack 1, win 8235, options [nop,nop,TS val 1102314783 ecr 289594259,nop,nop,sack 1 {2897:4038}], length 0
21:12:39.029305 IP 192.168.0.2.51398 > 178.248.233.6.https: Flags [F.], seq 179, ack 1, win 8235, options [nop,nop,TS val 1102316904 ecr 289594259,nop,nop,sack 1 {2897:4038}], length 0
21:12:41.652656 IP 192.168.0.2.51398 > 178.248.233.6.https: Flags [F.], seq 179, ack 1, win 8235, options [nop,nop,TS val 1102319524 ecr 289594259,nop,nop,sack 1 {2897:4038}], length 0
21:12:43.775462 IP 192.168.0.2.51398 > 178.248.233.6.https: Flags [F.], seq 179, ack 1, win 8235, options [nop,nop,TS val 1102321645 ecr 289594259,nop,nop,sack 1 {2897:4038}], length 0
21:12:45.998680 IP 192.168.0.2.51398 > 178.248.233.6.https: Flags [F.], seq 179, ack 1, win 8235, options [nop,nop,TS val 1102323864 ecr 289594259,nop,nop,sack 1 {2897:4038}], length 0
21:12:48.121683 IP 192.168.0.2.51398 > 178.248.233.6.https: Flags [R.], seq 180, ack 1, win 8235, length 0
root@rtr:~# tcpdump -i ppp0
21:07:28.759660 IP 10.100.9.5.62653 > dns.google.domain: 11903+ A? www.linux.org.ru. (34)
21:07:28.760106 IP 10.100.9.5.55189 > dns.google.domain: 56192+ AAAA? www.linux.org.ru. (34)
21:07:28.778960 IP dns.google.domain > 10.100.9.5.62653: 11903 1/0/0 A 178.248.233.6 (50)
21:07:28.782236 IP 10.100.9.5.51398 > 178.248.233.6.https: Flags [S], seq 4008111092, win 65535, options [mss 1460,nop,wscale 4,nop,nop,TS val 1102006709 ecr 0,sackOK,eol], length 0
21:07:28.785532 IP 178.248.233.6.https > 10.100.9.5.51398: Flags [S.], seq 3128115892, ack 4008111093, win 5792, options [mss 1460,nop,wscale 9,sackOK,TS val 289594195 ecr 1102006709], length 0
21:07:28.787245 IP 10.100.9.5.51398 > 178.248.233.6.https: Flags [.], ack 1, win 8235, options [nop,nop,TS val 1102006714 ecr 289594195], length 0
21:07:28.790477 IP 10.100.9.5.51398 > 178.248.233.6.https: Flags [P.], seq 1:179, ack 1, win 8235, options [nop,nop,TS val 1102006717 ecr 289594195], length 178
21:07:28.793666 IP 178.248.233.6.https > 10.100.9.5.51398: Flags [.], ack 179, win 60, options [nop,nop,TS val 289594259 ecr 1102006717], length 0
21:07:28.798524 IP 178.248.233.6.https > 10.100.9.5.51398: Flags [P.], seq 2897:4038, ack 179, win 60, options [nop,nop,TS val 289594264 ecr 1102006717], length 1141
21:07:28.800404 IP 10.100.9.5.51398 > 178.248.233.6.https: Flags [.], ack 1, win 8235, options [nop,nop,TS val 1102006726 ecr 289594259,nop,nop,sack 1 {2897:4038}], length 0
21:07:32.290679 IP 10.100.9.5.51315 > 178.248.233.6.https: Flags [.], ack 817493168, win 8235, length 0
21:07:33.798448 IP 178.248.233.6.https > 10.100.9.5.51398: Flags [F.], seq 4038, ack 179, win 60, options [nop,nop,TS val 289599264 ecr 1102006726], length 0
21:07:33.995834 IP 10.100.9.5.51398 > 178.248.233.6.https: Flags [.], ack 1, win 8235, options [nop,nop,TS val 1102011916 ecr 289594259,nop,nop,sack 1 {2897:4038}], length 0
21:07:51.112551 IP 10.100.9.5.51317 > 178.248.233.6.https: Flags [.], ack 1788523727, win 8235, length 0
21:07:57.638168 IP 10.100.9.5.51377 > 178.248.233.6.https: Flags [.], ack 3270250373, win 8235, length 0
21:08:10.959740 IP 10.100.9.5.51378 > 178.248.233.6.https: Flags [.], ack 1734482898, win 8235, length 0
21:08:34.005553 IP 10.100.9.5.51398 > 178.248.233.6.https: Flags [.], ack 1, win 8235, length 0
21:08:47.300740 IP 10.100.9.5.51315 > 178.248.233.6.https: Flags [.], ack 1, win 8235, length 0
21:08:57.645805 IP 10.100.9.5.51377 > 178.248.233.6.https: Flags [.], ack 1, win 8235, length 0
21:09:06.123174 IP 10.100.9.5.51317 > 178.248.233.6.https: Flags [.], ack 1, win 8235, length 0
21:09:10.966463 IP 10.100.9.5.51378 > 178.248.233.6.https: Flags [.], ack 1, win 8235, length 0
21:09:34.515235 IP 10.100.9.5.51398 > 178.248.233.6.https: Flags [.], ack 1, win 8235, length 0
21:09:58.156944 IP 10.100.9.5.51377 > 178.248.233.6.https: Flags [.], ack 1, win 8235, length 0
21:10:02.312030 IP 10.100.9.5.51315 > 178.248.233.6.https: Flags [.], ack 1, win 8235, length 0
21:10:10.974612 IP 10.100.9.5.51378 > 178.248.233.6.https: Flags [.], ack 1, win 8235, length 0
21:10:21.133238 IP 10.100.9.5.51317 > 178.248.233.6.https: Flags [.], ack 1, win 8235, length 0
21:10:34.521362 IP 10.100.9.5.51398 > 178.248.233.6.https: Flags [.], ack 1, win 8235, length 0
21:11:34.527046 IP 10.100.9.5.51398 > 178.248.233.6.https: Flags [.], ack 1, win 8235, length 0
21:12:29.224932 IP 10.100.9.5.51398 > 178.248.233.6.https: Flags [F.], seq 179, ack 1, win 8235, options [nop,nop,TS val 1102307109 ecr 289594259,nop,nop,sack 1 {2897:4038}], length 0
21:12:29.525815 IP 10.100.9.5.51398 > 178.248.233.6.https: Flags [F.], seq 179, ack 1, win 8235, options [nop,nop,TS val 1102307410 ecr 289594259,nop,nop,sack 1 {2897:4038}], length 0
21:12:29.829507 IP 10.100.9.5.51398 > 178.248.233.6.https: Flags [F.], seq 179, ack 1, win 8235, options [nop,nop,TS val 1102307713 ecr 289594259,nop,nop,sack 1 {2897:4038}], length 0
21:12:30.233681 IP 10.100.9.5.51398 > 178.248.233.6.https: Flags [F.], seq 179, ack 1, win 8235, options [nop,nop,TS val 1102308117 ecr 289594259,nop,nop,sack 1 {2897:4038}], length 0
21:12:30.741447 IP 10.100.9.5.51398 > 178.248.233.6.https: Flags [F.], seq 179, ack 1, win 8235, options [nop,nop,TS val 1102308622 ecr 289594259,nop,nop,sack 1 {2897:4038}], length 0
21:12:31.447466 IP 10.100.9.5.51398 > 178.248.233.6.https: Flags [F.], seq 179, ack 1, win 8235, options [nop,nop,TS val 1102309329 ecr 289594259,nop,nop,sack 1 {2897:4038}], length 0
21:12:32.663073 IP 10.100.9.5.51398 > 178.248.233.6.https: Flags [F.], seq 179, ack 1, win 8235, options [nop,nop,TS val 1102310541 ecr 289594259,nop,nop,sack 1 {2897:4038}], length 0
21:12:34.581053 IP 10.100.9.5.51398 > 178.248.233.6.https: Flags [.], ack 1, win 8235, length 0
21:12:34.782782 IP 10.100.9.5.51398 > 178.248.233.6.https: Flags [F.], seq 179, ack 1, win 8235, options [nop,nop,TS val 1102312662 ecr 289594259,nop,nop,sack 1 {2897:4038}], length 0
21:12:36.906420 IP 10.100.9.5.51398 > 178.248.233.6.https: Flags [F.], seq 179, ack 1, win 8235, options [nop,nop,TS val 1102314783 ecr 289594259,nop,nop,sack 1 {2897:4038}], length 0
21:12:39.029563 IP 10.100.9.5.51398 > 178.248.233.6.https: Flags [F.], seq 179, ack 1, win 8235, options [nop,nop,TS val 1102316904 ecr 289594259,nop,nop,sack 1 {2897:4038}], length 0
21:12:41.652914 IP 10.100.9.5.51398 > 178.248.233.6.https: Flags [F.], seq 179, ack 1, win 8235, options [nop,nop,TS val 1102319524 ecr 289594259,nop,nop,sack 1 {2897:4038}], length 0
21:12:43.775747 IP 10.100.9.5.51398 > 178.248.233.6.https: Flags [F.], seq 179, ack 1, win 8235, options [nop,nop,TS val 1102321645 ecr 289594259,nop,nop,sack 1 {2897:4038}], length 0
21:12:45.998938 IP 10.100.9.5.51398 > 178.248.233.6.https: Flags [F.], seq 179, ack 1, win 8235, options [nop,nop,TS val 1102323864 ecr 289594259,nop,nop,sack 1 {2897:4038}], length 0
21:12:48.121943 IP 10.100.9.5.51398 > 178.248.233.6.https: Flags [R.], seq 180, ack 1, win 8235, length 0

 , ,

Evenik
()

Использование на low-end VPS

Форум — Admin

Коллеги, плиз поделитесь опытом ( и если есть годные туториалы ну или треды обсуждений) по использования zswap и zram на дешманских VPS c памятью макс 1-2 гига рама .

На них будет запускаться в докерах ( docker-compose) zabbix + postgresql c timescaledb и netflow коллекторы типа akvorado

 , ,

pinachet
()

Добавить имя branch в prompt

Форум — Desktop
PS1="\[\033[0;37m\]\342\224\214\342\224\200\$([[ \$? != 0 ]] && echo \"[\[\033[0;31m\]\342\234\227\[\033[0;37m\]]\342\224\200\")[$(if [[ ${EUID} == 0 ]]; then echo '\[\033[0;31m\]\h'; else echo '\[\033[0;33m\]\u\[\033[0;37m\]@\[\033[0;96m\]\h'; fi)\[\033[0;37m\]]\342\224\200[\[\033[0;32m\]\w\[\033[0;37m\]]\n\[\033[0;37m\]\342\224\224\342\224\200\342\224\200\342\225\274 \[\033[0m\]"

Как добавить в этот PS1 вот это?

$(__git_ps1 '(%s)')

Что-то я растерялся из-за обилия [

┌─[user@debian-home]─[~/media/source_project/dj/work/some/auth]
└──╼

Надо сделать

┌─[user@debian-home]─[git_branch_name]-[~/media/source_project/dj/work/some/auth]
└──╼

А то неудобно, иногда сабмодули перескакивают на HEAD-some с мастера, нужно видеть в каком бренче нахожусь

 

bryak
()

Где бы поглядеть на сложные гитлаб пайплайны?

Форум — Talks

Привет, ЛОР. Подскажи, где бы посмотреть на сложные пипелайны, дабы мудрости перенять и глупость свою уменьшить? В гугле смотрел, но там что-то не очень сложные примеры на первой странице, в мануале гитлаба, я так подозреваю, тоже вводные примеры. Где же найти сложностей? Ну тип что-то вроде ансибл-галактики мож чего есть?

 

SpaceRanger
()

mpv tips & trics

Форум — Desktop

Кто какие фильтры\конфиги\плагины использует? Может кто-то юзает какие-то плагины, которые 60fps делают или что-то оригинальное

 

bryak
()

кто использует tree-sitter?

Форум — Development

Заметил, что очень многие сидят на надёжных, старых конфигурациях своих редакторов, vim и emacs.

В Neovim решили пойти по пути унификации и внедрили, кажется, с 4й версии tree-sitter.

В Emacs с тормозами, но впилили с 29 версии.

Результат впиливания в Emacs: все major modes идут на юга отдыхать (не нужны). Даже с csharp-mode, который приняли в ядро в 29 версии смешно получилось. Tree-sitter выполняет все задачи, описанные в этом примере.

С чем столкнулся я. Примеров в сети на данный момент практически нет, документация недооформлена, приходится обращаться к остаткам доки замороженных проектов, перенесённых в основную ветку разработки (привет Eglot и команды (или названия) для установки lsp сервера).

В сообществе Neovim с этим более чем полный порядок. Правда тут меня напрягает legacy и ублюдочный синтаксис конфига из-за vim. может после 0.11 версии станет легче, не знаю. И нам, осьминогам, Emacs ближе за возможность поменять всё на свете, даже Cthulhu!..

У кого есть истории успеха? Хорошие, годные статьи и (или) примеры конфигов с новыми фишками 29 версии? Их что-то маловато или я не там ищу.

 ,

Neuromagus
()

Йоба кулер

Форум — Talks

Потратил кучу времени чтобы найти что-то способное держать 5800X в бусте при полной нагрузке без троттлинга, но при этом не стоящее сотен нефти и способное уместиться в NR200P. И наконец нашел.

280 мм AIO водянки сразу отмел как дорогие (протекающее через неделю говно за 5 тысяч в расчет не берём) и имеющие сомнительную эффективность. Scythe Fuma 2 отсутствует в продаже не только в РФ, но и у белых людей, а Noctua U12A тут стоит неоправданно дорого.

В поисках доступной альтернативы я перекопал кучу тем на reddit, overclock.net и других ресурсах и по итогу остановился на Thermalright Peerless Assassin 120 SE. Топовая башня за свою цену, на ютубе много подробных разборов.

В продаже в РФ этого кулера не оказалось, пришлось идти на али и я чуть было его не заказал, но вовремя наткнулся на Phantom Spirit 120 SE от того же производителя.

По габаритам он такой же как и PA120 SE, но имеет 7 теплотрубок вместо 6 (трубки отлично пропаяны по всей длине и пронизывают всю подошву), чуть более широкую подошву и немного другое расположение лепестков радиатора.

Обошёлся он мне в 3700 рублей вместе с доставкой. Стоковые вентиляторы я сразу выбросил, поскольку Thermalright нормальные делать не умеет, и установил вместо них ADATA XPG VENTO PRO 120 - реинкарнация знаменитого Gentle Typhoon. Вертушки эти шумные на максимальных оборотах, так что пришлось в UEFI кривую настраивать чтобы они раскручивались на полную только при 80+ градусах. За две отдал около 3 тысяч. Ещё 450 рублей за 40x40 мм пластинку PTM7950 вместо стоковой термопасты.

Курва -20 all core, лимиты PBO оставил на motherboard. Гонял Cinebench, Prime95 и y-cruncher - температура была в районе 75°C и камень держал буст по всем ядрам без троттлинга, что для 5800X просто отличный результат. В остальных задачах максимум 70 градусов было.

В итоге примерно за 7 тысяч я получил кулер по эффективности на уровне AIO водянки Arctic Liquid Freezer II 280 или Noctua U12A которые сейчас у нас стоят в 2 раза дороже.

Если вам нужен нормальный воздушный кулер для процессоров под AM4/AM5 или LGA151x/LGA1700 - берите этот.

 ,

Meyer
()

Как отключить доступ к сети для теста?

Форум — Admin

Всем привет. Пишу небольшую научную статью, для нее разработал простенькую программу, которая делает обработку на нескольких машинах, потом сливает все вместе. Сейчас мне потребовалось в ходе теста устроить локальную проблему: нужно отключить сеть и посмотреть на поведение программы (отключать нужно часто). В контейнер ее не положить, виртуалка тоже не вариант. Отключить адаптер тоже - я это проверяю и уже исследовал. Нужно именно устроить черную дыру для всех пакетов определенного интерфейса. Я пока склоняюсь к варианту iptables и, возможно, route … reject. Но может есть что-то попроще? Например, sysctl может что-то универсальное имеет или же может вообще без вмешательства в систему? Был бы очень признателен за идею.

 ,

ahnenerbe
()

Какjdf конфигурация .emacs

Форум — Development

Как у товарища настроен emacs вот тут https://www.youtube.com/watch?v=p4HH8w3f-8s

 , ,

saufesma
()

Посоветуйте ресурсы и методы для изучения математического анализа

Форум — General

Привет!

Стремлюсь улучшить свои навыки в математическом анализе и ищу советов по дополнительным ресурсам для изучения. Прочитал уже книгу Кудрявцева, но хочу найти ещё материалы, такие как рекомендуемые книги или YouTube-каналы, чтобы более глубоко понять эту дисциплину. Буду благодарен за любые рекомендации и советы!

 

Molossus
()

firefox как запретить suspend tab?

Форум — Desktop

Очень неудобно, когда открываешь броузер и все табы, кроме текущего, являются suspend, перехожу на любой из них и они начинают рефрешиться. Как запретить firefox’у это делать?

 , ,

bryak
()

возможен ли фиксированный poll rate мышки?

Форум — Desktop

Установил частоту опроса на 1000Hz

options usbhid mousepoll=1
#systool -m usbhid -A mousepoll
Module = "usbhid"

    mousepoll           = "1"

Смотрю через evhz

USB OPTICAL MOUSE : Latest  1008Hz, Average   538Hz
USB OPTICAL MOUSE : Latest   496Hz, Average   538Hz
USB OPTICAL MOUSE : Latest   500Hz, Average   538Hz
USB OPTICAL MOUSE : Latest   502Hz, Average   538Hz
USB OPTICAL MOUSE : Latest   496Hz, Average   538Hz
USB OPTICAL MOUSE : Latest   494Hz, Average   538Hz
USB OPTICAL MOUSE : Latest   506Hz, Average   538Hz
USB OPTICAL MOUSE : Latest   494Hz, Average   538Hz
USB OPTICAL MOUSE : Latest   500Hz, Average   538Hz
USB OPTICAL MOUSE : Latest   336Hz, Average   536Hz
USB OPTICAL MOUSE : Latest   479Hz, Average   535Hz
USB OPTICAL MOUSE : Latest   523Hz, Average   536Hz
USB OPTICAL MOUSE : Latest   490Hz, Average   536Hz
USB OPTICAL MOUSE : Latest     1Hz, Average   528Hz
USB OPTICAL MOUSE : Latest   100Hz, Average   514Hz
USB OPTICAL MOUSE : Latest   492Hz, Average   514Hz
USB OPTICAL MOUSE : Latest   111Hz, Average   508Hz
USB OPTICAL MOUSE : Latest    13Hz, Average   500Hz
USB OPTICAL MOUSE : Latest    91Hz, Average   494Hz

Двигаю быстро частота взлетает, начинаю пиксельхантить частота вплоть до 1 герца.

Просто интересно можно ли принудительно заставать мышку опрашивать 1000Hz стабильно? Чтобы даже если она на месте стоит частота опросов не падала?

И вот ещё, заметил что клик происходит не когда я НАЖИМАЮ кнопку, а когда я её ОТПУСКАЮ. Нет не потому что клик происходит при отпускании, а потому что он настолько долгий походу что я это тупо вижу. Тоесть нажал отжал два щелчка от кнопки, ну так вот действие от клика (любое игры,сброс выделения текста да что угодно) происходит с запозданием, я нажимаю отжимаю, даже палец вверх на милимерт успеваю поднять и происходит действие.

Ну так вот, у меня мышек в жизни мало было эта OKLICK какая то дешманская, но такая задержка… Может что-то подкрутить можно? Тила latency какой может есть, на примере с скоростью опроса датчика. Ну и вот ещё до кучи, если мышку поднять то светодиод через секунду тише светит как будто она чрез N мс уходит в ожидание это софтовым методом происходит как с падением частоты опроса или нет?

Суть… В CS:GO нагибать неполучается душенька лютует. У меня реакция 15 мс я тупо нажимаю и жду кулак в подбородок когда железо по проводам данные перешлёт… Хнык.

UDP: Нет игры нет проблем, нафиг эту задротилку =)

 , ,

LINUX-ORG-RU
()

Транскрипция видео в текст , Локально

Форум — General

Коллеги, кто как делает локально разпознавание речи( русский язык ) в видео файле и как отдельно юзать что-то по типу этого https://nextcloud.com/blog/app-contest-develop-an-app-with-the-smart-picker-to-win/ ?

 ,

pinachet
()

Геймпад Xbox на Linux

Форум — Linux-hardware

Современные геймпады Xbox (Xbox Series и возможно Xbox One) поддерживают Linux и macOS?

Перемещено hobbit из general

 ,

ConLenov
()

Не работает amd_pstate на B550 и Ryzen 7 5800X

Форум — Desktop

Стало мне казаться (спустя 1.5 года после того, как собрал), что мой десктоп слишком часто крутит вентилятором. Решил посмотреть, включено ли управление частотой/питанием, и обнаружил, что у меня amd_pstate не включается.

В BIOS CPPC включил явно (было Auto) - не помогает.

Пробовал параметр amd_pstate.shared_mem=1 - не помогает.

Пробовал блэклистить acpi вместе с этим параметром - не помогает (исчезает вообще любое управление частотой CPU).

Arch, обычное ядро 6.2.13 из дистрибутива, pstate вкомпилен в ядро разработчиками дистрибутива. lscpu не показывает cppc во флагах.

Материнка Gigabyte Aorus B550M PRO.

Эта комбинация материнки и проца и amd_pstate находится лишь однажды в google, там человеку помогло amd_pstate=passive. Я ещё не пробовал, но вроде как здесь писали, что passive mode - отстой.

 , , ,

emorozov
()

Какую видеокарту для проброса в QEMU KVM купить?

Форум — Linux-hardware
Имею
AMD Ryzen™ 5 5600G
MSI B450M PRO-VDH MAX
32 RAM 2*16

Нужно запускать прогу, которой нет на LINUX называется Cscalp, ни у кого толком не удалось запустить ее через WINE и тд...
Пришел к выводу что нужно докупить видеокарту, только для того чтобы пробросить ее в виртуалку QEMU KVM.

Нужен совет на что обратить внимание? Производительность особо не нужна, а вот тишина очень нужна тк в системе на данный момент при обычном использовании пк крутится только один вентиль в БП на выдув, не хотелось бы портить эту тишину.
Буду брать с авито +-5тр 
Есть MSI GT 1030 за 4500р с пассивным охладом

С вертушками, раньше постоянно пахали, а сейчас в простое с каких то серий вроде вентили останавливаются?

 , , , ,

MULT
()

мониторить процессы которые живут несколько миллисекунд

Форум — Admin

Напомните как в linux мониторить процессы, кроме как через ps, top, /proc? Речь идёт о процессах, которые живут несколько миллисекунд и не успевают засветиться в top. Погуглил, вроде бы можно запустить ps в цикле watch, но он выводит просто на экран, а допустим нужно в файл. Может через отладку ядра как-то?

 

sunny1983
()

А какие хорошие игры вышли за последние 10 лет?

Форум — Games

Хотелось бы услышать мнение лоровцев и подвести итоги. Может есть какая игра хорошая, которая прошла мимо меня и я о ней не слышал. Предлагаю запостить ваш личный топ-10 (или меньше, если не наберется, можно и больше)

Мой список:

  • Resident Evil 0 Remake Remastered
  • Resident Evil 1 Remake Remastered
  • Resident Evil 2 Remake
  • Resident Evil 3 Remake
  • Metro: Last Light
  • Metro: Exodus
  • DOOM (2016)
  • Killing Floor 2
  • Crash Bandicoot N’Sane Trilogy
  • Railway Empire

 

Zadoff386
()