LINUX.ORG.RU

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

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

Какую нишу займет/занял язык программирования Rust?

Форум — Web-development

Добрый день! Я не имею опыта в программировании, за исключением мелких программ на С++. Я гуглил за языки, и наткнулся на Раст, и его советуют учить новичкам, но я не до конца понимаю его нишу? Где его применяют? Где он может в будущем закрепить себя? Стоит ли его учить для новичков, или лучше начать с питона ?

 , ,

LordCopalov
()

Родительский контроль через Telegram (и без)

Форум — Talks

Всем привет. В общем, история такова.

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

Возможности:
Управление через Telegram
Вход в настройки по скрытому паролю
Установка количества времени по-умолчанию
Добавление и убавление времени на текущий день
Не зависит от системных часов
Минимальное потребление ресурсов ПК
Не требуются права администратора

Управление через Telegram:
Нужно создать бота через @BotFather
Создать группу и добавить туда созданного бота
Дать боту права на чтение сообщений
В настройках программы указать bot_token (с приставкой bot) и chat_id группы (узнать chat_id здесь https://api.telegram.org/botXXX:XXX/getUpdates)

Обойти можно, но мне защита не потребовалось. Так же, если захотеть, можно зашить в винду его посерьезнее
Телеграм как опция. Можно всем рулить через cli
В чат можно добавить родителей и ребенка (не забудьте отозвать права на текст)
Для работы используются гнутые утилиты.
«Исходники» и бинари тут https://github.com/iroxville/pclocktime

 , ,

admucher
()

Почему в современных фильмах такой тихий звук?

Форум — Talks

Когда я смотрю новые фильмы (после 2010 года), в них очень качественный атмосферный звук, но вместе с тем он очень тихий. Приходится буквально выкручивать регулятор громкости у телевизора на максимум, а если я смотрю его на телефоне, то предпочитаю смотреть его в наушниках. При этом когда я смотрю старые фильмы (до 2000 года), то в них со звуком как правило все нормально.
Телевизор у меня уже относительно старый (Samsung 2013 года), а в последнее время расплодилось куча звуковых форматов (DTS, Dolby Surround и прочие), но телефон-то у меня современный и тем не менее у него тоже хреновый звук, когда слушаешь из встроенных динамиков.
Вот сейчас смотрю «Интерстеллар» 89 ГБ рип. Картинка отличная, но звук отвратный, потому что очень тихий. Громкость у телевизора выкрутил до 60, хотя обычно смотрю его (фильмы, ТВ, Youtube) на 20-25. В чем причина?

 , ,

Rinaldus
()

Улучшаем изображения и видео с помощью нейросетей [увеличение, очистка артефактов сжатия, 60 FPS]

Форум — Talks

(!) Во-первых, если вы хотите сразу, ни в чем не разбираясь, просто сходу нажать пару кнопок и получить результат, то вам нужна графическая программа, которая написана только под Windows -
Waifu2x-Extension-GUI: https://github.com/AaronFeng753/Waifu2x-Extension-GUI/releases
Она уже содержит все необходимое, и позволяет в полностью автоматическом режиме разбить видео на кадры, улучшить их с помощью одной нейросети, увеличить плавность (60 FPS) в другой, и собрать итоговое видео в пару кликов.
Пример такой конверсии: https://www.youtube.com/watch?v=qGk4E9ss95s
Оригинал видео: https://www.youtube.com/watch?v=ht39JUr5wm8

Свободные Vulcan-реализации нейросетей представлены 是 nihui !

Нейросети для улучшения фотографий

Topaz Gigapixel AI - Лучшая нейросеть для улучшения фотографий, дает самое четкое и детальное изображение. Только для Windows и macOS. Может работать как плагин для Photoshop. Встречайте на всех файлопомойках страны.

realsr-ncnn-vulcan - Лучшая-свободная нейросеть для улучшения фотографий, победитель конкурса 2020 года среди собратьев. Скачать: https://github.com/nihui/realsr-ncnn-vulkan


Нейросети для улучшения рисунков

Topaz Gigapixel AI - Также является лучшей и для улучшения рисунков, дает самое четкое и детальное изображение. Только для Windows и macOS. Может работать как плагин для Photoshop. Встречайте на всех файлопомойках страны.

waifu2x-ncnn-vulcan - Лучшая-свободная нейросеть для улучшения рисунков, значительно опережает по качеству всех своих собратьев. Скачать: https://github.com/nihui/waifu2x-ncnn-vulkan


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

Topaz Video Enhance AI - Дает более лучший результат на видео-материале, чем нейросети, обученные на статике. Подходит как для фильмов, так и мультфильмов. Только для Windows и macOS. Встречайте на всех файлопомойках страны.


Нейросети для увеличения плавности видео (60 FPS)

rife-ncnn-vulkan - Лучшая нейросеть для увеличения плавности видео. На голову опережает всех, как по качеству, так и по скорости. Скачать: https://github.com/nihui/rife-ncnn-vulkan


Обзоры и примеры

Обзор RealSR - https://linuxreviews.org/RealSR
Обзор Waifu2x - https://linuxreviews.org/Waifu2x

480p видео, улучшенное при помощи Topaz Gigapixel AI - https://www.youtube.com/watch?v=b-U8CA1yF4U
(Topaz Video Enhance AI даст еще более лучший результат, при использовании модели Gaia-HQ)

Плейлист видео с увеличенной плавностью (60 FPS) - https://www.youtube.com/playlist?list=PLbZqiWS25MmQAJSuE4MbsvzriRfew_kNQ

Moderators
()

Ваши 10 самых любимых книг

Форум — Talks

Делитесь:)

 

