программирование

Удаление умершей ноды в кластере «Оракла»

Небольшой видеоурок по удалению умершей ноды из кластера «Оракла». В одном месте там требуется, чтобы на машине был своп не менее 500МБ, у нас не было (на машине 96ГБ памяти, зачем ей своп), мы его просто создали на диске в файле.

2 комментария

Как поставить Кобол на Мак

Пусть здесь полежит, чтобы я потом не искал.

Как поставить Кобол на Мак (при помощи «брю»):

curl http://www.sim-basis.de/open-cobol-1.1.tar.gz
tar xvfz open-cobol-1.1.tar.gz
cd open-cobol-1.1
brew install gmp
brew install berkeley-db

./configure --prefix=/usr/local/Cellar/open-cobol/1.1
make && make install
brew link open-cobol

9 комментариев

Будущее формата CSS image

Лого в CSS (38.79КиБ)

Мои эксперименты с картинками на чистом CSS внезапно дали следующий результат. Я взял для экспериментов логотип студии Лебедева в первом попавшемся цвете и перегнал его в CSS image при помощи своего конвертора, используя все префиксы браузеров: «webkit», «moz», «o» и «ms» («Конкерор» включать не стал, доля этого браузера исчезающе мала).

Оказалось, что если сжать получившийся файл (браузеры же понимают сжатый CSS), то получившийся файл будет размером меньше, чем та же картинка в формате PNG: 2,5КБ против 6,7. Файл с лого Студии я пропускал через онлайновый оптимизатор PNG (punypng), так что он вряд ли неоптимален.

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

Добавлено позднее: в комментариях картинку «утоптали» в 448 байт. Непонятно почему у меня такой плохой PNG получился.

23 комментария

Картинка на чистом CSS-2

Ещё картинка на чистом CSS (82.31КиБ)

Я тут всё это время думал как дёшево улучшить свой конвертор картинок в чистый CSS и придумал.

Выкинул «radial-gradient» и стал использовать «linear-gradient». Картинка, которая в прошлой версии занимала 164КБ, сейчас занимает 89 79. Это уже неплохо, в сжатом (gzip) виде этот код занимает 19КБ, что всего в два с половиной раза больше, чем исходное изображение в PNG. Результат можно сильно улучшить, если отказаться от повтора цвета, но изображения с мелкими деталями (с текстом, например) пострадают.

Кстати говоря, логотип студии Лебедева при конвертации из PNG (6,7КБ) уменьшился до 1,6КБ 1,3КБ. Это уже реальная экономия, если бы не префиксы браузеров.

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

Кстати этот раз «Опера», увы, не смогла показать картинку, так как, похоже, не поддерживает позиционирование для линейных градиентов.

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

Ключи запуска те же.

<?
// ffccbb → fcb
// ff0000 → red
// abcdef → abcdef
function Bo_shortcolor($color) {
    static $shortnames = array(
        "c0c0c0" => "silver",
        "808080" => "gray",
        "800000" => "maroon",
        "ff0000" => "red",
        "800080" => "purple",
        "008000" => "green",
        "808000" => "olive",
        "000080" => "navy",
        "008080" => "teal",
        "f0ffff" => "azure",
        "f5f5dc" => "beige",
        "ffe4c4" => "bisque",
        "a52a2a" => "brown",
        "ff7f50" => "coral",
        "ffd700" => "gold",
        "808080" => "gray",
        "008000" => "green",
        "808080" => "grey",
        "4b0082" => "indigo",
        "fffff0" => "ivory",
        "f0e68c" => "khaki",
        "faf0e6" => "linen",
        "800000" => "maroon",
        "000080" => "navy",
        "808000" => "olive",
        "ffa500" => "orange",
        "da70d6" => "orchid",
        "cd853f" => "peru",
        "ffc0cb" => "pink",
        "dda0dd" => "plum",
        "800080" => "purple",
        "ff0000" => "red",
        "fa8072" => "salmon",
        "a0522d" => "sienna",
        "c0c0c0" => "silver",
        "fffafa" => "snow",
        "d2b48c" => "tan",
        "008080" => "teal",
        "ff6347" => "tomato",
        "ee82ee" => "violet",
        "f5deb3" => "wheat",
    );

    $c = strtolower($color);
    if (isset($shortnames[$c])) {
        return $shortnames[$c];
    }

    if ($c[0] == $c[1] && $c[2] == $c[3] && $c[4] == $c[5]) {
        return '#' . $c[1] . $c[3] . $c[5];
    }

    return '#' . $c;
}

class Bo {
    public function __get($num) {
        return $num ? $num . 'px' : 0;
    }
}

