misc/class
lib/jquery_pnotify, lib/moment, lib/lodash, misc/notification, site/engine, misc/social
if( $.browser.msie && $.browser.version <= 8 ) include('lib/respond'); $._social.__cfg = {"init":[{"service":"basic"},{"fb_app_id":"1997094873850041","service":"fb"},{"vk_app_id":"2978320","service":"vk"},{"service":"twi"}],"like":[{"service":"fb"},{"service":"vk"},{"via":"","channel":"","hash_tag":"","service":"twi"}]}; window._SiteEngine = new classes.SiteEngine( { user_id: 0, controller: 'content_tape', action: 'view', content_css_version: '1459538664', social_enabled: 0} );

Faiwer

Блог web-программиста

Загрузка больших текстовых файлов из JS-окружения

5 апреля 2016

Текстовый (и не только) файл можно слепить на ходу, используя client-side JS, и отдать юзеру на загрузку. Обычно для этого используют что-то вроде:

var $a = $('<a/>',
	{
		'href': 'data:' + mime + ';charset=utf-8,' + encodeURIComponent(data),
		'download': _.isString(fname) ? fname : 'file.txt'
	})
	.appendTo(document.body);
$a[0].click();
$a.remove();

Т.е. формируется dataURI с указанием кодировки. А её содержимое экранируется за счёт encodeURI.  В случае, если файл превышает некий предел, то начинаются проблемы в Chrome, который не позволяет скачать содержимое ругаясь на ошибку сети. Полагаю, что сбоит или encodeURIComponent, или же длина аттрибута href превышает некую норму. Firefox такими проблемами не страдает — 100+ MiB грузит, в то время как Chrome спотыкается уже на 15 MiB.

Можно попробовать паковать содержимое как base64, оставив суть подхода (dataURI + a[href]) неизменной, а можно воспользоваться Blob-ом.

Blob

Упаковываем строку в Blob-объект:

new Blob([str], { encoding: 'UTF-8', type: mime });

Получаем псевдо-ссылку на этот Blob-ресурс:

const link = (window.URL || window.webkitURL).createObjectURL(blob);

Ссылка будет начинаться с blob://. Подставляем её в [href] и дело в шляпе.

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

Broadcast в PM2

5 апреля 2016

PM2 — это обёртка вокруг node-cluster для запуска nodeJS приложения, в виде нескольких instance's, каждый из которых представлен отдельным процессом. При этом, в стандартном применении, в качестве master-а выступает внутренний скрипт пакета pm2, а в качестве worker-ов ваше приложение. Они всё так же могут слушать один и тот же порт. PM2 (а может и не он) сам будет распределять запросы между worker-ами.

Рано или поздно может возникнуть необходимость коммуникации между worker-ами. Как её осуществить? Изначально worker-ы друг о друге ничего не знают, но если подключить require('pm2'), то через полученное API можно добиться многого. Нормальной документации к этому API, похоже не существует. Но большая часть доступных методов общая с CLI API.

Для начала необходимо подключиться к pm2 из worker-а:

const pm2 = require('pm2');
pm.conect(err =>
  {
    // с этого момента можно использовать API
  });

Для получения всех запущенных pm2-процессов (кроме master-а) можно воспользоваться pm2.list(). Он вернёт вам ВСЕ запущенные им процессы, включая те, что никак не связаны с вашим приложением. Включая текущий instance тоже. Возвращённая пачка данных изобилует подробностями. 3-10 KiB всякой ерунды на каждый worker.

Отфильтровать оттуда текущий instance можно сравнив pmID текущего процесса (process.env.pm_id), с pmID каждого worker-а из списка. Для того чтобы выфильтровать лишние процессы, не связанные с вашим приложением, можно воспользоваться name-ом (process.env.pm_id). Это значение name из json-pm2-профиля вашего приложения.

Метод list несколько избыточен, мягко говоря. Можно получить только pmID-ки worker-ов по указанному name-у. Для этого есть pm2.getProcessIdByName(name). Останется только выфильтровать из него pmID текущего процесса.

А для того, чтобы разослать каждому из worker-ов сообщение с данными, есть метод pm2.sendDataToProcessId(id, packet). Где packet должен быть объектом с ключами: topic (строка с ключом сообщения) и data (сами данные).

Получать сообщения можно как штатными методами pm2, через launchBus (что у меня сделать не получилось), так и штатным process.on('message', cb) от nodejs.

