LINUX.ORG.RU

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

Текстовый редактор Micro 2.0.14

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

micro – консольный текстовый редактор для Linux, написанный на языке Go и не имеющий зависимостей. Поддерживает плагины на языке Lua и различные команды во внутреннем терминале. В этом выпуске были добавлены новые настройки, улучшения синтаксической подсветки и поддержки мыши.

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

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

 , ,

MrCookie
()

Восьмилетняя девочка написала веб-чат

Форум — Talks

Восьмилетняя дочь вице-президента американской компании Cloudflare Ричарда Робинетта (Richard Robinett), написала веб-приложение для чата. В предыдущем предложении преднамеренно пропущено слово «самостоятельно», хотя никто из взрослых или других детей ей не помогал. Девочка воспользовалась помощью двух программ-ассистентов - платформы для разработки ПО Workers AI и сервиса Cursor. Cursor представляет собой редактор кода со встроенным ассистентом на основе искусственного интеллекта, тогда как Workers AI отвечал за генерацию кусочков кода по запросу пользователя. Последовательно запрашивая нужные сниппеты кода, а потом применяя их к существующему проекту девочка смогла повторить обычный воркфлоу типичного веб-разработчика и получить нужный результат. Работа велась в онлайн-среде разработки от всё той же Cloudflare - Workers IDE, а вся необходимая инфраструктура была построена на облачной среде Cloudflare. Таким образом для работы требовался только браузер и настроенный аккаунт в Cloudflare - как разработка, так и развёртывание приложения целиком происходили на «облачных» ресурсах.

Видео с 10-ти минутным процессом работы можно посмотреть.

Кроме того, на проходящей выставке GamesCon стартап Exists представил альфа-версию своего проекта для разработки игр, позволяющей создавать не только отдельные ассеты, но и целиком 3D-сцены с частично прописанной игровой механикой. Этот проект ещё ждёт своего 8-ми летнего мальчика.

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

 

LamerOk
()

Сайты на древних технологиях - Во имя бобра!

Форум — Web-development

LOR славится своими специалистами по самым шизанутым вопросам. И вот у меня их есть.

Короче, современный веб - это звездец. Сайты жрут дохера ресурсов, бесконечные прокрутки, оптимизированный под мобилу размер текста итд.

У всего этого много разных причин, но одна из них - а потому что современные веб-технологии это позволяют.

Что же касается негативных эффектов у этой ситуации, то есть один, о котором задумываются редко. Чем сложнее веб - тем сложнее создать и поддерживать под него веб-браузер. Соответственно, рынок браузеров поделён между буквально несколькими крупными игроками: Хром Идж Фокс Сафари, а остальные браузеры в рамках статпогрешности. И это не есть хорошо.

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

Короче идея такая чтобы сайты были на примитивных технологиях. Но не ради прикола или ностальгии, а так сказать с практическими целями: А) Чтобы работало на медленном коннекте (не везде в мире интернет быстрый, да и приколы со всякими замедлениями итп никто не отменял), Б) Чтобы не жрало ресурсы (опять же, не у всех в мире компы мощные, и ситуации в экономике тоже бывают разными: сегодня у тебя типа блокчейн крипта нейронки, а завтра ты в землянке с аналогом IBM PC XT из веток), В) Чтобы работало не только в крутых мейнстримовых брозуерах, но и в стареньких или доморощенных.

Я бы запилил, но у меня лапки. Может оно уже есть?

 , , ,

jsx_
()

OpenTESArena 0.15.0

Новости — Игры
OpenTESArena 0.15.0
Группа Игры

Спустя почти 3 года после предыдущей версии вышел движок OpenTESArena 0.15.0.

OpenTESArena – это OpenSource-реализация движка от The Elder Scrolls: Arena (первой игры серии The Elder Scrolls). OpenTESArena нативно доступен на Windows 10/11, Linux, Raspberry Pi, chromeOS и MacOS.

Нововведения:

  • Введён новый программный 3D-рендерер, который соответствует 256-цветной палитре оригинальной игры.

  • Улучшено обнаружение файлов данных TES:Arena (в Windows теперь проверяется директория C: Steam install по умолчанию).

  • Добавлена поддержка Mac на Apple Silicon.

  • Улучшено сохранение скриншотов.

  • Добавлена ​​эксклюзивная опция полноэкранного режима.

  • Добавлена ​​опция коррекции высоты пикселей.

  • В движение игрока внесены небольшие изменения в для соответствия оригинальной игре.

  • SDL обновлён до 2.0.10 для устранения периодической задержки, вызванной SDL_PollEvent().

  • Исправлен цвет тумана в Элден Гроув и Мерквуде.