function Bo_convert_helper($im, $lim1, $lim2, $getpix, $dotmask, $z) {
    $end  = $lim1 - 1;
    $dots = array();

    for ($o1 = 0; $o1<$lim2; $o1++) {
        $coll = array();
        $prev    = null;
        $start   = 0;

        for ($o2 = 0; $o2<$lim1; $o2++) {
            $pixel = $getpix($im, $o2, $o1);

            if ($prev !== null && $prev !== $pixel || $o2 == $end) {
                if ($prev !== null) {
                    $color = Bo_shortcolor(vsprintf('%02x%02x%02x', $prev));

                    $coll[] = "$color {$z->$start},$color {$z->$o2}";
                }

                if ($o2 == $end && $prev != $pixel) {
                    $color = Bo_shortcolor(vsprintf('%02x%02x%02x', $pixel));
                    $cell[] = "$color {$z->$o2}";
                }

                $start = $o2;
            }

            $prev = $pixel;
        }

        $dots[] = sprintf($dotmask, implode(',', $coll), $z->$o1);
    }

    return $dots;
}


function Bo_convert($filename, $prefix = '') {
    $z = new Bo();

    if (!file_exists($filename) && !is_readable($filename)) {
        throw new Exception('File not found.');
    }

    $types = array(
        IMAGETYPE_JPG => 'jpeg',
        IMAGETYPE_PNG => 'png',
        IMAGETYPE_GIF => 'gif',
    );

    $imagedata = @getimagesize($filename);
    if (!is_array($imagedata) || !isset($imagedata[2]) || !isset($types[$imagedata[2]])) {
        throw new Exception('Unknown image format');
    }

    $im = call_user_func("imagecreatefrom" . $types[$imagedata[2]], $filename);
    $sw = $w = imagesx($im);
    $sh = $h = imagesy($im);

    $hfunc = create_function('$im, $x, $y', 'return imagecolorsforindex($im, imagecolorat($im, $x, $y));');
    $hmask = "{$prefix}linear-gradient(0,%s) 0 %s";

    $hdots = join(',', Bo_convert_helper($im, $w, $h, $hfunc, $hmask, $z));

    $vfunc = create_function('$im, $y, $x', 'return imagecolorsforindex($im, imagecolorat($im, $x, $y));');
    $vmask = "{$prefix}linear-gradient(90,%s) %s 0";

    $vdots = join(',', Bo_convert_helper($im, $h, $w, $vfunc, $vmask, $z));

    if (strlen($hdots) > strlen($vdots)) {
        $dots = $vdots;
        $sw = 1;
    } else {
        $dots = $hdots;
        $sh = 1;
    }

    return <<<HTML
<head>
<title>$filename converted in background by Evgeny Stepanischev //bolknote.ru</title>
<style text="text/css">
    div {
        background: $dots;
        background-repeat: no-repeat;
        {$prefix}background-size: {$sw}px {$sh}px;
        background-size: {$sw}px {$sh}px;
        width: {$w}px; height: {$h}px;
    }
</style>
<body>
<div></div>
</body>
HTML;
}

if ($_SERVER['argc'] < 2) {
    echo <<<HELP
Image to background CSS convertor v2 by Evgeny Stepanischev. //bolknote.ru Nov 2011

Usage: {$_SERVER['argv'][0]} filename [prefix]

filename  - image file name (PNG, JPEG or GIF)
prefix    - your browser name (Opera, IE, FF, Safari, Chrome)

HELP;
exit;
}

$browsers = array(
    'ie'     => '-ms-',
    'opera'  => '-o-',
    'safari' => '-webkit-',
    'chrome' => '-webkit-',
    'chromium' => '-webkit-',
    'ff' => '-moz-',
    'firefox' => '-moz-',
);

if (isset($_SERVER['argv'][2])) {
    $type = strtolower($_SERVER['argv'][2]);
    $prefix = isset($browsers[$type]) ? $browsers[$type] : '-' . $type . '-';
} else {
    $prefix = '';
}

try {
    echo Bo_convert($_SERVER['argv'][1], $prefix);
} catch (Exception $e) {
    echo "Error: ", $e->getMessage(), "\n";
    exit(1);
};

Оставить комментарий

Активность аудитории

Немного статистики.

В среднем на сайта в месяц бывает примерно 90 тысяч человек. Каждый месяц это число может быть меньше или больше. Например, в июне было 108 тысяч, а в августе — 77. Я недавно перелил большинство юзерпиков пользователей на «Яндекс.Фотки» и, делая скидку на людей с одинаковыми юзерпиками и именами (у меня для двух одинаковых имён сгенерируется один юзерпик), активная аудитория сайта порядка 1700 человек.