В итоге простой broadcast можно организовать примерно так:

// при инициализации приложения
pm.connect(err => {});

// отправка сообщения
function broadcast(topic, data)
{
	pm.getProcessIdByName(process.env.name, (err, ids) =>
	{
		if(err) {}
		else ids
			.filter(i => i != process.env.pm_id)
			.forEach(id =>
			{
				pm2.sendDataToProcessId(id, { topic, data }, err => {});
			});
	});
}

// приём
process.on('message', msg => { /* msg */ });

Ложка дёгтя

На getProcessIdByName на моей машине уходит от 6ms до 15ms. Т.е. очень очень много. Код внутри по любому поводу формирует и гоняет между процессами груду данных. Используются теже механизмы, что и задействуются для pm2 monit. Даже если запрашивался только ID

Почему нет никаких стандартных и удобных механизмом для коммуникации между процессами мне не ясно. Причём ни в pm2, ни в nodeJS. С nodeJS впринципе всё ясно, ведь использование node-cluster предполагает, что master всему голова и сам всё порешает. Но в случае pm2 ситуация несколько иная.

Мобильная вёрстка

2 апреля 2016

Прицепил к этому блогу мобильную вёрстку. Учитывая насколько примитивен здесь дизайн, это оказалось несложно. Заняло 10-15 минут. Хочу остановиться на ключевых моментах.

<meta/>

Для начала необходимо указать браузерам то, что они вольны в определённых пределах менять пикселное разрешение viewport-а. По-умолчанию мобильные браузеры исходят из того, что едва ли сайт адаптирован к мобильной вёрстке, а значит, рендерить его в комфортных для просмотра 500px по ширине = убить всю вёрстку вхлам. Поэтому для viewport-а устанавливается что-нибудь вроде 1280px, и экран мобильника имитирует из себя полноценный монитор.

Если влепить в HTML такой мета-тег: <meta name="viewport" content="width=device-width; initial-scale=1.0"/>, то браузер будет волен взять такой width, который вшит в устройство как комфортный для данной модели. В зависимости от того, какого размера экран, это число будет различным. К примеру если у вас какая-нибудь лопата или планшет, то там и за 1000px может перевалить.

Параметр initial-scale немаловажен, т.к. он фиксирует изначальный zoom страницы в значение 100%. Дескать не надо извращений, всё адаптировано и отобразится хорошо.

Media-Queries

Дальнейшие шаманства, в основном, связаны с media-queries. Для тех кто с ними не знаком, это такая возможность задать ряд CSS правил, которые будут применяться в зависимости от ситуации. К примеру только тогда, когда ширина экрана не превышает x-пикселей:

@media screen and (max-width: 980px){}

Или наоборот, когда не менее х-пикселей:

@media screen and (min-width: 980px){}

Вариаций и трюков довольно много. Но эти два самые распространённые. 

В шапке сайта сверху справа отображается иконка дельтаплана. На малых экранах она перегораживает меню. Скрываем её:

@media screen and (max-width: 980px)
{
    header .sh_top:after { display: none; }
}

YouTube и прочие <iframe/>-ы

С ними всё сложнее. Они отображаются с фиксированными, вшитыми размерами. Но ведь у нас плавающая ширина сайта. Что делать? Главная проблема ― как задать height пропорциональным width? Ну можно сделать, скажем так:

  1. Располагаем <iframe/> внутри другого блока-обёртки
  2. Блоку обёртке задаём
    • width: 100%
    • position: relative
    • height: 0px
    • padding-bottom: 56.25%
    • font-size: 0; line-height: 0;
  3. Самому <iframe/>-у:
    • position: absolute
    • top: 0; left: 0; width: 100%; height: 100%;

Т.е., по сути, мы заставляем <iframe/> занимать 100% родительского блока. А сам родительский блок занимает всю ширину экрана. При этом его высота зависит от его ширины, благодаря свойству padding-bottom. Шрифты гасим в ноль, дабы убрать нежелательные отступы.

Изображения

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

.site_content img {
   max-width: 100%;
   height: auto !important;
}

