LE Blog

Инженер с поэтической душой

03.08.2016 firtree_right Июль

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

♯♯♯

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

♯♯♯

Ещё месяц проходил под знаком маргинальности. Когда я зимой угорел по Ингрессу, среди всех моих знакомых я был в подавляющем меньшинстве. Никто не играет в эту игру. Теперь, когда вышли Покемоны, я оказался в подавляющем меньшинстве в тусовке ингрессовцев, кто не стал играть в это.

А ещё мы сходили на совершенно прекрасный фильм Swiss Army Man, но я уверен, что он понравится меньшинству моих знакомых. Хотя я всем готов рассказать, какой он прекрасный. Но это потому, что я люблю постмодернизм и сказки о смерти.

♯♯♯

Как раз, когда все страсти успокоились, хочу сказать про флэшмоб «я не боюсь сказать». Живя в большом городе уже довольно долго, я обнаружил, что стал совершенно невосприимчив к просьбам помочь деньгами. В большом городе много попрошаек, но со временем им всё больше и больше приходится пускаться на хитрости. В большом городе вероятность встретить человека, история которого про зачем ему нужны деньги правда, крайне мала. Большой город отключает во мне сострадание случайному человеку. Я всегда настроен критически. Интернет создаёт перманентное ощущение большого города. Здесь тоже всё время орудуют попрошайки. И здесь ещё проще обманывать. И здесь нужно ещё больше всё проверять. Невозможно точно определить, сколько действительно историй людей, нуждающихся в сочувствии, а сколько историй людей просто жаждущих внимания. И самое плохое, что делает в этой ситуации интернет — он обесценивает сам факт сочувствия и само понятие о поддержке. Лайки, репосты, реакции, комментарии — это всё настолько мало стоит человеку, что любому проще не разбираться и не вникать, а тупо полайкать всё подряд. И получается странный дисбаланс: с одной стороны никто не может прожить все те истории, которые на него сыплются из интернета, а с другой стороны, любой выплеск в этом тренде получит море поддержки. Вывода не будет. Пусть расцветают все цветы.

♯♯♯

Если в интернете написать в одном месте два комментария, и в одном повести себя как мудак, а в другом подробно, аргументированно, вежливо и взвешенно ответить, то воспринимать тебя будут как мудака. Потому что слишком много информации в интернете, никто уже не успевает во всё вникать. А эмоциональная реакция быстрая и однозначная. На неё проще опираться. Нас ожидает много интересного впереди.

01.07.2016 firtree_right Июнь

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

♯♯♯

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

♯♯♯

В целом, весь месяц и особенно к концу, чаще пребываю в мизантропии и поганом настроении. Может, кризис среднего возраста, может, приближающийся день рождения, а может, просто начинает формироваться склад характера, который будет доминировать в старости. Глядишь, так и стану ворчливым злым старикашкой: «Прочь с моего газона, поганцы!» :)

♯♯♯

Внезапно угорел по когнитивным искажениям. Кстати, какое у вас любимое из списка?

♯♯♯

Представилась возможность сказать: «Я же говорил!» Но я не стал. Во-первых, действительно, иногда более эффективно расходовать ресурс, когда ситуация уже происходит по факту, чем готовиться на всякий случай. А во-вторых, «я же говорил» — это тоже когнитивная ошибка. Я много чего говорил, что потом не произошло. Поэтому в момент предсказания по-прежнему неясно, нужно ли меня слушать. Также, если не прессовать заранее и не говорить «я же говорил», то, возможно, удастся не превратиться в злобного старикашку. :)

♯♯♯

Ещё не досмотрел шестой сезон «Игры престолов», но заметил интересный ход. С самого начала саги авторы заставили всех переживать за героев, дав понять, что любого из них могут убить в любой момент. Это интересный ход, потому что гораздо привычнее в литературе считать, что если герой удостоился заметной роли в повествовании, то его жизнь каким-то образом важна для сюжета. У него есть Предназначение, что-то должно срастись и совпасть и выстроиться в красивую линию. (Возможно, потому что вступает в силу ассоциация с собой, а про себя всегда есть какая-то история, которая всё обосновывает, потому что тяжело жить в бессмысленности бытия) Убивая героев первого плана, авторы шокируют публику. Но со временем и к этому публика привыкает, начиная следить за развитием сюжета, как за бесполезной вознёй и, видимо, теряет интерес. И вот уже авторы вынуждены воскрешать героев, возвращать забытых героев в повествование. Но при этом уже открытым текстом говорить об их избранности и предназначении. Что будет, когда и это окажется ложью? Ощущение собственной малости и незначительности — это сильное переживание. Даже, временами, интересное и привлекательное. Но кому в него охота тыкаться носом постоянно?