То есть более чем за 10 лет существования сайта (правда комментарии на сайте появились сильно позже) его посетила толпень народу, но комментрировали всего несколько тысяч. Похожая ситуация, судя по всему, у Экслера на его персональном блоге, в день его сайт посещает столько же народу, как меня за месяц, а комментариев в блоге — только несколько десятков к каждой записи.

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

Причин низкой активности я вижу две: всё-таки мне не удалось научить читателей подписываться на комментарии через RSS (и надо думать о том, чтобы как-то корректно высылать их на почту), вторая — отсутствие древовидных комментариев (но их-то я вводить не хочу, они провоцируют уход от темы и читать их все у меня не получится и не интересно, тогда вопрос — зачем стимулировать дискуссии, в которых мне не хочется участвовать).

Надо думать в сторону почты. Проблема с почтой, что её надо как-то подтверждать. Устроить какому-нибудь ящику «сладкую жизнь» очень просто (у меня даже была программа «Shit Streamer» на Перле и ПХП для этого) — достаточно подписать ящик на комментарии на куче ресурсов, ведь сейчас мало кто озабочен подтверждением подлинности ящика.

Увы и подтверждение подлинности через письмо не очень-то хорошая идея — кого угодно можно засыпать по брови письмами с подтверждениями.

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

Что-то типа: mailto:bolknote-confirmation+dy43udsj36HdshddkjdDds@gmail.com?subject=http://someopenid.example.org

Пользователь жмёт на ссылку, отсылает пустое письмо на этот адрес, с этого момента считается, что он подтвердил свой адрес. «Гмэйл» устроен таким образом, что на часть после плюса он внимания не обращает и эти письма будут попадать на ящик «bolknote-confirmation». Мой скрипт время от времени будет заходить туда, прямо по протоколу POP3, это текстовый простой протокол, мне надо только научиться работать с ним через TSL, получать список писем, их заголовки, подверждать адреса и удалять обработанное.

Не слишком неудобно получается?

64 комментария

Хочу поменять библиотеку для OpenID на сайте

Хочется поменять библиотеку, которую я использую для OpenID на сайте. Я сейчас использую Janrain OpenID, но она давно не обновлялась, генерирует варнинг и только с костылями и патчами работает с реализацией OpenID в ЖЖ (баг-репорт я писал в обе компании, бестолку).

Посоветуйте какие-нибуль альтернативы (на PHP). Я рассматриваю сейчас LightOpenID.

Требования — библиотека должна поддерживать OpenID 1.x и 2.x, SREG, предоставлять возможность хранить данные где я укажу (мне нужно на диске, а не в БД) и не должна использовать экзотические модули.

6 комментариев

Юзерпики на «Яндекс.Фотках»

Вчера в Татарстане был выходной, поэтому пока мы ехали на машине за город, я сделал заготовку для хранения юзерпиков на «Яндекс.Фотках», а вечером её доделал. Сейчас все юзерпики в фоне переливаются туда в порядке запроса пользователем в закрытый альбом специального пользователя, которого я завёл.

До этого юзерпики по сложным правилам (с использование адреса почты из SRE OpenID или vCard, favatar, gravatar, pavatar, юзерпиков из «Ярушки» и ЖЖ и т. д.) получались для каждого человека в комментариях и загружались каждый раз с совершенно разношёрстных ресурсов.

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

Есть и четвёртая причина, не знаю насколько она значимая. Некоторые юзерпики (побайтно) повторяются, но так как браузер не знает, что с двух разных УРЛов грузится та же картинка, он грузит её два раза. У меня сейчас не так — я хеширую картинку и по хешу ищу совпадение. Есть вероятность совпадения хеша, но юзерпики не слишком-то важные данные, чтобы бояться коллизий.

Сейчас юзерпики грузятся только в фоне, то есть какое-то время он грузится с «настоящего» адреса и только через какое-то время — с «Яндекс.Фоток». Так как запросов в стеке пока очень много (сейчас загружено 779 картинок и их число растёт очень быстро), процесс перекладывания может растянуться на часы. В будущем, когда бо́льшая часть будет уже лежать на «Фотках», этот процесс сократится до минут.

17 комментариев

У̶д̶о̶б̶н̶о̶е̶ новое цитирование в блоге

Долго думал как упростить цитирование у себя в блоге, наконец придумал. Ещё остались кое-какие шероховатости, но в целом должно стать лучше.

Сделал две вещи.