Т.е. ограничиваем их максимальную ширину (это свойство сильнее, чем width). А height насильно срезаем, задав ему auto. Это довольно грубое и нехорошее решение. Но, увы, других простых решений на этот счёт я не знаю. Проблемы:

  • Страница снова прыгает при загрузке, т.к. браузер не знает какой размер примет изображение до того момента, пока оно не загрузится.
  • Если рисунок был отображён непропорционально, то теперь он будет отображаться пропорционально принудительно.

Однако результат того стоит. Если вы знаете решение получше, милости прошу в комментарии или мне на почту ;)

Заметки про OSX, часть 2

1 апреля 2016

  1. dmg файлы всё таки открываются, это установщики. Образы директорий. Для установки может потребоваться изменить настройки безопасности, чтобы можно было устанавливать ПО из неблагонадёжных источников. А так как в Store практически ничего нет…
  2. Похоже, что для OSX в целом характерно не показывать никаких ошибок, будто их нет. Упало и упало. 
  3. Для открытия zip-архивов есть предустановленная софтина, похоже запароленные zip-архивы она не умеет. И как обычно никакой ругани. ПО просто молча падает и всё. Мде. Воспользовался The Unarchiver.
  4. Почти всё на 13" выглядит мелким. На DELL было также, но я сменил разрешение экрана на комфортное и стало зашибись... Теперь и тут. Видимо производители ультрабуков считают, что каждый ходит с лупой.
  5. Клик по неактивному окну не срабатывает, как клик по тому месту, куда вы кликнули. Он только активирует окно. Нужно повторно нажать, скажем на кнопку, чтобы её запустить. В принципе, это удобно.
  6. В зависимости от того, с какой стороны я смотрю на экран, сильно меняется картинка, вплоть до негатива. Печаль. Я уже отвык от таких экранов... Привык к IPS матрицам даже на телефонах.... На DELL XPS 13 экран гораздо лучше.
  7. Я всё ещё не понял, как пользоваться F1-12 кнопками. Вместо них срабатывают FN-кнопки. Видимо придётся гуглить ;)
  8. Dock-панель можно настраивать: размеры, расположение, анимации. Подумываю о том, чтобы разместить её сбоку, ибо по вертикали места маловато. Зато по вертикали всё на расстоянии одного движения на тачпаде.
  9. В обновлениях системы висело обновление штатного видео-редактора iMovie. Каких-то 2 GiB… Зачем он там такой огромный нужен?
  10. Без шамнаских бубнов переключение раскладки по capslock не настроить. Большая часть существующих гайдов на этот счёт устарела. В конечном счёте идея такая: устанавливается приложение, которое умеет заменять клики по capslock-у на какой-либо другой клик (например на клик по вирт. кнопке F19), а уже его биндить в настройках. Для его установки мне потребовалось обновлять систему.
  11. Похоже новая версия OSX пока не умеет ходить по SMB-шарам. Никакой LAN-сетки вобщем, грустно. А вот с Mint-а до AIR-а я добраться смог, причём по какому-то экзотичному протоколу. Но без бубнов, оно само взлетело. Причём получил полный картбланш к системе (о_О). 
  12. Помимо Store есть ещё homebrew. Это местный аналог aptitude. Принцип работы с ним примерно такой же, как и с apt-get. Сами приложения при этом устанавливаются как в nix-ах, а не в /Applications.
  13. Стандартный терминал неудобен, а цветовые схемы в нём просто ужасны. Буду искать другие решения. Или как-нибудь домкратить этот. Хотя бы не такой вырвиглазный шрифт поставлю.
  14. Я всё ещё не понял как жать клавишу delete. На клавиатуре её просто нет. А cmd+backspace срабатывает как delete только в Finder-е. 
  15. Диалоги сохранения и открытия файлы такие же убогие, как и в этих наших линуксах. Т.е. ничего кроме как выбрать файл сделать не позволяют, увы. Это один из тех пунктов где стоило бы равняться на Windows. Для показа скрытых файлов нужно жать не cmd+H или ctrl+H, а ctrl+shift+..
  16. Как и в Ubuntu, тут наличиствует sudo. Используется также.

Заметки про OSX, часть 1

30 марта 2016