Для трилогии The Elder Scrolls, помимо ОpenTESArena, развивается OpenSource-реализация движков от второй и третьей части, то есть Daggerfall Unity и OpenMW соответственно.

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

 ,

ConLenov
()

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
()

Где взять всяких фоновых штук?

Форум — Talks

Сижу в наушниках за компьютером. Музыку тихо слушаю, но иногда это надоедает. Хочется чего-то нейтрального, чтобы заглушало шум извне, но не акцентировало на себе внимание. Есть какие-то сборники таких звуков? Чтобы понакачать торрентов и выбрать то, что подходит

 , ,

serg002
()

Музончик

Форум — Talks

Я уверен - многие из нас что-то ставят для фона. И сидят в «ушках» когда работают.

Интересно - что у кого играет, и отвлекает или нет?

Ну, понеслась…

ПыСы. Модель «ушей» - тоже интересует. Сейчас точно тряпками закидают…

 

bugfixer
()

Настраиваем и используем ZSH вместо Bash, или превращаем терминал в раскраску

Статьи — Администрирование
Настраиваем и используем ZSH вместо Bash, или превращаем терминал в раскраску

Я не буду расписывать преимущества ZSH над Bash, отмечу лишь то, что ZSH используется по дефолту во многих дистрибутивах Linux, а также с недавних пор и в macOS (тут должна быть шутка про Торвальдса и его макбук). Главной причиной повсеместной замены bash на zsh является встроенный механизм модулей, поверх которого было навалено такое великолепие, как Oh My Zsh.

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

 , ,

rtxtxtrx
()

Есть ли альтернатива Emacs?

Форум — Development

JB требует VPN.
VS требует Windows.
VSCode сливает всё на сервер дополнениями.

Что выбрать, чтобы надолго и минимум зависимости проекта от компаний?

Vim с плагинами - инородная хрень. Голый Vim для front/back -> (‿|‿)

 , ,

Eulenspiegel
()

нашёл сервис для поиска поддерживаемых форков на github

Форум — Development

http://forked.yannick.io

Заходишь туда, забиваешь имя репозитория, например, mleibman/SlickGrid и тебе показывают табличку, в которой достаточно ясно, какие форки поддерживаются. До этого сервиса я смотрел сеть форков, её нужно было отлистать направо (для этого, правда, есть горячая клавиша) и далее по виду сети как-то угадывать, тыкаться в каждый и т.п.

А можно сразу задать репозиторий в URL, только github.com заменить на forked.yannick.io

http://forked.yannick.io/mleibman/SlickGrid

 ,

den73
()

LMMS и аналоги в 2019

Форум — Multimedia

Полагаю, сабж поднимается в десятитысячный раз на данном ресурсе - хотелось бы актуальной инфы от юзеров, ибо давно не сталкивался со всем этим, но желание пришло. Как там в 2019 с поддержкой звуковых карточек? С латенси подсистемы звука? С поддержкой VST и VSTi (и, возможно, кряков на них)? Приходится ли ради VST и VSTi прикручивать Wine и подобное и подливает ли это масла в огонь? Стоит ли овчинка выделки, или лучше стать дуалбутчиком ради фруктов?

 , , , ,

tchspprt
()

IRC баунсер Бесплатно

Форум — Talks

На базе ZNC. штатно шифрование ваших бесед.

Обращаться тут или на фриноде, контакты в профиле.

Сервер сегодня в Москве. Если будут люди и их запросы, перенесу. Бесплатно.

 ,

mandala
()

Какие счас есть годные фильмы?

Форум — Talks

Что-нибудь свежее 2017-2018, что-то годное (а не из посредственного), по жанрам - хоррор, либо сайнс фикшн (можно и просто с интересным и необычным сюжетом), либо что-то романтическое для просмотра с тян. Ну и в доступе на каком-нибудь рутрекере чтобы было, на всяких приватных фильмообменниках как-то не удосуживался регу искать. А то не слишком часто смотрю, а последние 2 попались - такой шлак редкостный, даже как-то пожалел о потраченных полутора часах жизни.

 