Во-первых, когда вы вставляете какой-то кусок текста в поле для комментариев, движок пытается догадаться не вставляете ли вы цитату и, если вставляете, расставляет значки цитирования („>“) перед каждой строкой.

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

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

Сохранённые звёздочки

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

13 комментариев

Цвета из CSS, названия которых короче их номеров

Кстати, если вдруг кому-нибудь для оптимизации CSS нужна таблица цветов, названия которых короче их номеров, то я вчера для конвертора сделал скрипт, который все такие цвета нашёл. Наверняка её кто-то составлял и раньше, но мне было быстрее программу написать. Список в виде массива есть в конверторе, а вот список их названий строкой: navy, green, teal, indigo, maroon, purple, olive, gray, grey, sienna, brown, silver, peru, tan, orchid, plum, violet, khaki, azure, wheat, beige, salmon, linen, red, tomato, coral, orange, pink, gold, bisque, snow, ivory.

Например, «silver» короче своего числового эквивалента «#c0c0c0» на один символ, а «tan» короче «#d2b48c» на четыре.

8 комментариев

Картинка на чистом CSS

Картинка на чистом CSS (130.69КиБ)

Сейчас CSS позволяет делать всё более интересные вещи. Раз уж сегодня короткий день, за 20 минут из «сэкономленного» часа накидал программу, которая конвертирует любую картинку (в форматах PNG, JPEG или GIF) в чистый CSS (без использования dataURI). Каждая точка кодируется через одну конструкцию radial-gradient. Очень расточительно, сделал для иллюстрации принципа.

Если заморачиваться серьёзно, то можно выявлять в картинке области с градиентом (так делается в формате PNG для упаковки) и покрывать их целиком одной конструкцией, а так же оптимизировать наложением линейных градиентных областей с прозрачностью (через rgba или hsla). Достаточно интересная программисткая задача, кстати.

Самую очевидную оптимизацию я сделал в пробке по работе домой — выделил самый частый цвет и пустил его на фон, выиграл примерно 11,5КБ на картинке в примере.

За 20 минут я только успел сделать небольшой конвертор (результат работы можно посмотреть в разделе «Храню», только для «Хрома»). Так как синтаксис завязан на префиксы, то я в генераторе сделал указание браузера под который нужно сгенерировать код (поддерживаются: IE, Opera, Firefox и Chrome). Всё это весьма расточительно, картинка 75×75 занимает 164КБ.

У меня заработало для «Хрома» и «Файэрфокса», под «Оперой» не завелось, лень проверять что она не поддерживает, IE попробовать не могу — его не бывает на «Маках».

Код конвертора

// Конвертор картинки в чистый CSS, / ноябрь 2011 

<?
// ffccbb → fcb
// ff0000 → red
// abcdef → abcdef
function Bo_shortcolor($color) {
    static $shortnames = array(
        "#000080" => "navy",
        "#008000" => "green",
        "#008080" => "teal",
        "#4b0082" => "indigo",
        "#800000" => "maroon",
        "#800080" => "purple",
        "#808000" => "olive",
        //"#808080" => "gray",
        "#808080" => "grey",
        "#a0522d" => "sienna",
        "#a52a2a" => "brown",
        "#c0c0c0" => "silver",
        "#cd853f" => "peru",
        "#d2b48c" => "tan",
        "#da70d6" => "orchid",
        "#dda0dd" => "plum",
        "#ee82ee" => "violet",
        "#f0e68c" => "khaki",
        "#f0ffff" => "azure",
        "#f5deb3" => "wheat",
        "#f5f5dc" => "beige",
        "#fa8072" => "salmon",
        "#faf0e6" => "linen",
        "#ff0000" => "red",
        "#ff6347" => "tomato",
        "#ff7f50" => "coral",
        "#ffa500" => "orange",
        "#ffc0cb" => "pink",
        "#ffd700" => "gold",
        "#ffe4c4" => "bisque",
        "#fffafa" => "snow",
        "#fffff0" => "ivory",
    );

    $c = strtolower($color);
    if (isset($shortnames[$c])) {
        return $shortnames[$c];
    }

    if ($c[0] == $c[1] && $c[2] == $c[3] && $c[4] == $c[5]) {
        return '#' . $c[1] . $c[3] . $c[5];
    }

    return '#' . $c;
}