♯♯♯

Ещё значимое событие произошло в моём рабочем процессе. Я разбирался с показом панорам с помощью таблиц стилей. Там в браузере всё немного по странной логике построено, но вроде бы я разобрался. Собрал кубик, посчитал матрицу поворота, начал тестировать. И обнаружил, что поворот искривляется в определённом месте. То есть в одном из 16 членов матрицы поворота я ошибся в знаке. Ну, допустим, в 10 из 16 я был уверен. Но я не смог найти у себя ошибку в выводе и тупо методом тыка нашёл неверный знак в одном из оставшихся под вопросом 6 членов. То есть, моя матрица поворота теперь работает, но я не знаю, почему. Такое со мной впервые.

♯♯♯

Опять про то, что тело подстраивается наименьшими усилиями к нагрузкам. Когда я регулярно занимался пилатесом в течение уже двух лет, я сходил впервые на пробное занятие по файту. У меня болело совершенно всё. Было больно садиться и вставать, спускаться по лестнице, двигать руками. Но я пошёл, тем не менее, на пилатес и с удивлением обнаружил, что откатал полную программу без проблем. Ничего из того, что болело, не мешало заниматься пилатесом. Сейчас, когда регулярным стал уже файт, и я в спортзале сделаю неожиданные силовые нагрузки. То, что болит после них, абсолютно не влияет на способность отработать полное занятие по файту.

♯♯♯

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

23.06.2016 firtree_right Тестирование сервера Node.js

Вводная

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

Для тестирования кода на ноде я использую жасмин. Для запуска задач — галп.

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

Staged reality

Кто запускает сервер

Первое, что хотелось бы сделать, это запускать сервер отдельно для каждого теста. Желательно на другом порту. Может быть, даже параллельно с работающим сервером, на котором мы что-то пробуем руками. Для этого в ноде есть возможность определить, находимся ли мы в основном файле, или его загружает какой-то другой файл:

if (require.main === module) {
    app.use('/', require(path.join(__dirname, 'routes', 'main'))());
    var server = app.listen(5000, function () {
        ...
    });
} else {
    exports = module.exports = function (options) {
        app.use('/', require(path.join(__dirname, 'routes', 'main'))(options));
        return app;
    };
}

Таким образом, уже в тестовом фреймворке мы будем запускать:

app = require('index.js');
server = app(...).listen(6000, function (err) {...});

перед каждым тестом, а после каждого теста:

server.close(function (err) {...});

Очень удобно!

Имитация библиотек

Второе, что бы хотелось сделать при тестировании сервера, — имитировать используемые им библиотеки, чтобы тестировать только код сервера, а не библиотеки. Связки тоже важны, но только тогда, когда мы этого сами хотим. И тут появляется интересный момент. Если мы хотим имитировать библиотеки при тестировании, нам нужно использовать определённый паттерн при программировании. Паттерн фабрик. То есть мы не просто тестируем свой код, но устраиваем архитектуру и стиль так, чтобы его удобнее было тестировать.

В случае с паттерном фабрики мы возвращаем в качестве модуля не объект, а функцию, которая создаёт объект в соответствии с нашими параметрами:

exports = module.exports = function (options) {
    options = options || {};
    var customLib = options.customLib || require(...);

    /* GET main page. */
    router.get('/', function (req, res, next) {
        res.end('Real server text. CustomLib: ' + customLib.name);
    });

    return router;
};

Как видно в вызовах выше, мы передаём сквозным образом наши опции при тестировании, а при нормальном запуске, не передаём ничего.

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

Для самостоятельного изучения

Полный код примера в сборе

09.06.2016 firtree_right Куда уходят программисты