bryak
()

Что сейчас хотят от Linux-админов и сколько платят?

Форум — Talks

Наконец понял, что разработка - это не мое. Сначала были кранчи и бесконечные дедлайны в игровой индустрии, после я перешел в другую сферу, но потом столкнулся с российской криптой и мутными ГОСТами, которые не осилил.

Слышал о том, что многие админы переходят в разработчики, но что на счет обратного?

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

На текущей работе часто имел дело с серверами, мелланоксовским железом и highload.

 ,

Meyer
()

Нейроябеда тестовый топик

Форум — Talks

В этот топик будет постить Нейроябеда во время тестирования и отладки.

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

 , ,

shkolnick-kun
()

Super UEFIinSecureBoot Disk — запуск любых ОС и .efi-файлов с флешки без отключения UEFI Secure Boot

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

Super UEFIinSecureBoot Disk — образ диска с загрузчиком GRUB2, предназначенным для удобного запуска неподписанных efi-программ и операционных систем в режиме UEFI Secure Boot.

Диск можно использовать в качестве основы для создания USB-накопителя с утилитами восстановления компьютера, для запуска различных Live-дистрибутивов Linux и среды WinPE, загрузки по сети, без отключения Secure Boot в настройках материнской платы, что может быть удобно при обслуживании чужих компьютеров или корпоративных ноутбуков, например, при установленном пароле на изменение настроек UEFI.

Образ состоит из трех компонентов: предзагрузчика shim из Fedora (подписан ключом Microsoft, предустановленным в подавляющее большинство материнских плат и ноутбуков), модифицированного предзагрузчика PreLoader от Linux Foundation (для отключения проверки подписи при загрузке .efi-файлов), и модифицированного загрузчика GRUB2, который загружает EFI-файлы самостоятельно, не используя функции UEFI.

Во время первой загрузки диска на компьютере с Secure Boot необходимо выбрать сертификат через меню MokManager (запускается автоматически), после чего загрузчик будет работать так, словно Secure Boot выключен: GRUB загружает любой неподписанный .efi-файл или Linux-ядро, загруженные EFI-программы могут запускать другие программы и драйверы с отсутствующей или недоверенной подписью.