function Bo_convert($filename, $prefix = '') {
    if (!file_exists($filename) && !is_readable($filename)) {
        throw new Exception('File not found.');
    }

    $types = array(
        IMAGETYPE_JPG => 'jpeg',
        IMAGETYPE_PNG => 'png',
        IMAGETYPE_GIF => 'gif',
    );

    $imagedata = @getimagesize($filename);
    if (!is_array($imagedata) || !isset($imagedata[2]) || !isset($types[$imagedata[2]])) {
        throw new Exception('Unknown image format');
    }

    $im = call_user_func("imagecreatefrom" . $types[$imagedata[2]], $filename);
    $w  = imagesx($im);
    $h  = imagesy($im);

    // Самый частый цвет идёт на background-color
    $colorfreq = array();
    for ($x = 0; $x<$w; $x++) for ($y = 0; $y<$h; $y++) @$colorfreq[imagecolorat($im, $x, $y)]++;

    $bg = array_search(max($colorfreq), $colorfreq);

    for ($x = 0; $x<$w; $x++)
    for ($y = 0; $y<$h; $y++) {
        $pixel = imagecolorat($im, $x, $y);
        if ($pixel != $bg) {
            $color = Bo_shortcolor(sprintf('%06x', $pixel));

            $dots[] = "{$prefix}radial-gradient($color,$color) {$x}px {$y}px";
        }
    }

    $dots = join(',', $dots);
    $bg   = Bo_shortcolor(sprintf('%06x', $bg));

    return <<<HTML
<head>
<title>$filename converted in background by Evgeny Stepanischev //bolknote.ru</title>
<style text="text/css">
    div {
        background: $dots;
        background-repeat: no-repeat;
        {$prefix}background-size: 1px 1px;
        background-color: $bg;
        background-size: 1px 1px;
        width: {$w}; height: {$h};
    }
</style>
<body>
<div></div>
</body>
HTML;
}

if ($_SERVER['argc'] < 2) {
    echo <<<HELP
Image to background CSS convertor by Evgeny Stepanischev. //bolknote.ru Nov 2011

Usage: {$_SERVER['argv'][0]} filename [prefix]

filename  - image file name (PNG, JPEG or GIF)
prefix    - your browser name (Opera, IE, FF, Safari, Chrome)
HELP;
exit;
}

$browsers = array(
    'ie'     => '-ms-',
    'opera'  => '-o-',
    'safari' => '-webkit-',
    'chrome' => '-webkit-',
    'chromium' => '-webkit-',
    'ff' => '-moz-',
    'firefox' => '-moz-',
);

if (isset($_SERVER['argv'][2])) {
    $type = strtolower($_SERVER['argv'][2]);
    $prefix = isset($browsers[$type]) ? $browsers[$type] : '-' . $type . '-';
} else {
    $prefix = '';
}

try {
    echo Bo_convert($_SERVER['argv'][1], $prefix);
} catch (Exception $e) {
    echo "Error: ", $e->getMessage(), "\n";
};

9 комментариев

Крестики-нолики на HTML CSS и прокрастинация

Крестики-нолики (38.95КиБ)

Есть такое понятие — прокрастинация, это про «никогда не откладывай на завтра то, что можно сделать послезавтра». В тяжёлых проектах есть соблазн отложить принятие трудных решений «на потом» или не делать что-то до последнего момента. Я человек и поэтому подвержен этой проблеме.

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

У меня с моими «Крестиками-ноликами» на HTML и CSS такое произошло — не хотелось заканчивать, искать ошибки и так далее. Вроде как проект-то уже сделан, проехали, кому-он-нужен-зачем-баги-править. Но какая-то неудовлетволённость осталась, хотелось доделать. В итоге я около недели по полчаса тщательно искал ошибку и вносил улучшения.

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

Багов быть не должно, могут быть шероховатости. Я сам знаю пару неаккуратных вещей — кнопка «Restart» работает не во всех браузерах и на ничьей следующий ход уже не сделать (хотя он и не нужен вообще-то).

11 комментариев

Перехода на зимнее время не будет

Все помнят, что перехода на зимнее время в ночь с этой субботы на воскресенье не будет? Переход отменён Федеральным законом Российской Федерации от 3 июня 2011 г. N 107-ФЗ «Об исчислении времени».

В этой связи нужно аккуратно проверить, что все ваши сервера, устройства и так далее, не перейдут на это время и не создадут путаницу. Если вы регулярно обновляете систему, то в большой вероятностью вам ничего не грозит, на «Хабре» есть довольно развёрнутая статья о том до какой версии нужно обновлять различные системы или какие обновления ставить (например, для всех актуальных версий «Виндоус», включая ЭксПи, существует обновление №2570791, проверить его установку можно, например, через «ПауэрШел», для «Мак ОС Икс» требуется обновиться до 10.7.2).