Сегодня хочу предаться размышлениям и выпустить статью без единой строчки кода. Программирование как общедоступное знание уже существует настолько давно, что уже есть люди, которые родились в этот период и начали программировать. То есть для них программирование, как понятная сфера деятельности, существовало всегда. Также это означает, что есть люди, которые были в этой сфере, а потом ушли, оставив после себя следы: разные и по разным причинам.

Missing people

Без следа

Недавно я проникся новым молодым языком программирования elm. Читал статьи, потом решил потрогать руками. Установил подсветку синтаксиса для текстового редактора, поставил библиотеки, и вдруг — беда! Поскольку язык молодой (максимум 4 года ему), то синтаксис немного поменялся в последнем релизе. И оказалось, что подсветка не работает с новым синтаксисом.

Я полез разбираться, начал строчить отчёты об ошибках и обнаружил такую историю. Человек, который написал и поддерживал подсветку синтаксиса для элма, пообещал добавить необходимые изменения сразу после выхода новой версии синтаксиса, и исчез бесследно. Имя его  deadfoxygandpa. Совпадение?

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

Уйти красиво

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

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

Почему

Первой же историей, которая обратила мой внимание на то, что люди отделены от своего виртуального персонажа, была история Why the Lucky Stiff. По его учебнику я изучал руби. Но в какой-то момент он решил уйти из интернета. Перед тем, как уйти в оффлайн, он написал: «Программирование весьма неблагодарно. Через год ты видишь, как твою работу замещает лучше сделанная, а через несколько лет её уже и не запустить». Это он сказал в 2009-м.

Сейчас сроки устаревания сжались ещё больше. О чём я писал после перерыва. От того и столпов-авторитетов, типа _why становится больше, а сами они становятся мельче и заметно быстротечнее. Но если вам, как и мне, стремительное устаревание технологий по душе, то предлагаю помянуть виртуальной минутой молчания ушедших персонажей, — и за работу!

01.06.2016 firtree_right Май

Уже довольно долго тело находится в режиме «плато», про которое говорят в разных текстах про спорт. Изнутри это, конечно, ощущается как вечная врождённая инвалидность. Очевидно, что это естественное положение вещей: тело адаптируется наименьшей кровью ко всему. С минимально возможными затратами, и чтобы минимально измениться. Если я учусь делать что-то одно, то очень медленно прогрессирую, но только в нём: если делать упражнения на равновесие на БОСУ (такая половинка шара большая), то на полу не станет лучше получаться, пока на полу тоже не буду их делать.

♯♯♯

Понимание причин и объяснение ситуаций — одна из базовых моих потребностей. Но опять же для реальных изменений построение моделей и концепций относительно происходящего нужно далеко не всегда. Когда только начинал заниматься файтом, то получать по лицу было очень обидно и вводило в шок. Но достаточно просто регулярно получать, чтобы с этого снялась вся излишняя эмоциональная нагрузка. Никакие телеги и осмысления не нужны. Тем более в телесных вещах. Понимание, что боль — это просто сигнал мозгу о том, что нужно принимать какие-то меры, или понимание того, что от того, как стало больно до того, как появилось какое-то повреждение или реальная опасность для здоровья ещё довольно далеко, — абсолютно не помогают. Помогает практика и опыт.

♯♯♯

Наступает эра некомпетентности. Большие и сложные системы, которые уже никто не понимает, как работают, охватывают совсем все сферы жизни.

Поломал Билайну привязку банковских карточек для своей карточки просто последовательностью своих обращений в сервис. Теперь моя заявка висит уже у их программистов, и когда будет решена — неясно. Количество усилий, которое пришлось затратить, чтобы показать, что ошибка у них, а не у меня — колоссальное. Девушка в сервисе, с которой я решал этот вопрос сначала лично, а потом звонил при ней же в их же тех. поддержку, потом ещё и ещё к концу моего визита начала заикаться. И это мы в итоге совместными усилиями продвинули решние вопроса. Другая девушка, с которой я общался в тех. поддержке была очень настойчива в своём нежелании разбираться. И, скажу вам, это ужасное чувство, когда ты звонишь в последний оплот, чтобы решить свои проблемы, а там люди оказываются менее компетентны и упёрты в этом. Совершенно бессильным себя чувствовал. Пообещал ей, что дойду до офиса и сделаю всё от меня зависящее, чтобы ей организовали ликбез. И записал имя и время. И таки дошёл до офиса, и написал на неё заяву.

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