fehhner
()

Retargetable C complier конечно, крут, а новее есть?

Форум — Development

Захотелось мне почитать про устройство и жизнь простого советского человека небольшого Си-компилятора. В качестве подопытного я выбрал Tiny C Compiler от Fabrice Bellard.

В качестве учебного пособия, для общего ознакомления с сабжем DELIRIUM посоветовал Retargetable C compiler Design and Implementation, но чёй-то от книги несёт нафталином... поновее ничего не написано? Книгу дракона не предлагать :-)

 , ,

Twissel
()

Тюнинг ext4

Форум — General

Подскажите как лучше тюнинговать ext4 (для домашнего использования - будут храниться музыка, архивы, pdf-ки и подобное барахло). Имею ввиду какие опции монтирования могут быть полезны, какие настройки tune2fs итп? Диск размером в терабайт.

 , ,

Night_Activity
()

Куда лучше помещать блокировки

Форум — Development

Добрый день.

Возник такой философский вопрос - куда лучше всего помещать блокировку контекста модуля:

  1. В сам контекст и делать функции lock/unlock внутри модуля при вызове каждой функции. Примерно так
    struct mytype
    {
        mutex_t m;
        /* some data to protect */
    }
    
    void mytype_do_something(mytype *ctx)
    {
        mutex_lock(ctx->m);
        /* do something with data */
        mutex_unlock(ctx->m);
    }
    
  2. В сам контекст, но сделать API функции lock/unlock, которые должен вызывать пользователь модуля
    struct mytype
    {
        mutex_t m;
        /* some data to protect */
    }
    
    void mytype_lock(mytype *ctx)
    {
        mutex_lock(ctx->m);
    }
    
    void mytype_unlock(mytype *ctx)
    {
        mutex_unlock(ctx->m);
    }
    
    void mytype_do_something(mytype *ctx)
    {
        /* do something with data with the assumption that user called mytype_lock() */
    }
    
  3. Возложить ответственность за синхронизацию на пользователя модуля, т.е. мьютекс, как минимум будет определен на уровень выше
    void thread_func(void *user_ctx)
    {
        mytype *ctx = ((thread_ctx *)user_ctx)->ctx;
        mutex_t m = ((thread_ctx *)user_ctx)->m;
        /* ... */
        mutex_lock(m);
        mytype_do_something(ctx);
        mutex_unlock(m);
    }
    
    int main()
    {
        mytype *ctx;
        mutex_t m;
        /* init ctx and mutex */
        /* start threads */
        /* ... */
    }
    

Лично у самого предпочтения идут с конца списка в начало, т.е. сначала 3, потом 2 и меньше всего нравится скрытая блокировка.

 ,

Vovka-Korovka
()

utf-8/16 библиотеки для Си

Форум — Development

Пилю тихонько веб-сервис на kore.io и сейчас стал нужен шаблонизатор пока что пишу его прототип ясен пень что он тоже на Си. База будет в utf-8, кодировка html выхлопа тоже utf-8,пользовательские данные через POST тоже могут быть utf-8, пока пишу прототип не парюсь и всё обрабатывается просто как char*. Но так как никогда вплотную не работал с utf-8 боюсь граблей от переменной длинны символов. Если есть что по теме посоветовать, буду рад почитать.

Ну и по традиции cast сишников DELIRIUM, i-rinat, beastie, ncrmnt, Iron_Bug.

UDP: Довольно забавное чтиво, отражающее «всю» красоту работы с utf-8 :D https://gist.github.com/outpunk/1956399

UDP2: я ненавижу unicode

UDP3: не так страшен чёрт как его малюют utf8+char=❤

UDP4: Агрегирую всё нижесказанное и дополнительные ссылки которые могут быть полезны залётным падаванам ::)

Что-бы понять как оно в памяти лежит.

  • Перво наперво => wikipediaUTF-8