Всё плохо у «Андроидов», в большинстве устройств переход состоится даже в последних прошивках (2.3.6, 3.2.1, CyanogenMod-7.1), придётся исправлять это вручную. Например, мой «Нексус Уан» к переходу не готов, несмотря на «Андроид» 2.3.6, сам проверил. Я поэкспериментировал и переставил телефон на часовой пояс «Дубай, GMT+04:00», должно помочь.

Так же пострадают джейлбрейкнутые устройства на «айОС», версия 4 не имеет необходимого обновления (а пятая пока не поддаётся джейбрейку), но есть способ обновить зоны вручную, ну или выставить всё тот же «Дубай».

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

Кстати, некоторые программы и языки программирования имеют собственные базы временны́х зон. Например, серверу почты «Эксченч» требуется отдельное обновление, а интерпретатору ПХП лучше выставить настройку зоны на что-нибудь нейтральное без сезонных поясов (я выставил те же «Asia/Dubai»), а ещё лучше, если есть возможность — обновить пакет временны́х зон.

82 комментария

«Крестики-нолики» на HTML и CSS

Давно хотел попробовать сделать «крестики-нолики» только на HTML и CSS, без использования скриптов, вот — сделал.

HTML+CSS only tic-tac-toe (24.29КиБ)

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

Кстати, несмотря на всю винтажность, от браузера требуется поддержка CSS3, включая трансформации.

Я сильно не тестировал, спать уже надо ложиться. Если будут ошибки, пишите, постараюсь поправить, если только они не относятся к недостаткам алгоритма Остермиллера. Всё-таки это скорее концепт, чем полноценная игра.

20 комментариев

ЭЦП

На конференции «Осенний документооброт-2011», где я сейчас, затронули интересный вопрос.

ЭЦП (электронная цифровая подпись) сейчас, фактически, используется не для того, для чего была придумана. Её назначение — кратковременное подтверждение подлинности переданного документа. К примеру, я получил письмо и мне нужно убедиться, что оно действительно пришло именно от того адресата, которые указан в поле отправителя. Стандартный протокол обмена письмами мне этой уверенности не даёт. А ЭЦП — пожалуйста.

Я открываю письмо, по подлинности ЭЦП убеждаюсь, что всё в порядке, дальше ЭЦП мне не нужна.

В электронных документооборотах не так. Там ЭЦП используется для подтверждения подлинности документа в долгосрочном периоде — годы, десятки лет, в перспективе, возможно, сотни (если речь идёт о долгосрочных договорах между государствами, например).

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

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

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

В долгосрочной перспективе использования ЭЦП это проблема — при хранении документов десятки и сотни лет, риск подмены документов, особенно важных, где игра стоит свеч возрастает многократно.

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

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

Можно увеличивать длину ключа, (фантазирую) применять ключи, занимающие десятки килобайт. Шифрование будет крайне медленным, но для важных документов можно и потерпеть. Но вот вопрос — если через двадцать лет появятся квантовые компьютеры, которым такая длина ключей «на один зуб», что будем делать? Как видно, простого выхода из ситуации не существует.

43 комментария

Sublime Text 2 (совет)

Поставил по совету коллег с прежней работы себе текстовый редактор «Sublime Text 2», всё хорошо, одна проблема — файлы с кодировкой CP1251 он открывает неправильно, приходится переоткрывать через «File → Reopen with Encoding → Cyrillic (Windows 1251)».

Покопался по настройкам, нашёл выход. Нужно зайти в настройки (на «Маке», как обычно — ⌘+,) и добавить между фигурными скобками вот такую настройку: «»fallback_encoding«: „Cyrillic (Windows 1251)“». Мой файл настроек сейчас почти пустой и выглядит вот так:

// Settings in here override those in "Default/Base File.sublime-settings", and
// are overridden in turn by file type specific settings. Place your settings
// here, to ensure they're preserved when upgrading.
{
    "fallback_encoding": "Cyrillic (Windows 1251)"
}

26 комментариев

CSS hack for FireFox 6

Меня тут попросили сделать CSS-хак для шестого Файрфокса. Пожалуйста:

x::-moz-progress-bar, body {
    background: orange; /* FF 6.0+ */
}

@-moz-document regexp(".*") {
    body {
        background: orange; /* FF 6.0+ */
    }
}

Проверено на Firefox 6.0 и 7.0.1.

4 комментария

Поддержка MJPEG в «Опере»

Как я уже писал, «Опера» иначе показывает MJPEG, не как остальные браузеры. Если открыть какую-нибудь веб-камеру, использующую этот формат, в десятой версии, то «Опера» сначала задумается, потом покажет какой-то кадр, 11 и 12 версии показывают ещё хуже — подобие потоковой передачи уже есть, но кадры лепятся один на другой и ничего разглядеть невозможно.

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

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