Для демонстрации работоспособности, в образе присутствует Super Grub Disk (скрипты для поиска и загрузки установленных операционных систем, даже если их загрузчик поврежден), GRUB Live ISO Multiboot (скрипты для удобной загрузки Linux LiveCD прямо из ISO, без предварительной распаковки и обработки), One File Linux (ядро и initrd в одном файле, для восстановления системы), и несколько UEFI-утилит.

Диск совместим с UEFI без Secure Boot, а также со старыми компьютерами с BIOS.

>>> Репозиторий диска

 , , , ,

ValdikSS
()

Сбор донейтов на разработку движка (собрано 30%)

Форум — Talks

Привет, котаны.

Есть мысль нанять одного разработчика на фуллтайм для разработки движка.

В треде просьба отписаться тем, кто готов поддержать инициативу рублем.

Донейт будет раз в месяц, оплату реализуем через какой-нибудь patreon для простоты.

Если наберётся желающих хотя бы на 100к рублей в месяц — в Job откроем вакансию на это дело.

Если наберётся желающих больше, чем на 200к — откроем две вакансии.

Инициатива моя, но разработчика будет предложено одобрить maxcom’у, так как ему принимать pull request’ы.

Работа будет прежде всего по задачам в https://github.com/maxcom/lorsource/issues, улучшению покрытия тестами, исправление технического долга, а также реализации новых стандартов аутентификации вроде FIDO2. В общем работы для одного человека более чем достаточно.

Просьба воздержаться от сторонних обсуждений.

Текущее состояние ~31679 ₽ в месяц:

 , , ,

Deleted
()

лавкрафт, с чего начать

Форум — Talks

Читал Петухова Звездную месть. Проглотил захлебом всю пенталогию.

Потом узнал, что есть Лавкрафт. Начал читать, но по уровню угара и хоррора по сравнению с монстрами Полигона показалось каким-то детсадом.

Может я не с той книжки начал? Что посоветуете?

 

dikiy
()

Openbugfarm - опенсорсная ферма жуков для еды

Форум — Talks

Я не шучу. Пруф, у них и на гитхабе есть аккаунт, где содержаться чертежи фермы и инструкция по сборке и фармингу. Предлагается выращивать сверчков, мучных жуков, тараканов

Утверждается, что это довольно дешёвый источник еды для человека, содержащий необходимые организму белки.

Было бы интересно услышать ваше мнение. Здравая это идея или идиотская? Насколько рентально, сложно? Интересно, что есть опенсорсные материальные объекты

 ,

russian-turist-2019
()

Программа учета финансовых средств имитирующая тетрадь [UPD]

Форум — Talks

Доброго времени суток

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

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

Буду рад прочитать ваши советы, а так же про альтернативные простые методы учета.

Спасибо



-------------------
UPD:

Спасибо за советы, но увы, оказалось все немного не применимо, как и советы гугла. К сожалению, Excel оказался в некотором плане неудобен, если откинуть даже защиту ячеек от перезаписи (частично удалось решить), посему, думаю заказать написание сего ПО.

Сколько будет стоить такое для Win примерно?

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

Вот схематический набросок в Excel https://i.imgur.com/rUewTiF.png

Исходники будут под GPL 3

 , , ,

admucher
()

Ушла эпоха

Форум — Talks

После недавнего хайпа на тему возможного Сталкер 2 решил вечерок побегать. Пошел купил сразу все три ключа (и разработчиков поддержать, и может поиграть в них потом когда).

Запустил самую последнюю. Выкрутил все на максималки, поставил нативное разрешение. И... И... и ужаснулся!

Оно убогое шо ппц. Кругом лесенке вместо картинки. Текстовые диалоги читать очень неприятно (ввиду высокого разрешения монитора). Картинка хрень. Раньше казалась прикольной, а теперь хреновая хрень! (Тут стоит пояснить, что я с тех пор больше ни в одну подобную игру не играл, я вообще не любитель)

И как дальше жить с таким грузом?

 ,

dk-
()

Годной SF нить

Форум — Talks

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