wchar_t - если хотите любой символ хранить и юзаете только системы где он 32 бита то пожалуйста, но помните даже utf-8 текст очень часто на 90% состоит из набора ASCII который занимает 1 байт и просто так увеличивать размер текста в четверо заимея лишь возможность безболезненно прыгать по нормализованным символам, ну не знаю, вам решать, можно но не нужно.

  • Код от a1batross за авторством mittorn полезно для понимания как сдвинуть символ, как перевести в другую кодировку и прочее
  • libutf Предложенный beastie предоставляет функции для насущных нужд
    typedef int32_t Rune;
    Rune *runestrcat(Rune *, const Rune *);
    Rune *runestrncat(Rune *, const Rune *, size_t);
    int runestrcmp(const Rune *, const Rune *);
    int runestrncmp(const Rune *, const Rune *, size_t);
    Rune *runestrcpy(Rune *, const Rune *);
    Rune *runestrncpy(Rune *, const Rune *, size_t);
    size_t runestrlen(const Rune *);
    Rune *runestrchr(const Rune *, Rune);
    Rune *runestrrchr(const Rune *, Rune);
    Rune *runestrdup(const Rune *);
    Rune *runestrstr(const Rune *, const Rune *);
    //и прочие не менее полезные
    
  • utf8proc от проекта языка julia написана на С и создана для жизненно необходимой нормализации utf-8.
    ** NFD normalization (@ref UTF8PROC_DECOMPOSE). */
    UTF8PROC_DLLEXPORT utf8proc_uint8_t *utf8proc_NFD(const utf8proc_uint8_t *str);
    /** NFC normalization (@ref UTF8PROC_COMPOSE). */
    UTF8PROC_DLLEXPORT utf8proc_uint8_t *utf8proc_NFC(const utf8proc_uint8_t *str);
    /** NFKD normalization (@ref UTF8PROC_DECOMPOSE and @ref UTF8PROC_COMPAT). */
    UTF8PROC_DLLEXPORT utf8proc_uint8_t *utf8proc_NFKD(const utf8proc_uint8_t *str);
    /** NFKC normalization (@ref UTF8PROC_COMPOSE and @ref UTF8PROC_COMPAT). */
    UTF8PROC_DLLEXPORT utf8proc_uint8_t *utf8proc_NFKC(const utf8proc_uint8_t *str);
    
  • iconv или ICU перекодировка и манипуляция с unicode и другими кодировками
  • glib utf-8/16, содержит практически всё что нужно
  • i-rinal дал очень полезную штуку в которой описаны псевдонимы кодировок, кодировка одна а названий её десяток =)
  • waker предложил свою библиотеку для манимуляций с utf-8, стоит отметить разнообразие функций и скорость.
  • Анон пнул в матчасть =) отдельное спасибо
  • mittorn поделился ссылочкой декодера и ещё одного

Ну и вроде как всё, на последок просто ссылок

https://habrahabr.ru/post/45489/ https://habrahabr.ru/post/311518/ https://ru.wikipedia.org/wiki/UTF-16 http://unicodebook.readthedocs.io/programming_languages.html http://i.voenmeh.ru/kafi5/Kam.loc/inform/UTF-8.htm https://ru.wikipedia.org/wiki/Широкий_символ http://www.cl.cam.ac.uk/~mgk25/unicode.html#c https://habrahabr.ru/post/138173/

 , ,

Deleted
()

Самые лучшие квесты (ура, перепись)

Форум — Games

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

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

Для затравки, мой топ:

1. Secret of Monkey Island
2. Space Quest V
3. LOOM

ну и стандартные для всех репов bass и fotaq тоже хороши

 ,

buratino
()

А существует ли СОВРЕМЕННОЕ годное чтиво на тему оптимальных укладок данных и доступа к ним в современных процах?

Форум — Development

Было какое-то чтиво времён 2008 про пни третьи, но есть ли похожее про Core i7 / Xeon последние?

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

Вот хочется почитать что-то по сабжу, но про новые камни, а не древние пни.

 

hlamotron
()

Добавить swap - swapfile, zswap, zram, etc?

Форум — Desktop

Дано: ноутбук с 8 Gb RAM, SSD, Debian 9. Swap раздела или файла нет. Тяжелых задач нет, потребление памяти редко превышает 4 Gb, остальное - дисковый кэш.

Недавно столкнулся с ситуацией, когда при монтировании каталога через fuse закончилась память. Система зависла намертво, через пару минут убила процесс и развисла. Ситуацию с тем каталогом исправил, но решил на будущее добавить swap.

Я предполагаю сделать swap file, использовать для него ZSWAP, а также задействовать ZRAM, задать swappinness и приоритеты. Прошу людей с реальным опытом подсказать, что именно мне нужно использовать и почему, какое задать значение параметра swappiness.

Перемещено tailgunner из admin

 , ,

aquadon
()