Идея такая — находим картинку во всех тегах IMG, у которой расширение «mjpg». Через XHR получаем с сервера видеопоток, копим кадр, подменяем найденную картинку и так далее.

Работает начиная с 12 версии браузера, которая ещё не вышла (кстати, в позавчерашней сборке сломали userjs напрочь) — в предыдущих версиях XHR слабоват по возможностям для этого.

Больше всего я бился с декодированием бинарных данных. Последняя версия «Оперы» поддерживает тип ответа arraybuffer (а вот blob не поддерживается), но если его выбрать, то данные в цикле почему-то перестают накапливаться, вероятно какой-то баг браузера. Я долго мучался, прежде чем придумал следующий хак — если сделать overrideMimeType(’image/jpeg’), то данные приходят в двухбайтовой кодировке UCS-2, которую несложно декодировать.

Кстати, быстрее всего у меня получилось декодировать не циклом, а регулярными выражениями, дальше тормозит уже не мой код, а способ получения отдельного кадра — каждый раз я соединяюсь с сервером заново. Можно бы накапливать кадры пачкой и потом их сбрасывать, но у меня нет задачи смотреть трансляции футбола, у меня дом, который не бегает и полученной скорости мне вполне хватает (где-то 2-3 кадра в секунду).

В общем, получился довольно небольшой код — 94 строки.

Посмотреть код.

// ==UserScript==
// @name        MJPEG+Opera - Add to Opera MJPEG support
// @author      Evgeny Stepanischev aka Bolk
// @version     1.00
// @namespace   /files/opera-mjpeg.js
// @modified    2011-10-09
// @include     *
// ==/UserScript==

opera.addEventListener('BeforeEvent.DOMContentLoaded', function() {
    if (opera.version() < 12) return;

    var BolkMJpeg = function (url, img) {
        var req = new XMLHttpRequest(); 
        img.src = 'about:blank'; // Сброс картинки, чтобы прекратить передачу

        // Асинхронно забираем кадр при помощи XHR, нам придёт видеопоток
        var start = function (url) {
            req.open("GET", url, true);
             // Свежепридуманный хитрый хак, позволяет получить данные в UCS-2
            req.overrideMimeType('image/jpeg');
            req.send(null);
        }

        // Хитрыми путями восстанавливаем порядок байт в reponse, там сейчас UCS-2,
        // получаем просто последовательность байт
        var reqbytes = function (n, l) {
            return escape(req.response.substr(n, l)).
                replace(/([^%]|%[^u].|%u....)/g, '<$1>').
                replace(/<%u(..)(..)>/g, '%$2%$1').
                replace(/<%(..)>/g, '%$1%00').
                replace(/<(.)>/g, '$1%00');
        }

        // заменяем картинку на бинарные данные
        var draw = function (bin) {
            img.src = 'data:image/jpeg,' + bin;
        }

        // На изменение состояние входных данных собираем по частям JPEG
        req.onreadystatechange = function () {
            // состояние «3» — данные пошли, но ещё не кончились
            if (req.readyState === 3) {
                var hnd = setInterval(function () {
                    // по заголовку получаем размер данных
                    var header = unescape(reqbytes(0, 300)).toLowerCase();

                    var idx = header.indexOf('content-length:');

                    if (idx > -1) {
                        // 15 — длина заголовка
                        var len = parseInt(header.substr(idx + 15), 10);
                        var headend = header.indexOf("\r\n\r\n");

                        // дожидаемся конца заголовка
                        if (headend > -1) {
                            var resp = reqbytes(0, len);
                            // умножаем всё на три, так как reqbytes даёт esc-последовательность
                            // вида %XX%XX…
                            len *= 3;
                            headend = (headend + 4) * 3;

                            // если собрали весь JPEG, обрываем соединение
                            // и начинаем новый цикл. Мы могли бы собирать кадры и дальше, но
                            // это съест всю память со временем
                            if (resp.length >= len + headend) {
                                req.abort();
                                resp = resp.substr(headend, len);

                                draw(resp);
                                clearInterval(hnd);

                                start(url);
                            }
                        }
                    }
                }, 100);
            }
        }

        start(url);
    };

    // ищем картинку, у которой расширение mjpg
    var imgs = document.getElementsByTagName('IMG');
    for (var i = 0, len = imgs.length; i<len; i++) {
        if (/\.mjpg$/.test(imgs[i].src)) {
            BolkMJpeg(imgs[i].src, imgs[i]);
            break;
        }
    }
}, true);