Получил презент от конторы - заряженный MacBook AIR. Начал понемногу осваивать OSX. Честно говоря, я не думал, что она покажется мне такой странной после нескольких версий Windows и штук пяти различных Linux DE. Здесь почти всё подругому. Поэтому я решил, что имеет смысл строчить небольшие заметки про особенности OSX.

  1. Добавить второй язык в систему несложно. Это можно сделать в системных настройках. Настоящим же квестом было понять как тут переключать раскладки. Все стандартные известные мне схемы тут же дали сбой. Ни ctrl+shift, ни alt+shift, ни ctrl+alt, ни capslock... Оказывается cmd+пробел. Но.... И эта комбинация клавиш не работает. По ней запускается Spotlight поиск. Его нужно в настройках сочетаний клавиш предварительно отключить. Но... Всё равно не работает. Теперь нужно включить hotkeys для смены раскладки. Называется графа очень не очевидно: Inpus Sources -> Select the previous input sources.
  2. По-умолчанию все окна занимают не всю область экрана. Часть съедает системная панель, дофига и больше съедает докер, и приличный такой зазор остаётся по бокам. Через этот зазор виден рабочий стол. В чём смысл так нерационально тратить пространство я не понял.
  3. Набор кнопок у заголовка окна похож на привычный... Но кнопка закрытия окна закрывает окно, не выключая приложения. Как на android-е. Для выхода нужно воспользоваться какой-нибудь специальной опцией в меню.
  4. Кнопка развернуть работает как-то специфично. Как вернуть окну прежний размер я пока не понял. Можно навести мышь на то место где ранее был заголовок окна и подождать пока он появится. А там уже снова нажать на эту кнопку. Полагаю, что должны быть способы и поудобнее.
  5. Скроллить можно и неактивные окна. Тачпадом. Т.е. тачпад скроллит то окно, над которым курсор, не передавая в него фокус. Непривычное, но интересное поведение.
  6. В качестве проводника по файлам какое-то упрощённое приложение под названием Finder. В нём по умолчанию нет адресной строки. А может оной и вообще нет. По клавише enter при активном элементе не происходит перехода внутрь него... вместо этого система предлагает его переименовать. Backspace не возвращает вас на уровень выше, а вот cmd+вверх возвращает.
  7. Дебильная система со скроллбаром. Он отображается везде только тогда, когда вы скроллите. А до тех пор не всегда даже понятно, помещается ли весь контент в блок, или же его нужно проскроллить. UPD: это настраивается.
  8. На MacBook-ах очень удобный тачпад. Главная его идея в том, что он целиком является одной прожимаемой кнопкой. И клик будет защитан именно тогда, когда кнопка прожмётся. С характерным звуком. Гораздо удобнее, чем любой другой тачпад, которым мне приходилось пользоваться. Нет ложных срабатываний. Помимо прочего этот тачпад по разному реагирует на одно и два прикосновения одновременно. По сути тут есть дву-пальцевые жесты. Очень удобно скроллить ими.
  9. В стандартных интерфейсах ОС я часто сталкиваюсь с непониманием - сработала ли кнопка или нет. Обычно становится ясно только спустя некоторое время.
  10. По субъективным ощущениям ОС работает медленнее, чем мой аналогичный по железу Dell XPS 13. А ещё монитор не сенсорный и не переворачивается как на Dell-е :)
  11. Меню у всех приложений не в окнах, а верхней панели. Тянуться приходится далеко. Меня эта фича ещё в Unity раздражала. Впрочем Unity много странных UI решений из OSX спёр. Тот же поиск и докер.
  12. При копировании и переключении языка система всплывающей подсказкой уведомляет вас об этом. Классная фича. Мне на Mint-е такой не хватает.
  13. Даже текстовые комбинации клавиш другие. Придётся изучать новые. К примеру я пока не знаю, как перенести курсор на одно слово левее. Но нашёл альтернативу клавишам home и end: cmd+вправо и cmd+влево.
  14. С непривычки часто включаю контекстное меню на тачпаде двумя пальцами... случайно. 
  15. Хотел проставить браузер Vivaldi. Официальный сайт предложил мне скачать некий dmg-файл. Что с ним делать система не знает. Пока не разбирался почему. А в Store его пока нет. UPD: оказывается установщик просто молча дохнет, но таки запускается.
  16. Этот Store притча во языцах. По дурацки построенное, не очевидное и не продуманное приложение с кучей бюрократии. Прежде чем я смог установить yandex.disk оттуда мне пришлось по 10 раз заполнять одну и ту же форму, кликать одни и те же кнопки, пока я не понял что именно и в какой последовательности от меня хотят. Одна из форм чуть ли не мою родословную затребовала.