♯♯♯

На файт к нам пришёл молодой юноша южных кровей. Ещё месяца не проходил рассказывает мне по дороге домой:

— Вот, круто, файт даже прямо придаёт уверенности в себе. Вот мы тут едем с девушкой на моей машине. У меня прав нет (лишили за езду по встречке), — она за рулём. И там есть одно место, там всегда на поворот стоят, а навстречу две полосы, ну и там все по встречке ездят. И мы едем, и мужик начинает возмущаться. Я стекло опустил, — говорит. — и даже не разозлился. Улыбаюсь и говорю ему: «Ну, зачем ты возмущаешься, езжай себе спокойно!»

Я, конечно, сделал паузу. Кто меня знает лично, знает, как я отношусь к нарушениям правил во всех областях. Думаю, как бы ему объяснить, что «поубывав бы» за такое. Говорю:

— Про самооборону скажу так: нужно понимать, что это всё статистика, и никаких гарантий не существует. Есть ситуации заведомо сложные, когда не повезло, а есть на грани. И вот на грани, на мой взгляд, на каждых двух человек, которые бы могли сохранить здоровье в ситуациях, если бы умели самооборону, приходится трое, которые наоборот усугубили и спровоцировали ситуацию, потому что у них что-то там в спортзале получилось, и через это пострадали. Ты готов умереть за своё право ездить по встречке? — спрашиваю.

♯♯♯

Сходили в «Гоголь-центр» на «Кому на Руси жить хорошо». За культуру у нас отвечает Ира. Я согласился при покупке билетов и забыл, куда и на что мы идём до самого дня. В итоге оказалось очень круто! Очень интересные параллели, классные находки, красивая хореография и неисчезающая актуальность. Ну и Некрасовские Демон ярости и Ангел милосердия, дающие Летова в финале — это такая вишенка на торте. Мои рекомендации.

26.05.2016 firtree_right Создание своих правил для udev

Магия

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

Но, чем более профессионально во всё вникаешь, тем больше приходится играть роль того самого «волшебника», ответственного за «магию», а по сути — фокусника. Для меня такое наступает примерно на двадцатый раз однообразных действий, когда уже хочется, чтобы оно само как бы по волшебству сделалось.

Вставил диск

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

Sorting robot

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

udevadm info -a --name=/dev/sdd1
udevadm info --query=env --name=/dev/sdd1

как уже говорил, имея инструкцию, как подготовить диск для копирования, можно всегда иметь одинаковую метку тома. Чем и пользуемся: создаём файлик /etc/udev/rules.d/90-my-storage-copy.rules, куда пишем:

ACTION=="add", ENV{ID_FS_USAGE}=="filesystem", ENV{ID_FS_TYPE}=="ext4", ENV{ID_FS_LABEL_ENC}=="storage-copy", RUN+="/usr/local/bin/storage-copy-mount.sh"
ACTION=="remove", ENV{ID_FS_USAGE}=="filesystem", ENV{ID_FS_TYPE}=="ext4", ENV{ID_FS_LABEL_ENC}=="storage-copy", RUN+="/usr/local/bin/storage-copy-umount.sh"

Всё, что выдавалось нам с параметром --query=env будет в параметрах окружения нашего скрипта /usr/local/bin/storage-copy-mount.sh:

#!/bin/sh
mount_point="/mnt/myrules/$(basename $DEVNAME)"
mkdir -p $mount_point
mount -t $ID_FS_TYPE -o ro $DEVNAME $mount_point

/usr/local/bin/storage-copy-umount.sh:

#!/bin/sh
mount_point="/mnt/myrules/$(basename $DEVNAME)"
umount -l -f $mount_point
rmdir $mount_point

Некоторые гайды не рекомендуют вызывать команду mount из правил udev, но когда это останавливало настоящих волшебников? :)

Массив без массива

Второй случай — половина рэйд-массива. Нужно собирать и разбирать массивы так, чтобы это не пересекалось с работой остальной системы. Некоторые хранилища задают метки тома своим разделам с информацией, а некоторые можно определить только по номеру партиции. /etc/udev/rules.d/90-my-storage-rais.rules:

