LINUX.ORG.RU

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

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. Установите расширение Greasemonkey или Violentmonkey.
  2. Установите LORScriptPack с Greasy Fork.

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

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

Настройка

В настройках сайта «Форматирование по умолчанию» должно быть LORCODE.

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

NEW_TO_OLD_SEARCH_BY_DEFAULT
HIDE_NEWS_AND_GALLERY
ADD_TAGS_AND_DOCS
TECH_ONLY_BY_DEFAULT
HIDE_TAG_CLOUD
HIDE_STARS
HIDE_REACTIONS

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

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

.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.1
// ==/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 = false;
const HIDE_NEWS_AND_GALLERY = false;
const ADD_TAGS_AND_DOCS = true;
const TECH_ONLY_BY_DEFAULT = false;
const HIDE_TAG_CLOUD = false;
const HIDE_STARS = false;
const HIDE_REACTIONS = false;


// Глобальные константы и переменные
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':
            if (!collp)
                mtext = mtext.replace(/\n/gm, '\n'+'[br]');
            else 
                open = '[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 ? '' : '\n[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
}


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

let 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;
    }

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

    #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;
    }
`;

if (HIDE_TAG_CLOUD) {
    LOR_CSS += `
        .tag-cloud {
            display: none;
        }
    `
}

if (HIDE_STARS) {
    LOR_CSS += `
        .stars {
            display: none;
        }
    `
}

if (HIDE_REACTIONS) {
    LOR_CSS += `
        .reactions {
            display: none;
        }
    `
}

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
        
        if (reply) {
            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
()

GSmartControl 2.0.0

Новости — Open Source
Группа Open Source

После почти 3 лет разработки состоялся выпуск GSmartControl 2.0.0 – графической утилиты для отслеживания состояния HDD и SSD дисков по технологии S.M.A.R.T.

( читать дальше... )

>>> Подробности

 , , , ,

cchr
()

Первый выпуск мультимедийной библиотеки LDL c поддержкой старых систем

Новости — Open Source
Группа Open Source

Представляю Вашему вниманию разработанную мной первую версию мультимедийной библиотеки Little DirectMedia Layer, сокращённо LDL.

Библиотека написана на С++ 98 стандарта, что позволяет компилировать ее начиная с Visual C++ 6.0. Код распространяется на условиях Boost Software License 1.0. Но библиотека не ограничивает программистов в выборе стандарта языка C++, программист может использовать любой современный стандарт языка. Я придерживаюсь философии downgrade — это использование старых устройств и софта в повседневной жизни, когда компании не поддерживают свои же «устаревшие» операционные системы или устройства, увеличивая с каждой новой версией своего продукта системные требования, или прекращают поддержку девайса. Миллиарды устройств по всему миру ежесекундно перемалывают миллиарды инструкций неоптимизированного кода.

В этом году я выступил на конференции С++ 2023 с докладом «Вперед в прошлое, или Разрабатываем фреймворк под Windows 95 в 2023 году».

Своим проектом я хочу доказать, что старое железо еще может приносить пользу и его рано списывать в утиль. Есть что-то чарующее в старых технологиях, подходах и железках, а еще это вызов новым библиотекам и программам.

В самом начале процесса разработки я и не предполагал, что данная библиотека вообще возможна. Но при практической реализации прототипа, добавляя строчку за строчкой в фундамент будущей библиотеки, убеждался в возможности ее создания и практическом применении.

Резюмируя вышесказанное, возможно писать быстрые программы, нужно просто воспользоваться знаниями древних.

Возможности библиотеки:

  • поддержка Linux Debian 3 и выше (обеспечена нативная сборка);
  • поддержка Windows 95 — Windows 11;
  • простое API для работы с 2D графикой;
  • загрузка множества графических форматов (bmp, png, tga, jpg);
  • кроссплатформенное API над окнами и событиями ОС;
  • для аппаратного ускорения графики используется OpenGL 1.2 и
  • OpenGL 3.3, присутствует поддержка обработки графики только на ЦПУ, если отсутствует аппаратное ускорение;
  • рендер может быть выбран динамически при загрузке приложения;
  • единое API для всех систем — напиши один раз и компилируй везде!
  • воспроизведение звука;
  • динамическая и статическая линковка.

Планы на будущее:

  • поточное воспроизведение звука;
  • вывод текста с поддержкой библиотеки freetype;
  • дополнительные рендеры Direct3D 9, 10, 11;
  • API для работы с потоками;
  • встроенная поддержка API для работы с сетью;
  • портирование фреймворка на другие платформы: Android, IOS, MacOs.

Ссылки:

>>> Подробности

 ,

JordanCpp
()

ОС «Быть». Наши дни

Галерея — Скриншоты

Haiku OS, установлена на ноутбук, использую для работы (хотя пока это громко сказано) каждый день.

На скриншоте, в порядке следования:

  • ЛОР, виртуалка с FreeBSD 14, информация о системе https://0x0.st/HHVm.png

  • Сторонние приложения, результат недавних достижений по портированию. GIMP через wayland прослойку, FEMM и WinRAR через Wine. https://0x0.st/HHVB.png

  • Пустой рабочий стол https://0x0.st/HHVM.png

  • Системное меню, системный ФМ, терминал. https://0x0.st/HHVu.png

Бонус скриншоты:

Из интересных моментов.

На скриншоте рабочего стола - видны репликанты (как виджеты в плазме, но работает это несколько иначе).

Окно с виртуалкой QEMU - в его заголовок присоединены (stacked, не знаю, как по-русски сказать) другие окна QEMU и терминал, переключаются как вкладки. Это киллер фича Haiku, как вы знаете.

Аппаратной виртуализации нет, поэтому QEMU работает в TCG-режиме. Но работает.

В Wine работают только чистые 64 бит приложения, и это огромная проблема, потому что винду…дисты до сих пор на 2023 год плещут 32 битные бинари в перемешку с 64 битными. Во имя Ктулху видимо.

Система установки и обновления приложений тоже оставляет самые положительные впечатления. При обновлении, установке, удалении чего-либо - создается «снапшот» системы и в загрузчике можно выбрать его, если что-то пошло не так. Причем как он создается! Без всех этих ваших линукс/FreeBSD чудес с вундер файловыми системами. Это работает просто на папках.

Система работает на ноуте, работает встроенный WiFi из коробки, звук из коробки, разрешение экрана родное из коробки. Отзывчивость, стабильность работы, производительность и безглючность системы впечатляет.

Хотя баги, конечно, есть, например драйвер сети иногда валит ядро при загрузке системы. Но в целом, то, что называется в линуксе DE, - работает чётко. (Привет, плазма).

>>> Просмотр (2732x1536, 1943 Kb)

 

James_Holden
()

Установка usb wifi драйверов на Fedora 37

Форум — General

Установил Fedora 37 на материнке есть встроенный wifi он работает чётко но есть необходимость в работоспособности usb wifi.Usb wifi предварительно должен поддерживать работоспособность на linuxe без дополнительных изменений за исключением установки самих драйверов,в установке которых я ступоре.Хотелось бы выйти на связь со знающим человек во благо поиска помощи с данным случаям. Дискорд:Ashura#7067

 

Khamzat06
()

fastfetch и flashfetch 1.11.0

Новости — Open Source
fastfetch и flashfetch 1.11.0
Группа Open Source

Состоялся выпуск 1.11.0 консольных утилит вывода системной информации fastfetch и flashfetch. Эти программы можно считать быстрыми аналогами neofetch, в отличие от последнего, они написаны на языке C (стандарт C11).

( читать дальше... )

>>> Подробности

 ,

dataman
()

MidnightBSD 3.0

Новости — BSD
Группа BSD

MidnightBSD — это Unix-ориентированная операционная система для настольных компьютеров, изначально созданная на основе FreeBSD 6.1 и периодически обновляемая кодом и драйверами из более поздних выпусков FreeBSD.

( читать дальше... )

Ссылки:

http://www.midnightbsd.org/notes/
https://www.opennet.ru/opennews/art.shtml?num=58866

>>> Подробности

 ,

Clockwork
()

FBReader 2.0

Новости — Проприетарное ПО
Группа Проприетарное ПО

В декабре 2021 года, через несколько лет после выхода предыдущей версии, была выпущена FBReader 2.0 для Linux. Хотя программа включает большинство возможностей из версий для iOS и Android, это полноценное приложение, специально разработанное для «настоящих» компьютеров.

FBReader 2.0 бесплатен, но его код не открыт. На сегодня программа доступна в Snap Store. Это означает, что вы можете установить FBReader в большинстве популярных версий Linux'а: в Debian, Fedora, Ubuntu, и многих других. В ближайшее время будет выпущена версия в AppImage.

Текущая версия: 2.0β10 (опубликована 18 февраля 2022 года)

>>> FBReader Та самая читалка

 

jester-666
()

Firefox 98, пропал пункт «открыть в» при сохранении любого файла

Форум — Desktop

Опять гениальные менеджеры Мозиллы что-то выпилили или чего, как вернуть?

 

Ros
()

Переход с Linux на FreeBSD. Успех, но...

Галерея — Скриншоты

Этот небольшой пост не порадует вас обилием картинок, он скорее объявление о намерениях.

Под новый год сдох последний SSD в рейде, и я наконец перешел с Oracle Linux 6 (с 2013 года) на FreeBSD (да, у меня много времени). И вот хочу бороться с мифами на форуме и всеми этими постами «стоит ли переходить», а «какого цвета трава по вашу сторону забора» и т.д. Обычно на эти вопросы отвечают фанаты FreeBSD, а я предлагаю ниже краткое описание от линуксоида. Посвящено оно в основном звуку. Это при том, что я уже два года пробую FreeBSD на личных серверах.

Сначала о постановке целей.

1) Недостижимый идеал темы выглядит вот так https://www.gnome-look.org/p/1120849/ Недостижим он по той причине, что исходники иконок в SVG недоступны, а темы QT и GTK изрядно переписаны в сторону метросексуальности. Так что пока скриншот только с консолью.

2) Основные требования к софту:

2.0 стабильность
2.1 Urxvt/bash/mc
2.2 Google Chrome
2.3 Thunderbird
2.4 VirtualBox
2.5 rtorrent/mplayer (mpv/moc)
2.6 LibreOffice
2.7 Контейнерная виртуализация.

3) Основные требования к ОС:

3.0 мышь и клавиатура
3.1 Firewall, маркировка по портами, маршрутизация вместе с NAT.
3.2 No Pottering, No pulseaudio, No systemd, No gstreamer, No avahi
3.3 FS live snapshots. Не ZFS.
3.4 nvidia proprietary driver
3.5 PA like functionality.

--------------
Реальность:
2.0 Во-первых, надо полностью развеять миф о бОльшей стабильности FreeBSD по сравнению с Linux. За время своего знакомства я наступил на кернел паник в стабильном, ванильном, оттестированом и т.д. выпуске, сегфолт пакетного менеджера (знаю, как повторить), сегфолт утилиты из базы (jails, зафиксировано в багтреке), сегфолт fsck (зафиксировано в багтрекере). И нет в шестом редхате такого не было, а с манжаро, арчами, федорами и гентами сравнивать не берусь, там все бывает. Во-вторых, везде кода меньше, система легче. Порядка немного больше. Это что касается базы. В прикладном софте примерно также.

2.1 bash. Я держу набор .bashrc файлов для всего набора линуксов, с которыми сталкивался, чтобы унифицировать поведение в консоли. Ес-но, FreeBSD отличается сильнее, консольные утилиты просто менее функциональны, дурацкое требование слеша на конце при использовании cp -r dir1/ dir2... Поэтому я просто заинсталировал все, что было в репозиториях из гнутого консольного софта и обмотал это альясами. Жить можно и даже неплохо. Из неудобного: все пути длинее, т.к. /usr/local/, блин:( Из странного каждый терминал считается залогиненым юзером в w. Это очень странно.

2.2 Google Chrome. Поломано определение Memory Footprint в Task Manager. Теперь не прибъешь вкладки, которые много отожрали. Хотя кто-то мне доказывал со скриншотами, что это только у меня. хз.

2.3 Thunderbird + (все также)

2.4 VirtualBox - отсутствует коммерческий пак от оракла, стало быть не работает быстрое USB2.0 (буду пробовать прокидывать контроллер) и не работает RDP (не решаемо). Проблемы со звуком, см. ниже.

2.5 mplayer - проблемы со звуком. см. ниже.

2.6 LibreOffice - от RH GTK не избавишься, т.к. программисты с freedesktop хуже поддерживают qt, чем gtk. на QT поломан размер шрифтов. неюзабельно.

2.7 контейнеры jails. Это то, к чему я привык во времена OpenVZ. Все есть, все хорошо. Но рестарт контейнера возможен только через минуту-две. Не освобождает ресурсы. Досадный глюк.

3.0 Из того, что работало на Linux десятилетней давности, но перестало на FreeBSD - это мышь и клава. У FreeBSD традиционно сложные отношения с определением устройств. Они не могу определиться, развивать свое или копировать с линукса udev. В итоге они делают и то, и то и в результате по-умолчанию нифига не работает, конфигурация Xorg'a превращается в разбор внутренней кухни этих заморочек. Плюс они что-то там намудрили, что мультимедийные кнопки на клаве - это как бы отдельная клавиатура (которая не работает). И в добавок, эти мультимедийные клавиши просто не могут работать, т.к. для них указаны ошибочные кейкоды в каком-то махровом году. Два года назад я это выяснил и пропатчил демон, который их определяет. Руки дойдут - сделаю снова.

Почему не определился Genius MX Master, я пока так и не понял, воткнул самую простую USB мышь.

3.1 Firewall - самая грустная часть FreeBSD:( При том, что он существует в трех(!) инкарнациях, они все глубоко в *опе. ipfw - основной и самый функциональный, застрял в середине 90х. Документации просто недостаточно. Сложные штуки с маркировкой трафика по портам и натами на нем невозможно выполнить. pf - устаревшая версия из OpenBSD. сама OpenBSD ее уже выкинула. и также с соляровским вариантом. Солярис тоже его давно выкинул в пользу OpenBSDшного. И хуже всего то, что внутриядерная часть фвола очень простая. Все пакеты падают в одно ведро, там перемешиваются и по факту невозможно их сепарировать. Короче говоря, я пытался три раза хотя бы спародировать какие-то простые места из своей огромной портянки iptables и все это заканчивалось ненавистью и фрустрацией. Так что придется просто поднимать виртуальную машину со старым Linux+iptables.

3.2 No Pottering, No pulseaudio, No systemd, No gstreamer, No avahi - тут полный успех. Но только, если собирать из портов.

3.4 live snapshots. Я говорю об аналоге lvm на линуксе. Я уже много лет использую эту схему, чтобы очень эффективно бекапить террабайты данных. В отличие от всех этих поделок с tar, которые приносят сюда ньюфаги, такая схема позволяет бекапить шифрованные данные, много, не выключая систему, а ZFS на сервере еще и позволяет всем этим свободно манипулировать. Очень круто. Я не знаю, появились ли в Linux offline snapshots, но лучше ZFS я тут ничего не видел.

У меня получилось собрать полный аналог слоеного пирога encryption layer+volume manager+fs. FreeBSD традиционно использует fs soft updates вместо журналирования. В чем принципиальная разница так и не понял, ну и ладно. Просто пришлось отказаться от soft update, включить журналирование посредством geom, словить баг в gjournal, зарапортовать, подождать два года. По-моему он пофикшен, но так ко мне и не прилетел. Но работает.

Вроде

Потому что в первую же ночь система стала колом с ошибками в журнал при распаковке архива!

Я уже хотел спустить это все в мусорку, когда случайно нашел пару sysctl сетингов, которые это решают. Ни в каких хваленых фрибздешных хендбуках вы это, конечно, не найдете. FreeBSD с одной стороны не имеет адекватных для современного оборудования лимитов по-умолчанию, а с другой хаутушек, чтобы это фиксить, поэтому этим всем можно заниматься только, если вот вам прямо хочется FreeBSD и вы такой весь олдфаг и готовы читать мейл-листы.

Короче, неожиданный успех. Если кому интересно, почему я не использую здесь ZFS, спросите меня.

3.4 FreeBSD - это единственная ОС из других *BSD с проприетарными драйверами. Полный успех.

3.5 PA like functionality. Да, это большая тема, в которой я к тому же понимаю слабо. Я использую USB-звуковуху, которая пригодна для записи звука и может работать с повышенной дискретизацией или что-то типа того. Считается, что FreeBSD изначально имела более качественный аудио-стек, чем Linux. И судя по количеству и качеству ручек, я в это верю. Такое впечатление, что инженеры, которые это писали, увлекались аудиозаписью. А вот сохранять уровни громкости для приложений она не может:( Поэтому ей нужна надстройка, аналог PA - sndiod.

Естественно, FreeBSD развивается очень медленно, а на линуксе тем временем тяп-ляп глючный пульсаудио. Сейчас к 15ой версии он, наверное, уже не доставляет столько гемороя, и в нем море фич. Я исторически использовал PA 8.0. Звук в VirtualBox заикался. Глючило при разных уровнях громкости для каналов. Рестарт пульсаудио - часть моего воркфлоу с 2013 года, но регулятор громкости для приложений - это прикольно и не имеет аналогов во FreeBSD.

Во FreeBSD нас встречает утилита в 10 раз меньше. Она не требует портянок конфигов, в ней нет блютуса и еще каких-то новомодных штук. Но она тут же сделала все, что мне нужно. Она принимает поток по tcp. Она объединяет каналы в моно, как мне нужно. Она выставляет нужную частоту дискретизации. Первое, что я заметил звук стал чище. Как будто новую аудиокарту взял. Очень круто.
Но
Но теперь звук в mplayer заикается при нагрузке на диск! Я не понимаю, это баг системы или конкретного софта. Вроде напрямую в OSS не заикается. Но так мне не надо.

VirtualBox теперь играет звук чисто. Но недолго. Он не может выводить в sndio, отправляет в OSS и скоро затыкается.

На этом со звуком все.

>>> Просмотр (3200x1200, 181 Kb)

 , , ,

crypt
()

Разблокируйте тему!

Форум — Linux-org-ru

https://www.linux.org.ru/forum/talks/16727523

удалено tailgunner по причине ‘Обсуждение вакцинации (0)’

@tailgunner, в каком месте ты увидел обсуждение вакцинации в этой теме? Тема об альтернативном видо хостере vidio.blog, к тому же российском. То, что @FedyaPryanichkov процитировал название одного из им найденых роликов никакого обсуждения вакцинации не означает. В конце концов ты мог бы удалить лишь одно это сообщение. Зачем ты удалил всю тему? У тебя болит голова или тебя кто-то сильно обидел? Почему ты срываешься на людях?

 , , , ,

hummer
()

fheroes2 0.9.11

Новости — Игры
fheroes2 0.9.11
Группа Игры

Здравствуйте, верные игроки и любители героев меча и магии.

Сегодня у нас состоялся предновогодний релиз новой 0.9.11 версии движка fheroes2! Мы немного отошли от нашего обычного расписания, чтобы приурочить данный релиз к праздникам, на которых вы сможете поиграть в Героев Меча и Магии 2 с теми улучшениями, которые мы добавили с прошлого выпуска.

Как мы и сообщали ранее, мы плотно занимаемся локализованными версиями, чтобы игроки могли запускать и играть в «Героев» на нужном им языке без поиска какой-то определенной локализации. Теперь игру можно запустить на русском языке имея установленной любую версию оригинальных героев: русские буквы генерируются на лету из оригинальных английских букв. Это стало возможным благодаря членам нашей команды, кропотливо собравшим все буквы алфавита вручную. Осталось немного поправить перевод некоторых частей текста, измененных в движке fheroes2, и игроки смогут насладиться игрой на родном русском языке владея лишь оригинальной английской версией игры. Для французского языка также были сгенерированы недостающие символы из исходного алфавита.
Поддержка других языков ожидается в ближайшие релизы.

Одним из приятнейших нововведений нашего проекта в этом месяце стали подробные описания заклинаний в книге героя. Больше не нужно гадать, сколько наносит урона то или иное заклинание, а также сколько появится вызванных Элементалей на поле боя: по правому клику игроки теперь могут узнать точные данные перед использованием заклинания. Изменения коснулись и заклинания «Городские Врата»: по правому клику вы сможете уточнить, в какой именно город отправится герой после использования заклинания.

А в списке доступных сценариев мы добавили информационное окно, чтобы игроки смогли уточнить, где в системе хранится выбранная карта и к какой версии игры она относится.

Наша команда исправила некоторые проблемы у ИИ при защите замка, поправила логику прокладки пути по дорогам, улучшила отрисовку в некоторых местах, а также расширила поддержку адресных символов при установке на Windows.

В общей сложности, за этот короткий период с прошлого релиза, было исправлено свыше 20 багов.

Мы благодарим всех неравнодушных за поддержку нашего проекта! Мы надеемся, что результаты нашей работы приносят вам хорошие эмоции и настроение.
Наша команда поздравляет всех с наступающими праздниками!

Разработку проекта можно поддержать на Patreon.

>>> Подробности

 , , ,

sirDranik
()

Оформление Firefox

Форум — Desktop

Обновился файрфокс и теперь выглядит как кусок чего-то неприглядного: https://ibb.co/3dpzsYX

Очевидная проблема: теперь абсолютно не считывается, какая вкладка активная. Как этим можно пользоваться, не представляю.

Но кроме этого также бардак с меню:

  • Оформление меню не следует дефолтным цветам тулкита. В частности в данный момент - Адвайты.
  • При этом контекстное меню страницы и классическое меню по Alt остались старыми - GTK3-шными, с ними всё нормально.
  • А меню-бутерброд и меню закладок - новые в нестандартном оформлении.

Действительно, в 20-х годах XXI-го века следовать оформлению тулкита не модно. Теперь мы сами с усами, теперь у нас СТИЛЛЛЬ.

Но пока у дизайнеров СТИЛЛЛЬ, простому пользователю надо это как-то фиксить.

По панели табов решение очевидно - усилить контраст. Я перебрал десяток готовых тем, но ни в одной сочетание цветов мне не понравилось. Точнее сказать, в некоторых табы становятся ничего, но вносятся «конструктивные» измненеия в цвет строки адреса, которые мне не нужны.

С меню ситуация хуже. Во всём десятке тем ни нашлось не одной, которая меняет серенький цвет подсветки активного пункта меню на что-то другое. Цвет фона меняют, цвет текста меняют, а цвет заливки выделения - нет.

У меня начали формироваться смутные подозрения, что этот цвет прошит в лису и изменению через стили не подлежит.

Ну тем хуже для лисы - если так, придётся патчить.

Похоже, что мне придётся разобраться, как сделать собственную тему оформления, и всё сделать в ней исключительно уже на свой вкус. А если вдруг я не найду документированного способа поменять цвет заливки выделенного пункта меню, то придётся лезть в исходники.

Всем этим я займусь, наверное, на выходных.

А пока может у кого-то есть конструктивные предложения? Кто как решал эти затруднения?

 , ,

wandrien
()

Firefox. Встроенный переводчик.

Форум — Desktop

Доброго времени! На сколько мне известно, в Firefox (в версиях выше 41) имеется встроенный переводчик страниц). О чем впринципе и свидетельствуют некоторые скрытые настройки:

  1. browser.translation.detectLanguage
  2. browser.translation.ui.show
  3. browser.translation.engine Так же на просторах интернета есть инструкции о том, как включить данный переводчик. К примеру вот:
    1. Убедитесь, что у вас установлен Firefox 41 или выше, так как в более младших версиях «огненного лиса» данного функционала нет. Чтобы увидеть версию, введите «about:support» в адресную строку браузера и нажмите Enter:
    1. Введите в адресную строку «about:config» и нажмите «Enter». В появившемся сообщении кликните по кнопке - «Я обещаю, что буду осторожен!»
    1. С помощью поисковой строки найдите параметр «browser.translation.detectLanguage» Дважды кликните по строчке с ним левой кнопкой мыши чтобы присвоенное ему значение сменилось с «false» на «true»
    1. Сделайте то же самое для параметра «browser.translation.ui.show»
    1. Далее найдите параметр «browser.translation.engine». Дважды кликните по нему и в появившемся окошке сотрите «bing», а вместо него вручную напишите «yandex» и нажмите кнопку ok
    1. Перейдите на сайт Yandex’s Translate API и войдите там в свою учётную запись на Яндекс, нажав ссылку Log in в верхнем правом углу по ссылке: https://tech.yandex.com/translate/
    1. Теперь прокрутите страницу вниз и кликните по ссылке «Get a free API key»
    1. На следующей странице поставьте галочку напротив «I have read….» и кликните по кнопке «Get API key»
    1. В итоге Яндекс сгенерирует для вас уникальный ключ, благодаря которому вы сможете встроить его переводчик в ваш Firefox. Скопируйте этот ключ в любой текстовый файл
    1. Вновь вернитесь на страницу «about:config». Щёлкните мышкой на пустом месте и в контекстном меню выберите «Создать» - «Строка»
    1. В появившемся окошке в качестве имени строки укажите "browser.translation.yandex.apiKeyOverride "
    1. В следующее, заключительное, окошко целиком скопируйте ключ, полученный в пункте 9.
  • Теперь закройте все служебные страницы, перезапустите браузер и попробуйте зайти на любой иностранный сайт.

И впринципе данная инструкция (по моему мнению) имеет право на жизнь, но дело в том, что у меня на пункте 10 возникает проблема, в контекстном меню нет «Создать», да и вообще контекстное меню страницы about:config выглядит так же как и на любой другой странице, хотя там добжно появляться совсем другое (служебное) контекстное меню. Ну и собственно теперь сам вопрос. Как воплотить данную инструкцию в Firefox?

И впринципе данная инструкция (по моему мнению) имеет право на жизнь, но дело в том, что у меня на пункте 10 возникает проблема, в контекстном меню нет «Создать», да и вообще контекстное меню страницы about:config выглядит так же как и на любой другой странице, хотя там добжно появляться совсем другое (служебное) контекстное меню. Ну и собственно теперь сам вопрос. Как воплотить данную инструкцию в Firefox?

Firefox 73.0. И впринципе данная инструкция (по моему мнению) имеет право на жизнь, но дело в том, что у меня на пункте 10 возникает проблема, в контекстном меню нет «Создать», да и вообще контекстное меню страницы about:config выглядит так же как и на любой другой странице, хотя там добжно появляться совсем другое (служебное) контекстное меню. Ну и собственно теперь сам вопрос. Как воплотить данную инструкцию в Firefox?

И впринципе данная инструкция (по моему мнению) имеет право на жизнь, но дело в том, что у меня на пункте 10 возникает проблема, в контекстном меню нет «Создать», да и вообще контекстное меню страницы about:config выглядит так же как и на любой другой странице, хотя там добжно появляться совсем другое (служебное) контекстное меню. Ну и собственно теперь сам вопрос. Как воплотить данную инструкцию в Firefox?

  • Firefox 73.0.
  • Kubuntu 18.94 LTS.

 

parnyagan
()

Как узнать мой внешний ip адрес?

Форум — Admin

Привет!

Последние несколько лет для этой цели пользовался командой wget --timeout=10 -O - -q icanhazip.com, но недавно запросы на этот сервер перестали проходить (м.б. провайдер блокирует, ХЕЗ).

Нужно решение, удобное для использования в скриптах.
Спасибо.

 ,

rmu
()

Plymouth - можно ли его использовать как обои для tty

Форум — General

Собственно вопрос в названии топика. В своё время был fbsplash, его можно было и как заставку использовать, а можно было указать verbose в самой теме сплеша и тогда вся консоль работала штатно, но на фоне картинки. Возможно ли такое с плимутом? Про «esc» я в курсе, но тогда пропадает картинка, а хотелось бы просто tty с фоном.

 

Gramozeka
()

Google и 72 кубита

Форум — Science & Engineering

Google объявил о создании квантового компа с 72 кубитами. В основном пишут какую-то воду, и непонятно, может ли этот комп практически считать что-то быстрее доступных суперкомпов, или же это очередная игрушка на волне хайповой темы. Например, может ли он факторизовать числа быстрее существующих классических суперкомпов.

Кто-нибудь разбирается в теме?

 ,

seiken
()

jollheef блокирован модератором anonymous по причине: удаление аккаунта

Форум — Linux-org-ru

jollheef - Блокирован 17.09.19 23:02:02, модератором anonymous по причине: удаление аккаунта

WTF??

 

Satori
()

Как увеличить размер SWAP на LVM2 ?

Форум — General

На работающей системе можно?

 , ,

bvn13
()

Lemmy — федеративный социальный новостной агрегатор

Новости — Open Source
Lemmy — федеративный социальный новостной агрегатор
Группа Open Source

Lemmy создан как альтернатива сайтам вроде Reddit, Lobste.rs, Raddle или Hacker News: можно подписываться на интересующие вас темы, публиковать ссылки и обсуждения, а затем голосовать и комментировать.

Но есть важное отличие: любой пользователь может запустить свой сервер, который, как и все другие, будет подключен к одной и той же «вселенной», называемой Fediverse. Пользователь, зарегистрированный на одном сервере, может подписаться на темы на любом другом сервере и может вести обсуждения с пользователями, зарегистрированными в других местах.

На каждом из lemmy-серверов может быть установлена собственная политика модерации и назначены модераторы для борьбы с токсичными троллями.

На данный момент доступна бета-версия, и многие функции в настоящее время не работают или находятся в активной разработке, например федеративная сеть.

>>> Подробности

 , ,

anonymous
()