Итого получилось:

  • «Спин» Роберт Чарльз Уилсон
  • «Червь» Джон МакКрэй
  • «Звёздный молот», «Боевая форма», «Мастер боя» Кристофера Раули
  • Лоренс ван Котт
  • Крис Невилл
  • «Пламя над бездной», «Глубина в небе» Вернор Виндж
  • «Диаспора» Грег Иган
  • «Задача трёх тел» Лю Цысинь
  • Пандорум
  • «Наследство», «Наследие» Уильям Кейт
  • «Схизматрица» Брюс Стерлинг
  • «Мировая Война Z/Война Миров Z» Макс Брукс
  • «Анафем» Нил Стивенсон

Всем спасибо!

 

gadzira
()

Достаточный уровень английского для путешествий

Форум — Talks

Хотел этим летом куда-то съездить, но всё накрылось медным тазом до следующего года. И чтобы времени зря не терять захотелось прокачать английский до уровня свободной беседы с носителями языка. Причём делать это хочется из любого состояния (хоть под веществами).

Сейчас имею A2 уровень (бумажка есть) и ~3900 словарного запаса. Хочется за годик подняться до B1/5-6к слов и начать легко понимать устную речь (сейчас с этим сложно). Если чтобы поднять свой уровень и словарный запас надо надо курить грамматику/читать книжки на английском/etc, то как воспринимать на слух хорошо? Смотреть фильмы на английском без русских субтитров очень сложно (вообще ничего не понимаю практически), а с субтитрами тупо начинаешь их читать и не слушаешь речь вообще. Попасть в англоязычную среду не представляется возможным. Как быть?

 ,

FluffyPillow
()

Вспомнить сериал

Форум — Talks

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

 

Frost
()

Линукс выкинут

Форум — Talks

Из Fuchsia.

В принципе, было очевидно, что места линуксу в новой ОС не видать, но то что сама по себе андроид машина остается - немного неожиданно, хоть и логично. Возможно, запихнут ее просто как легаси, а сами выкатят какой-нибудь NDK на Dart с очком и барышнями.

Вот и закончится период, когда линукс был везде и всюду.

Что вы думаете?

------------------
http://4pda.ru/2018/04/29/351016/

 , , , ,

admucher
()

Звук с компьютера на Android устройство

Форум — Desktop

Хочется странного - слушать музыку с ПК под Linux-ом через WiFi/интернет. А играть она должна не на компе, а на мобильном устройстве. Есть подобное в природе? Неплохо если технически декодирование аудиопотока будет происходить на компе, а воспроизводиться будет какой-нибудь простенький поток, который не слишком жрет батарейку. Это избавило бы меня от необходимости находится рядом с ПК, если я слушаю аудио.

 , ,

peregrine
()

Принимаю пари

Форум — Talks

Товарищи dk- и Oberstserj заключили пари на тему будущего поставок газа из России. Через 10 (десять) лет. За базу взяты текущие показатели.

Позиция dk-:

вангую рост минимум в 1,5. И точно не падение.

500 000 ₽ (комментарий)

Позиция Oberstserj:

считать будем именно объем газа поставленного с территории РФ за кардон. Я ж собственно поэтому поводу спор затевал. Вангую падение не мене, чем в 2 раза.

500 000 ₽ (комментарий)

На кону:
Коньяк 50 лет. Франция. Бутылка. Из приличного магазина. До 0,5-1к евро.

Товарищи обменялись контактами в почте и желают друг другу долголетия и трезвой памяти.

- - -

Товарищи dk- и greenman заключили пари на тему курса USD/RUR по итогу 2018 года.

Позиция dk-:

на 01.01.2019 рубль, если и ослабнет, то не более чем на 10% относительно показателя 01.12.2017.

Не только китайские коррупционеры (комментарий)

Позиция greenman:

[ослабнет ] более, чем на 10% к курсу на 01 декабря 2017. Курс рубля РФ к доллару США.

Не только китайские коррупционеры (комментарий)

На кону:
Бутылка стоимостью до 2 000 рублей (в ценах на сегодня), по выбору победителя.

Товарищи обмениваются контактами в почте и желают друг другу долголетия и трезвой памяти.

 

dk-
()