ACTION=="add", ENV{ID_FS_USAGE}=="raid", ENV{ID_FS_TYPE}=="linux_raid_member", ENV{ID_PART_ENTRY_NUMBER}=="3", ENV{ID_FS_LABEL_ENC}!="system*", RUN+="/usr/local/bin/storage-raid-mount.sh"
ACTION=="add", ENV{ID_FS_USAGE}=="raid", ENV{ID_FS_TYPE}=="linux_raid_member", ENV{ID_FS_LABEL_ENC}=="DiskStation*", RUN+="/usr/local/bin/storage-raid-mount.sh"
ACTION=="remove", ENV{ID_FS_USAGE}=="raid", ENV{ID_FS_TYPE}=="linux_raid_member", ENV{ID_PART_ENTRY_NUMBER}=="3", ENV{ID_FS_LABEL_ENC}!="system*", RUN+="/usr/local/bin/storage-raid-umount.sh"
ACTION=="remove", ENV{ID_FS_USAGE}=="raid", ENV{ID_FS_TYPE}=="linux_raid_member", ENV{ID_FS_LABEL_ENC}=="DiskStation*", RUN+="/usr/local/bin/storage-raid-umount.sh"

То есть, говоря человеческим языком, это или рэйд-партиция с номером 3, название которой не начинается с system или рэйд-партиция с именем, начинающимся с DiskStation. Теперь нам нужно собрать массив так, чтобы у него было уникальное имя, но при этом однозначно связанное с устройством, чтобы не плодить лишних сущностей. Для этого я решил точку для монтирования называть так же как имя устройства в /dev, а номер рейда брать из кода последней буквы. /usr/local/bin/storage-raid-mount.sh:

#!/bin/sh

mount_point="/mnt/myrules/$(basename $DEVNAME)"
num=$(printf %d "'$(echo $DEVNAME | head -c8 | tail -c1)")
raid_device="/dev/md$num"

mkdir -p $mount_point
mdadm -S $raid_device
mdadm -A -R $raid_device $DEVNAME
mount -o ro $raid_device $mount_point

Тут происходит магия баша. Я писал уже, что всегда испытываю большое удовольствие, когда что-то удаётся сделать на этом скриптовом языке:

echo $DEVNAME | head -c8 | tail -c1

Выдаёт нам восьмую букву имени устройства, то есть «d» для «/dev/sdd3», например, и «f» для «/dev/sdf5».

printf %d "'d"

Выдаёт нам 100, а в случае с «f» — 102. И мы получаем имя «/dev/md100», под которым насильно поднимаем raid1 на одном диске из двух. И обратно то же самое. /usr/local/bin/storage-raid-umount.sh

#!/bin/sh

mount_point="/mnt/myrules/$(basename $DEVNAME)"
num=$(printf %d "'$(echo $DEVNAME | head -c8 | tail -c1)")
raid_device="/dev/md$num"

umount -l -f $mount_point
mdadm -S $raid_device
rmdir $mount_point

Понятно, что с вытаскиванием сложнее, даже если монтировать, как это делаю я, только для чтения. Это всё актуально, если после работы с диском прошло значительное время. И я предпочитаю хотя бы размонтировать вручную. Но при этом совершенно прекрасно то, что все наши устройства будут создавать папки и появляться в /mnt/myrules, как флэшки появляются в /media на десктопных версиях Убунту.

Для самостоятельного изучения

  1. man udev

11.05.2016 firtree_right Как показать Яндекс Панорамы где угодно

История

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

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

<script src="//panoramas.api-maps....."></script>

Но никакого способа управлять, кроме как переходить по стрелкам, в нём не предусмотрено. Мне стало интересно, можно ли что-то с этим сделать. И поэтому сегодня, дорогой читатель, мы поиграем в хакеров. Вредить мы никому не будем: хакеры вредители — это только часть хакеров. Иначе говоря, приспособим под наши запросы то, что изначально под них не было предназначено.

Hack

Параметры

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

ll=37.61782676%2C55.75074572

Это же долгота и широта (в таком порядке) через запятую! Второе, с чем сразу же хочется повозиться — это параметр:

