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-программиста

Загрузка binary-файла

28 января 2016

Возникла задача ― загрузить с сервера XLSX-файл. Запрос ― POST. Файл, понятное дело, бинарный. На проекте уже подключена jQuery, и, первым делом, я решил воспользоваться её стандартным $.ajax. Сколько не бился ― так и не удалось получить blob-результат от запроса. Использовать же решение через iframe-ы и form-ы тоже не хотелось. Во-первых уж больно костыльно (привет 90-ые). во-вторых нужно будет ещё чудить с передачей параметров POST-запроса (content-type: json). Гуглил-гуглил, но ни одного вменяемого решения (которое не стыдно было бы утащить в проект) я так и не нашёл. Но, т.к. интересует поддержка только последних версий современных браузеров, удалось найти и применить более изящное и функциональное решение. Итак…

  1. Забудем про jQuery и воспользуемся нативным XMLHttpRequest-ом
  2. Указав ему responseType = 'blob'.
  3. Получившийся результат (blob) загоняем в URL.createObjectURL.
  4. В результате получим псевдо-URL ссылку, начинающуюся с "blob:".

Ну а дальше уже по-желанию. Я генерирую новый <a/>-тег, задаю ему нужные аттрибуты download и href (та самая blob-ссылка), вызываю у ссылки .click(). Апосля ссылку удаляю. Проверял в Firefox-е и Chrome-е.

Небольшая заготовка:

function loadBlob(params, data)
{
	return new Promise((resolve, reject) =>
		{
			const xhr = _.assign(new XMLHttpRequest(),
				{
					onload(){ resolve(xhr.response); },
					onerror(e){ reject(e.target.status); }
				});
			xhr.open(params.method || 'POST', params.url);
			xhr.responseType = 'blob';

			const contentType = params.contentType || 'application/json';
			xhr.setRequestHeader('content-type', contentType);

			if(contentType === 'application/json')
				data = JSON.stringify(data);
			xhr.send(data);
		});
}

подробности…

For-of и браузеры

28 января 2016

Оказывается, браузеры по-разному относятся к итерированию цикла for-of. Судите сами:

for(const a of []) ; // chrome: ok, ff: syntax error

На что ругается Firefox? Говорит, что a указана, как константа, а используется как переменная. Но почему?

for(let a of [1, 2])
  setTimeout(() => alert(a));
  • Chrome: 1, 2
  • Firefox: 2, 2

Всё просто. Chrome каждую итерацию пересоздаёт scope, а Firefox довольствуется одним. И если с const ошибка вылезет сразу (правда только в Firefox). То в случае чего-нибудь асинхронного ждите трудно-вылавливаемые баги, которые проявятся только в Firefox-е. В спеку мне лезть лень, но код генерируемый babel-ем ближе к Chrome-варианту. Кто прав?

Javascrpt ES6 и классы

8 декабря 2015

Понемногу перебираюсь на JavaScript ES6 синтаксис. По мере появления его нативной поддержки в nodeJS и Chrome. ES6 таит в себе множество не очевидных вещей. Больше всего я их встретил в class-ах. Классы в ES6 это обширный набор синтаксического сахара для prototype. С ними писать код приятнее, да и сам синтаксис вынуждает подходить к этому дисциплинированнее. В этой мини-заметке я опишу пары интересных моментов:

  • Синтаксис допускает только короткие имена методов;
  • Тело класса описывает методы, которые будут помещены в prototype и статические методы, которые будут помещены в сам объект класса. Так вот… Таким образом вы можете поступить только с методами и generator-ми. Т.е. никаких хешей, никаких чисел, строк, регулярных выражений. Всё, что вы захотите поместить сверх методов и генераторов будет идти уже отдельными строками кода после описания класса;
  • Это ограничение довольно понятно. Дескать дисциплинирует все подобные вещи определять в конструкторе и не позволяет переписывать детьми в прототипе. Но… Почему то nodejs не поддерживает статические generator-ы. Не статические поддерживает, а на статические лови syntax error. Наверное это баг (static * name());
  • Для удобного доступа к родительским методам (имею ввиду наследование классов) придуман super. Но… Если вы переопределяете конструктор класса, вы:
    1. Обязаны вызвать super, чтобы получить доступ к this
    2. Да да. До этого момента this отсутствует как таковой. Соответственно вызвать родительский конструктор, предварительно задав какие-нибудь поля новому объекту, без бунов не получится. Зачем они так сделали? о_О.
  • Методы и генераторы заданные в теле класса будут не-enumerable. Но для получения их списка можно воспользоваться: Object.getOwnPropertyNames(Object.getPrototypeOf(this)).

Пока что это все грабли на которые я наткнулся. В скором времени ожидается поддержка со стороны Firefox.

Переносы в тексте

24 ноября 2015

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

Итак. Что мы имеем? Мы имеем css3 свойство hyphen: auto. Но довольно унылую поддержку. Во-первых его не умеет ни Chrome, ни его клоны. Во-вторых его не умеет Firefox под linux. В третьих (в чём я не уверен) поддержка идёт на уровне встроенных словарей. Т.е. если вам потребовалась поддержка переносов в, к примеру, казахском тексте, вас снова ждёт облом. Но само свойство хорошо. В идеале вбив его можно забыть про все проблемы, ибо браузер сам их порешает.

Что у нас есть ещё вналичии? Есть такие опции как word-breakword-wrapoverflow-wrapline-break. К сожалению никакая комбинация этих свойств не позволяет добиться нормальных переносов в таблице, которую перекашивает, как раз из-за отсутствия этих переносов. Например вы можете добиться вот такого вот результата:

Подробности под катом…

подробности…

ETag, nginx и gZip

22 сентября 2015

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

Ситуация. Сервер сайта генерирует файл и пытается отдать его как статику, задействовав заголовок ETag. Реализовывается вся необходимая логика с возвратом 304 и поглядыванием в if-none-match. Но в итоге упираюсь в то, что браузер в упор не видит ETag заголовок. Первым делом полез смотреть заголовки в curl -v. Но заголовок возвращается. Начинаю ковыряться в разных статьях про ETag на предмет подводных камней. Ничего не нашёл, всё должно работать просто и надёжно. Пробую в разных браузерах. Они в упор не видят заголовка. Начинаю эксперементировать со значением заголовка, расположением в списке других заголовков, прочей чертовщиной заниматься. Очередной подход к google-у выдал мне причину.

Оказалось, что дело в том, что в конфигурации хоста nginx у меня прописано сжимать GZip-ом все ответы с определённым content-type-ом. А nginx при этом срезает ETag. Именно поэтому я видел заголовок в ответе curl-а, но не видел в браузерах. Поправил конфиг ― заработало.

Помимо прочего, я некоторое время не мог завести базовый механизм работы Last-Modified. Браузер упорно не хотел кешировать файл, несмотря на наличие всех необходимых заголовков. Оказалось всё дело в формате даты. Найденные мною быстрые решения для momentjs были кривыми, и браузер их не понимал. В конце некоторых дат указывалось смещение в виде +00:00, и всё ломалось.