Если вы не в курсе как устанавливаются скрипты userjs, то это очень просто. В интернете есть много инструкций на эту тему. Только напоминаю, что «Опера» нужна двенадцатая, причём в сборке за седьмое октября userjs сломаны.

7 комментариев

MJPEG, MPNG, MGIF, MSVG

Я вчера показывал вам «телевизор» про фестиваль в Самаре. Сделано очень просто — уменьшил все сделанные там снимки до 66×44 и сконвертировал их в анимированный ГИФ. Мелковато, конечно, но что делать — ГИФ не очень-то предназначен для фотографических изображений и размер анимации сильно растёт с увеличением размера кадра. Для фотографий в интернете принято использовать формат JPEG.

У JPEG есть свой формат анимации, если так можно выразиться, а точнее — скорее простенький формат видео, так как формат анимации, в моём представлении, помимо кадров должен содержать себе какую-то метаинформацию о кадре, например, задержку перед появлением следующего кадра. Этот формат — разновидность MJPEG (motion JPEG).

Мне давно хотелось посмотреть что у этого формата внутри, сегодня наконец посмотрел. Всё оказалось крайне просто.

В данном случае, MJPEG — формат стримминга (потоковой подачи) видео. Собственно, это даже не формат, а простое соединение JPEG и специального заголовка «multipart/x-mixed-replace», которым может применяться для стриминга чего угодно. Можно, например, подавать не JPEG, а PNG и «изобрести» формат видео MPNG, а то и M(JPEG+PNG+GIF), картинки разных форматов можно перемешивать в любых пропорциях.

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

Content-type:  multipart/x-mixed-replace;boundary=СТРОКАРАЗДЕЛИТЕЛЬ

--СТРОКАРАЗДЕЛИТЕЛЬ
Content-type: image/jpeg
Content-length: 232311

…бинарное содержимое первой картинки…

--СТРОКАРАЗДЕЛИТЕЛЬ
Content-type: image/jpeg
Content-length: 47474

…бинарное содержимое второй картинки…

СТРОКУРАЗДЕЛИТЕЛЬ надо выбирать так, чтобы она не встретилась в бинарных данных изображения.

Браузерам этот заголовок достался от фирмы Нетскейп, которая когда-то (ещё в версии 1.1) добавила её поддержку в свой браузер. «Микрософт», будучи конкурентом «Нетскейпа», поддержку заголовка в браузер не внедрила. Остальные браузеры заголовок понимают, но каждый немного по-разному.

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

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

Самое приятное в этом формате — что все современные браузеры считают его картинкой, то есть тег IMG его преспокойно показывает.

Любопытно, что браузеры позволяют сжимать содержимое всего файла и даже каждого кадра отдельно, то есть можно в заголовке кадра поставить «Content-encoding: gzip» и сжать кадр гзипом. Это полезно, если захочется сделать MSVG, например. Ведь SVG-изображения хорошо сжимаются.

Так что, если вы задумались сделать аналог какого-нибудь хостинга анимированных ГИФ и вам наплевать на «Эксплорер» (я посмотрел, девятая версия не поддерживает), сделайте ещё и поддержку MJPEG/MPNG.

11 комментариев

Гоу версия 1

Язык программирования «Гугл Гоу» наконец-то стабилизируется:

We propose to issue a Go release early next year that will be called “Go version 1”, Go 1 for short, that will be the first Go release to be stable in this way. Code that compiles in Go version 1 should, with few exceptions, continue to compile throughout the lifetime of that version, as we issue updates and bug fixes such as Go version 1.1, 1.2, and so on… Go version 1 will be a stable language with stable libraries. Other than critical fixes, changes made to the library and packages for versions 1.1, 1.2 and so on may add functionality but will not break existing Go version 1 programs.

Наконец-то. Это означает, что устранят один из существенных недостатков «Гоу» — постоянные изменения в языке и стандартных библиотеках.

4 комментария

Фотки можно добавлять ещё и с picplz.com

Внёс важное изменение в движок сайта — фотографии теперь можно добавлять ещё и с сайта «Пикплз». Вообще я переделал весь механизм добавления сайтов с картинками.

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

Во-вторых, я теперь использую мультизапросы модуля «кУРЛ» — запросы на каждую картинку идут не последовательно, как раньше, а параллельно.

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

Какие ещё сервисы картинок добавить? Два жёстких требования — они должны быть с регистрацией (чтобы мне не постили всякую порнуху) и имели АПИ для получения информации о фото (чтобы я мог выбрать подходящие размеры картинки).

Добавлено позже: теперь ещё и «Фликр» работает.

32 комментария

← 1 37 38 39 40 41 67 →