size=690%2C495

Это ширина и высота окна через запятую. Если посмотреть на то, как работает скрипт, и что он оставляет после себя на странице, то совсем не обязательно лезть и деобфусцировать код. Всё понятно: скрипт создаёт вместо себя тег iframe и другой тег script, а себя удаляет. Параметр size и задаёт размеры айфрейма.

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

var latlng = L.latLng(55.75074572, 37.61782676);
var $map = L.map('map', {center: latlng, zoom: 15});
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png').addTo($map);

function openPanoramaAt(latlng) {
    var panoDiv = $('#panorama');
    var panoScript = document.createElement('script');
    panoScript.type = 'text/javascript';
    panoScript.src = 'https://panoramas.api-maps.yandex.ru/embed/1.x/?lang=ru&ll=' + latlng.lng + '%2C' + latlng.lat + '&ost=dir%3A0.0%2C0.0~span%3A130%2C70.26418362927674&size=' + panoDiv.width() + '%2C' + panoDiv.height() + '&l=stv';
    panoDiv.empty();
    panoDiv[0].appendChild(panoScript);
}

openPanoramaAt(latlng);

$map.on('click', function (me) {
    $map.panTo(me.latlng);
    openPanoramaAt(me.latlng);
});

Кроме лифлета я, конечно, люблю джейквери. Но, к сожалению, на нём невозможно вставить тег script, чтобы он заработал. Поэтому тут немного намешано.

Но нам нужно идти дальше. Ведь внутри панорам можно переходить по стрелкам, а у нас это никак не отображается на карте. Что же делать?

Человек посередине

Чтобы определить, что делает скрипт, у нас и так уже открыт инспектор страницы. Теперь, дорогой читатель, давай переключимся во вкладку «сеть».

Переходя по стрелкам мы увидим, что, кроме всего прочего, плеер запрашивает файлик по адресу _https://panoramas.api-maps.yandex.ru/panorama/1.x/?l=stv&lang=ru_RU&...&format=json_. Тут уже нет широты и долготы, а присутствует идентификатор, но если открыть этот джейсон в новой вкладке, то внутри него мы увидим нужные нам координаты:

JSON.parse(response).data.Data.Point.coordinates

То, что браузер видит это запрос, означает, что скорее всего в основе лежит XMLHttpRequest. Так как заголовка Content-Security-Policy не видно (по правде сказать, настраивать его довольно сложно, и обычно если кто и прописывает такой заголовок, то там среди прочего есть unsafe-inline), то мы попробуем подслушать, о чём говорит плеер с сервером.

Можно не изобретать велосипед и просто поискать, как это делают уже до нас. В нашем случае плеер находится внутри генерящегося на лету айфрейма и именно класс внутри этого айфрейма нам и надо подменить. Для этого используем модный нынче MutationObserver. Я собрал работающий пример на jsfiddle, и предлагаю его вниманию дорогих читателей.

Сразу видно несколько недостатков:

  1. Размеры панорамы не адаптируются при изменении размеров окна.
  2. Невозможно отследить направление взгляда, чтобы показать его на карте. Я рылся в объектах, но ничего не нашёл.
  3. Пришла беда, откуда не ждали: случился прогресс!

Правильный API

Тема этого топика была запланирована у меня некоторое время назад. И основные фишки были опробованы и сделаны тоже некоторое время назад. Но когда я сел писать конкретный код, то обнаружил, что в стандартной поставке API карт уже есть панорамы. Произошло это в прошлой версии 2.1.38 от 31 марта 2016. Сейчас я работал с 2.1.39. Всего 42 дня как можно ставить панорамы на карты!

Конечно же, я собрал такой же пример на API Яндекс Карт. (Всё-таки синтаксис лифлета намного изящнее, извините). Это настолько новое явление, что даже не входит в стандартный полный набор модулей. Заметьте во внешних ресурсах слева я написал для загрузки (иначе не работает):

load=package.full,panorama.isSupported,panorama.locate,panorama.createPlayer,panorama.Player

Недостатки «хакерского» метода отсутствуют. Плеер следует за размерами элемента. Для изменения параметров просмотра есть специальные события, поэтому можно рисовать на карте направление взгляда. В моём примере это не сделано, конечно.

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

Отдельно ссылки:

  1. Как приспособить код для вставки Яндекс Панорам на свои карты (jsfiddle);
  2. Как показать панорамы с помощью API Яндекс Карт (jsfiddle);
  3. Intercept all ajax calls (stackoverflow);
  4. API Яндекс Карт;
  5. Лучший API для картографических сервисов Leaflet.

02.05.2016 firtree_right Апрель

Совершенно растерял все навыки выступления на публике. В этом месяце случилось два выступления на конференциях. Хорошо, что я готовился и репетировал. За, кажется, 8 или больше лет, что я не говорил на более трёх человек, почти утерян навык вести нить повествования. Хорошо бы ещё попрактиковаться. Больше всего практики было ещё в школе, когда я попал в околопедагогическую тусовку. Похожую тусовку мы встретили, когда отдыхали в прошлом году в «Веретьево» (вот, кстати, что из них вышло).

♯♯♯

У меня всегда очень плохо с границами. Не с теми, которые мы проводим при рассмотрении окружающего мира, чтобы определить, что одинаковое, а что разное, а с границами людей. Например, я не знаю, к кому можно зайти в гости, а к кому нет. Или с кем нужно обниматься, целоваться, жать руку при прощании, а с кем нет. Это как слепое пятно. Поэтому вокруг меня в основном либо люди, которые довольно сильно позволяют в их границы вторгаться, либо с которыми границы всегда неприкосновенны. Также слабо я чувствую и свои границы: либо отторгая чрезмерно, либо себе во вред. Либо ещё можно откровенно и чётко договориться на берегу про всё, но это далеко не со всеми. В этом смысле спорт, и файт в частности — это как постоянное упирание носом в свои границы. Осязаемые пределы возможностей, с которыми очень трудно встречаться.

♯♯♯

Одно из дел, которое мы сделали в поездке в Швейцарию — отправили свою слюну в 23andme. Будем надеяться, что ДНК не врёт, если не врёт и не ошибается лаборант.

♯♯♯

Возраст учит относиться ко всему легче. Можно сколько угодно осуждать разные качества и особенности, типа праздности, разгильдяйства или поверхностности, но достаточно увидеть один пример успешного сочетания оных, чтобы усомниться в их опасности. Такое дружественное: «если такой эффективный, то почему такой напряжённый»?

♯♯♯

Недавно писал в твиттер, что жду, когда искусственный интеллект будет собирать деньги на кикстартере. А тут меня поразила догадка. Я уже давно зарегистрировался на проекте CodingGame. Это такие игрушки для программистов. Решение головоломок с помощью программирования. Периодически там проходят соревнования по написанию микро-ИИ для решения игровых задач. То есть запрограммированный ИИ должен играть в игру по правилам и победить другие ИИ. И тут я подумал. А что, если весь этот проект создал искусственный интеллект и через геймификацию (да, там есть ачивки и бейджики) и соревнования заставляет людей программировать себе армию искусственных интеллектов, которые, между прочем, выигрывают сражения, хоть и симулированные!?

27.04.2016 firtree_right Стрелки для krpano на несуществующем языке

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

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

Стрелки

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

Mayalanguage

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

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

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

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

Сжатие

Для тестирования я решил использовать микро-сервер node.js, а вместе с ним инструмент для исполнения задач gulp. Одна такая задача — убирать лишние пробелы в придуманном скриптовом языке krpano. Не столько для обфускации или экономии трафика, сколько опять же для интереса.

Для сжатия xml я нашёл pretty-data, а недостающий кусок дописал:

var gulp = require('gulp'),
    prettyData = require('gulp-pretty-data');

// Minify krpano action
function minifyAction() {
    function dry (file, cb) {
        file.contents = new Buffer(String(file.contents).replace(/(<action.+?>)([\s\S]+?)(<\/action>)/ig, function (str, opentag, cnt, closetag) {
            return opentag + cnt.replace(/\s*(^|[;,=!])\s*/g, "$1") + closetag;
        }));
        cb(null, file);
    }

    return require('event-stream').map(dry);
}

// Minify plugin xml
gulp.task('xml', function () {
    return gulp.src('dev/arrows.xml')
        .pipe(prettyData({type: 'minify'}))
        .pipe(minifyAction())
        .pipe(gulp.dest('arrows/example/'));
});

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

Ссылки

  1. Виртуальный тур по Арктике с моими стрелками.
  2. Репозиторий krpano-arrows на гитхабе.
  3. Страница плагина на krpano.com.

25.04.2016 firtree_right Швейцария апрель 2016

Чтобы не затягивать.

Детали

В Швейцарии начали менять банкноты. Текущая серия самых красивых банкнот, что я когда-либо видел, была выпущена аж в 1995 году. С 2016 года Национальный банк Швейцарии решил выпускать новую серию. Больше степеней защиты, меньше размер, чтобы влазить в большее количество кошельков. Начали плавно, с одной банкноты. В городах висят плакаты. Продавцы в магазинах бурчат: «Что за фантики выпустили?» Ребята, но ведь и до этого ваши самые красивые банкноты все эти 20 лет все остальные, кроме вас, считали фантиками!

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

По-прежнему не отпускает «лебедевское краеведение», даже после двухлетнего перерыва от поездок за границу. В этот раз заметил, что в Цюрихе говноскрёбы для обуви на домах обращены к стене дома, чтобы упираться руками в стену, когда отскребаешь подошвы, а в Люцерне — перпендикулярно стене, чтобы чистить обувь, прислонившись к стене плечом. Но фоток не будет, как и обувь о них уже никто давно не чистит.

Уже писал, что швейцарцы изобрели шрифт гельветику и вёрстку по сетке. Немецкоговорящая Швейцария — это место, где порядок управляет человеком, а не наоборот. Вот, например, в «коммиссионку» зашли. Стоечка с б/ушными горшками:

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

Ещё про стены: раз, два, три.

В Ботаническом саду Цюрихского университета видели цветок, который пахнет тухлой рыбой. Я знал о его существовании, но реальность запаха так меня поразила, что я его для вас сфоткал:

Из ботсада ещё: раз и два.

Музеи

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

Музеи науки, конечно, — мои любимые. Посетили огромнейший музей «Технорама». Это, скажу я вам, в разы круче всех парков развлечений. Снаружи. Внутри.

Также мы ездили в Люцерну. Там проходил юбилейный 25-й фестиваль комиксов Фуметто. Мне нравятся комиксисты. У них чаще порядок с сюжетом и персонажами, чем у прочих иллюстраторов и художников. Я человек простой и не готов сильно напрягать воображение, чтобы получить удовольствие от искусства.

Ира с ребятами участвовали и сделали комикс «PersonaЖ». Который выставлялся в отеле, в здании которого раньше была тюрьма. Он так и называется The Jailhotel.

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

Заглавные выставки были отличные. Совершенно безумный картон от психопата Сейко:

И в динамике: раз и два.

Самое, на мой взгляд, крутое размещение выставки в городе — это «Неизвестная история роботов», которую разместили в Историческом музее. Том Голд, я уверен, изучил музей и вписался туда просто суперски. Среди настоящих исторических экспонатов его экспонаты была помечены специальным знаком. Интересно их находить и заодно можно к истории приобщиться.

Да и сама история роботов по тематике ложится в концепцию музея.

Литературный клуб

Пользуясь случаем посетили «Литературный клуб в Цюрихе», который делает наша очень хорошая подруга. Вот уже два года он проходит в помещении с некоторым количеством сохранившихся старых фресок.

Особо отмечу две вещи.

  1. Это очень круто, когда то, что человек делает для удовольствия, вырастает во что-то заметное и оформившееся. И можно позвонить и не просто какая-то Саша приглашает известного режиссёра или писателя, а предложение к нему поступает принять участие в Литературном клубе. Том самом!
  2. Эта встреча была посвящена Достоевскому, который в Швейцарии жил, и многие герои романов которого как-то со Швейцарией пересекались. Я не очень любил читать в школе, да и сейчас почти не читаю не по работе. Но обсуждение тонкостей творчества и биографии Достоевского людьми, которые, во-первых, эмоционально вовлечены, а во-вторых, находятся в общем подробном контексте, вызвало у меня желание восполнить пробелы конкретно в Достоевском. Очень вдохновляюще.